【自作ゲーム開発13】指揮官キャラクターの追加と敵の出現場所移動【Unity】


注意
本記事で掲載されている動作の実装方法及びプログラムのソースコードは最適な方法ではない可能性があります。
今後不具合等が判明した場合には修正及び改良をおこなう可能性があります。
また今後自分で同機能を実装する場合の参考にする可能性もあるためソースコードだけではなく説明しつつ記事を進めていきます。

はじめに

Unityによる自作ゲーム開発進捗その13になります!

今回は指揮官キャラクターの追加敵の出現位置を移動させるようにしました。

新しいスクリプトが3つ既存のスクリプトを3つ更新します。

新しいスクリプトの内2つは既存のスクリプトの変更ついでに名称も変更した物が含まれています。

以下にソースコードを掲載します。(今回は量が多いです🐷)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.UI;
using TMPro;
using Febucci.UI;

public class AssultEnemy : MonoBehaviour {
    private const int m_const_text_array = 10;

    [SerializeField]
    private Canvas m_hitpoint_canvas;

    [SerializeField]
    private Slider m_hitpoint_bar;

    [SerializeField]
    private TextAnimatorPlayer m_damage_text;

    [SerializeField]
    private ParticleSystem m_bomb_effect;

    [SerializeField]
    private ParticleSystem m_stun_effect;

    private List<TextAnimatorPlayer> m_text_animator = new List<TextAnimatorPlayer>();

    private GameObject m_script_manager;
    private GameObject m_pop_point;
    private GameObject[] m_end_point = new GameObject[5];

    private ColliderManager m_collider_manager;

    private NavMeshAgent m_navmesh_agent;

    private Rigidbody m_rigidbody;

    private Animator m_animator;

    private BaseSystem[] m_base_system = new BaseSystem[5];

    private Vector3 m_impact_vector;
    private Vector3 m_impact_power;

    private float m_time_count = 0.0f;
    private float m_hitpoint = 0.0f;
    private float m_damagepoint = 0.0f;
    private float m_stun_set = 0.0f;
    private float m_stun_use = 0.0f;

    private int m_transition = 0;
    private int m_iskinematic_change_frame = 0;
    private int m_text_array = 0;
    private int m_base_state = 2;

    enum STATE {
        WAIT,
        MOVE,
        DIE,
        BOMB,
        STUN,
        IMPACT
    }; STATE e_state;

    public void Initialize(string a_line) {
        for (int l_loop = 0; l_loop < m_const_text_array; l_loop += 1) {
            var l_create = m_hitpoint_canvas.transform;
            var l_object = Instantiate(m_damage_text, Vector3.zero, Quaternion.identity, l_create);
            m_text_animator.Add(l_object.GetComponent<TextAnimatorPlayer>());
        }

        m_hitpoint_canvas.worldCamera = Camera.main;

        m_script_manager = GameObject.Find("ScriptManager");
        m_collider_manager = m_script_manager.GetComponent<ColliderManager>();

        m_navmesh_agent = GetComponent<NavMeshAgent>();
        m_rigidbody = GetComponent<Rigidbody>();
        m_animator = GetComponent<Animator>();
        m_pop_point = GameObject.Find(string.Format("{0}Pop", a_line));

        for (int l_loop = 0; l_loop < 3; l_loop += 1) {
            // タスク5
            m_end_point[l_loop] = GameObject.Find(string.Format("{0}Base{1}", a_line, l_loop));
            m_base_system[l_loop] = m_end_point[l_loop].GetComponent<BaseSystem>();
        }

        m_hitpoint_bar.maxValue = 1000;
    }

    public void MobState(bool a_bool) {
        if (a_bool) {
            m_hitpoint_bar.value = m_hitpoint = m_hitpoint_bar.maxValue;
            this.gameObject.transform.position = m_pop_point.transform.position; // タスク3
            m_navmesh_agent.enabled = true; // 補足1
            m_time_count = 0.0f;
            e_state = STATE.WAIT;
        } else {
            m_navmesh_agent.enabled = false; // 補足1
            this.gameObject.SetActive(false);
        }
    }

    public void BaseChange(int a_number) {
        m_base_state = a_number;
    }

    private void FixedUpdate() {
        m_hitpoint_canvas.transform.rotation = m_hitpoint_canvas.worldCamera.transform.rotation;

        switch (e_state) {
            case STATE.WAIT:
                m_time_count += Time.deltaTime;

                if (m_time_count > 1.0f) {
                    m_animator.SetBool("animation_move", true);
                    m_navmesh_agent.SetDestination(m_end_point[m_base_state].transform.position);
                    e_state = STATE.MOVE;
                }
                break;

            case STATE.MOVE:
                m_time_count += Time.deltaTime;

                if (m_time_count > 1.0f) m_navmesh_agent.SetDestination(m_end_point[m_base_state].transform.position);
                break;

            case STATE.DIE:
                m_time_count += Time.deltaTime;

                if (m_time_count > 2.0f) {
                    m_animator.SetBool("animation_die", false);
                    MobState(false);
                }
                break;

            case STATE.BOMB:
                m_time_count += Time.deltaTime;

                if (m_time_count > 0.3f) MobState(false);
                break;

            case STATE.STUN:
                m_time_count += Time.deltaTime;

                if (m_time_count > m_stun_use) {
                    m_time_count = 0.0f;
                    m_stun_effect.Stop();
                    m_navmesh_agent.enabled = true;
                    m_rigidbody.isKinematic = true;
                    m_navmesh_agent.SetDestination(m_end_point[m_base_state].transform.position);
                    e_state = STATE.MOVE;
                }
                break;

            case STATE.IMPACT:
                if (m_transition == 0) {
                    m_transition = 1;
                    m_iskinematic_change_frame = Time.frameCount; // 補足2
                    m_navmesh_agent.enabled = false;
                    m_rigidbody.isKinematic = false;
                    m_rigidbody.AddForce(m_impact_power, ForceMode.Impulse);
                }

                if ((m_transition == 1) && (m_rigidbody.IsSleeping())) {
                    m_transition = 0;
                    m_iskinematic_change_frame = Time.frameCount; // 補足2
                    m_navmesh_agent.enabled = true;
                    m_rigidbody.isKinematic = true;
                    m_navmesh_agent.SetDestination(m_end_point[m_base_state].transform.position);
                    e_state = STATE.MOVE;
                }
                break;
        }
    }

    private void OnTriggerEnter(Collider a_collider) {
        if ((e_state == STATE.BOMB) || (e_state == STATE.WAIT) || (e_state == STATE.DIE) || (m_iskinematic_change_frame == Time.frameCount)) return; // 補足2

        if (a_collider.CompareTag("Finish")) { // タスク4
            m_bomb_effect.Play();
            m_base_system[m_base_state].BaseDamage(m_hitpoint_bar.value);
            m_navmesh_agent.enabled = true;
            m_rigidbody.isKinematic = true;
            m_navmesh_agent.ResetPath();
            m_time_count = 0.0f;
            e_state = STATE.BOMB;
            return;
        }

        m_collider_manager.ColliderDataInput(a_collider, this.gameObject, ref m_impact_vector, ref m_damagepoint, ref m_stun_set);

        if (m_damagepoint != 0) {
            DamageTextShow((int)m_damagepoint);
            m_hitpoint -= (int)m_damagepoint;
            if (m_hitpoint < 0) m_hitpoint = 0;
            m_hitpoint_bar.value = m_hitpoint;
        }

        if (m_hitpoint == 0) { // タスク2
            m_navmesh_agent.enabled = true;
            m_rigidbody.isKinematic = true;
            m_navmesh_agent.ResetPath();
            m_animator.SetBool("animation_move", false);
            m_animator.SetBool("animation_die", true);
            m_time_count = 0.0f;
            e_state = STATE.DIE;
            return;
        }

        if (m_impact_vector != Vector3.zero) {
            m_impact_power = m_impact_vector; // 補足3
            m_transition = 0;
            m_stun_effect.Stop();
            e_state = STATE.IMPACT;
            return;
        }

        if ((m_stun_set != 0.0f) && (e_state != STATE.IMPACT)) {
            m_stun_use = m_stun_set;
            m_navmesh_agent.enabled = true;
            m_rigidbody.isKinematic = true;
            m_navmesh_agent.ResetPath();
            m_stun_effect.Play();
            m_time_count = 0.0f;
            e_state = STATE.STUN;
            return;
        }
    }

    private void DamageTextShow(float a_damage) {
        // タスク6
        m_text_animator[m_text_array].gameObject.transform.localPosition = new Vector3(Random.Range(-0.1f, 0.1f), Random.Range(0.9f, 1.1f), 0f);
        m_text_animator[m_text_array++].ShowText(string.Format("<FADE><BOUNCE>{0}</BOUNCE></FADE>", a_damage));

        if (m_const_text_array <= m_text_array) m_text_array = 0;
    }
}

/*
    [規則]
    p_ 外部アクセス
    m_ メンバー変数
    l_ ローカル変数
    a_ 引数

    [説明]
    補足1 真だと座標移動時に障害物が干渉して初期位置にズレが発生する
    補足2 IsKinematic切り替え時と同フレームのコライダー判定を無視する
    補足3 コライダー内で取得した吹き飛ばし変数をそのまま使用すると吹き飛ぶ前に0で上書きする事が有るため実行用変数を用意

    [バージョン]
    2020-12-12 プーリングとナビゲーション
    2021-01-03 アセットストアのモデル反映とアニメーション
    2021-01-28 吹き飛ばし機能の実装
    2021-02-03 体力システムの実装及びゲージとダメージの可視化
    2021-02-19 味方拠点へのダメージ及び自身の休息
    2021-03-03 スタン動作の追加
    2021-03-12 標的の拠点の変更機能
    2021-03-17 拠点数と沸き拠点移動対応と名前の変更(旧CenterMobEnemy)

    [タスク]
    (済)タスク1 拠点の占拠状態で分岐が必要
    (済)タスク2 プーリング動作確認用のため消去する
    (済)タスク3 沸く場所全ての設定が必要
    タスク4 今後タグを拠点に統一させる
    タスク5 臨時防衛拠点数
    タスク6 コルーチンを使用したオブジェクトプーリングに変更する
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PopSystem : MonoBehaviour{
    [SerializeField]
    private ParticleSystem[] m_move_effect = new ParticleSystem[2];

    public void MoveObject(Vector3 a_move) {
        StartCoroutine("BaseMove", a_move);
    }

    IEnumerator BaseMove(Vector3 a_move) {
        m_move_effect[0].Stop();
        m_move_effect[1].Play();
        yield return new WaitForSeconds(0.5f);
        this.transform.position = a_move;
        m_move_effect[1].Play();
        m_move_effect[0].Play();
    }
}

/*
    [規則]
    p_ 外部アクセス
    m_ メンバー変数
    l_ ローカル変数
    a_ 引数

    [説明]
    

    [バージョン]
    2021-03-17 敵の沸く位置を移動させる(エフェクト付き)

    [タスク]
  
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CharacterManager : MonoBehaviour {
    [SerializeField]
    private GameObject m_mob_prefab;

    [SerializeField]
    private MagePlayer m_mage_script;

    [SerializeField]
    private PopSystem[] m_pop_point = new PopSystem[3];

    private List<GameObject> m_mob_prefab_list = new List<GameObject>();
    private List<AssultEnemy> m_mob_prefab_script = new List<AssultEnemy>();

    private GameObject[] m_center_base = new GameObject[5];

    private float m_time_count = 0.0f;

    private int m_center_state = 2;

    private short m_mob_count = 0;

    private void Start() {
        m_mob_count = 10; // タスク1

        for (int l_loop = 0; l_loop < m_mob_count; l_loop += 1) {
            var l_instance = Instantiate(m_mob_prefab);
            l_instance.SetActive(false);
            m_mob_prefab_list.Add(l_instance);
            m_mob_prefab_script.Add(l_instance.GetComponent<AssultEnemy>());
            m_mob_prefab_script[l_loop].Initialize("Center");
        }

        for (int l_loop = 0; l_loop < 3; l_loop += 1) {
            // タスク2
            m_center_base[l_loop] = GameObject.Find(string.Format("CenterBase{0}", l_loop));
        }

        m_mage_script.Initialize("Center"); // タスク3
    }

    private void Update() {
        m_time_count += Time.deltaTime;

        if (m_time_count > 3.0f) {
            CenterBaseCheck();
            ActiveObject();
        }
    }

    private void CenterBaseCheck() {
        if (!m_center_base[m_center_state].activeSelf) {
            m_center_state -= 1;

            if (m_center_state < 0) m_center_state = 0;

            switch (m_center_state) {
                case 1:
                    m_pop_point[1].MoveObject(new Vector3(20.0f, 0.5f, 25.0f));
                break;

                case 0:
                    m_pop_point[1].MoveObject(new Vector3(30.0f, 0.5f, 25.0f));
                break;
            }

            for (int l_loop = 0; l_loop < m_mob_count; l_loop += 1) {
                m_mob_prefab_script[l_loop].BaseChange(m_center_state);
            }

            m_mage_script.BaseChange(m_center_state); // タスク3
        }
    }

    private void ActiveObject() {
        m_time_count = 0.0f;

        for (int l_loop = 0; l_loop < m_mob_count; l_loop += 1) {
            if (m_mob_prefab_list[l_loop].activeSelf == false) {
                m_mob_prefab_list[l_loop].SetActive(true);
                m_mob_prefab_script[l_loop].MobState(true);
                break;
            }
        }
    }
}

/*
    [規則]
    p_ 外部アクセス
    m_ メンバー変数
    l_ ローカル変数
    a_ 引数

    [説明]

    [バージョン]
    2020-12-12 指定数生成及びプーリング呼び出し
    2021-03-12 管理拠点を配列化し拠点管理を行いモブに伝える
    2021-03-17 センターライン指揮官キャラの生成と名前変更(旧CenterMobManagerEnemy)

    [タスク]
    タスク1 プレイヤーキャラのステータスを反映させる
    タスク2 臨時防衛拠点数
    タスク3 メニュー画面からセンターラインのキャラクターを受け取るようにする(当面は臨時でMageを使用)
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class MagePlayer : MonoBehaviour {
    [SerializeField]
    private GameObject m_defense_player;

    private List<GameObject> m_defense_prefab_list = new List<GameObject>();
    private List<DefensePlayer> m_defense_prefab_script = new List<DefensePlayer>();

    private Transform[] m_serch_object = new Transform[5];

    private NavMeshAgent m_navmesh_agent;
    private Animator m_animator;

    private int m_base_state = 2;

    enum STATE {
        SET,
        SERCH,
    }; STATE e_state;

    public void BaseChange(int a_number) {
        m_base_state = a_number;

        for (int l_loop = 0; l_loop < 3; l_loop += 1) {
            m_defense_prefab_script[l_loop].BaseChange(m_base_state);
        }

        m_navmesh_agent.SetDestination(m_serch_object[m_base_state].transform.position);
        m_animator.SetBool("animation_move", true);
        e_state = STATE.SET;
    }

    public void Initialize(string a_line) {
        m_navmesh_agent = GetComponent<NavMeshAgent>();
        m_animator = GetComponent<Animator>();

        for (int l_loop = 0; l_loop < 3; l_loop += 1) {
            var l_instance = Instantiate(m_defense_player, this.transform);
            m_defense_prefab_list.Add(l_instance);
            m_defense_prefab_script.Add(l_instance.GetComponent<DefensePlayer>());
            m_defense_prefab_script[l_loop].Initialize(a_line, l_loop);
        }

        for (int l_loop = 0; l_loop < 3; l_loop += 1) {
            // タスク4
            GameObject l_object = GameObject.Find(string.Format("{0}Base{1}", a_line, l_loop));
            m_serch_object[l_loop] = l_object.transform.Find("SetPositionL");
        }

        m_navmesh_agent.SetDestination(m_serch_object[m_base_state].transform.position);
        m_animator.SetBool("animation_move", true);
    }

    private void FixedUpdate() {
        switch (e_state) {
            case STATE.SET:
                if ((this.transform.position - m_serch_object[m_base_state].transform.position).sqrMagnitude < 1.0f) {
                    m_animator.SetBool("animation_move", false);
                    e_state = STATE.SERCH;
                }
            break;

            case STATE.SERCH:

            break;
        }
    }
}

/*
    [規則]
    p_ 外部アクセス
    m_ メンバー変数
    l_ ローカル変数
    a_ 引数

    [説明]

    [バージョン]
    2021-03-17 3体の味方呼び出しと防衛拠点変更動作

    [タスク]
    
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SerchObject : MonoBehaviour{
    private List<GameObject> m_object_list = new List<GameObject>();

    private SphereCollider m_serch_collider;

    private string m_tag;

    private void Start() {
        m_serch_collider = this.GetComponent<SphereCollider>();
        m_serch_collider.enabled = false;
    }

    public void SerchStart(Transform a_area, string a_tag) {
        m_object_list.Clear();
        m_tag = a_tag;
        m_serch_collider.transform.position = a_area.transform.position;
        m_serch_collider.enabled = true;
    }

    public GameObject SerchEnd(Transform a_area, int a_distance) { // 補足1
        m_serch_collider.enabled = false;

        if (m_object_list.Count <= 0) return null;

        int l_list_array = 0;

        if (a_distance == 0) {
            float l_distance_check = 999.0f;

            for (int l_loop = 0; l_loop < m_object_list.Count; l_loop += 1) {
                if ((a_area.transform.position - m_object_list[l_loop].transform.position).sqrMagnitude < l_distance_check) {
                    l_list_array = l_loop;
                    l_distance_check = (a_area.transform.position - m_object_list[l_loop].transform.position).sqrMagnitude;
                }
            }
        } else {
            float l_distance_check = 0.0f;

            for (int l_loop = 0; l_loop < m_object_list.Count; l_loop += 1) {
                if ((a_area.transform.position - m_object_list[l_loop].transform.position).sqrMagnitude > l_distance_check) {
                    l_list_array = l_loop;
                    l_distance_check = (a_area.transform.position - m_object_list[l_loop].transform.position).sqrMagnitude;
                }
            }
        }

        return m_object_list[l_list_array];
    }

    private void OnTriggerEnter(Collider a_collider) {
        if (a_collider.CompareTag(m_tag)) m_object_list.Add(a_collider.gameObject);
    }
}

/*
    [規則]
    p_ 外部アクセス
    m_ メンバー変数
    l_ ローカル変数
    a_ 引数

    [説明]
    補足1 引数1のオブジェクトから近い距離か遠い距離のオブジェクトを返す(引数2が0だと近いオブジェクト)

    [バージョン]
    2021-02-25 コライダー内オブジェクトの取得と指定オブジェクトからの距離で一番近い及び遠いオブジェクトを戻り値とする関数
    2021-03-17 関数の引数をGameObjectからTransformに変更

    [タスク]
    
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class DefensePlayer : MonoBehaviour{
    private const float m_serch_speed = 1.0f; // 補足1

    [SerializeField]
    private SerchObject m_serch_system;

    [SerializeField]
    private BoxCollider m_attack_area;

    private NavMeshAgent m_navmesh_agent;
    private Animator m_animator;
    private GameObject m_target_object;
    private Transform[] m_serch_object = new Transform[5];

    private float m_time_count = 0.0f;

    private int m_attack_switch = 0;
    private int m_base_state = 2;

    private bool m_serch_state = false;
    private bool m_attack_state = false;

    enum STATE {
        SET,
        SERCH,
        MOVE,
        ATTACK
    }; STATE e_state;

    public void BaseChange(int a_number) {
        m_base_state = a_number;

        m_navmesh_agent.SetDestination(m_serch_object[m_base_state].transform.position);
        m_animator.SetInteger("animation_attack", 0);
        m_animator.SetBool("animation_move", true);
        e_state = STATE.SET;
    }

    public void Initialize(string a_line, int a_number) {
        m_navmesh_agent = GetComponent<NavMeshAgent>();
        m_animator = GetComponent<Animator>();

        for (int l_loop = 0; l_loop < 3; l_loop += 1) {
            // タスク4
            GameObject l_object = GameObject.Find(string.Format("{0}Base{1}", a_line, l_loop));
            m_serch_object[l_loop] = l_object.transform.Find(string.Format("SetPosition{0}", a_number));
        }

        m_navmesh_agent.SetDestination(m_serch_object[m_base_state].transform.position);
        m_animator.SetBool("animation_move", true);
    }

    private void FixedUpdate() {
        switch (e_state) {
            case STATE.SET:
                if ((this.transform.position - m_serch_object[m_base_state].transform.position).sqrMagnitude < 1.0f) {
                    m_animator.SetBool("animation_move", false);
                    e_state = STATE.SERCH;
                }
            break;

            case STATE.SERCH:
                m_time_count += Time.deltaTime;

                if ((m_time_count > m_serch_speed) && (!m_serch_state)) {
                    m_navmesh_agent.ResetPath();
                    m_navmesh_agent.velocity = Vector3.zero;
                    m_animator.SetBool("animation_move", false);
                    m_serch_state = true;
                    m_serch_system.SerchStart(m_serch_object[m_base_state], "Enemy");
                }

                if ((m_time_count > m_serch_speed * 2.0f) && (m_serch_state)) {
                    m_serch_state = false;
                    m_time_count = 0.0f;
                    m_target_object = m_serch_system.SerchEnd(m_serch_object[m_base_state], 0); // タスク3

                    if (m_target_object != null) {
                        m_navmesh_agent.SetDestination(m_target_object.transform.position);
                        e_state = STATE.MOVE;
                    }
                }
            break;

            case STATE.MOVE:
                if (m_target_object.activeSelf == false) {
                    m_time_count = m_serch_speed * 2.0f;
                    m_navmesh_agent.ResetPath();
                    m_navmesh_agent.velocity = Vector3.zero;
                    m_animator.SetBool("animation_move", false);
                    m_serch_state = true;
                    m_serch_system.SerchStart(m_serch_object[m_base_state], "Enemy");
                    e_state = STATE.SERCH;
                } else if ((this.transform.position - m_target_object.transform.position).sqrMagnitude < 1.0f) {
                    if (((this.transform.position - m_serch_object[m_base_state].transform.position).sqrMagnitude) < ((m_target_object.transform.position - m_serch_object[m_base_state].transform.position).sqrMagnitude)) {
                        m_attack_switch = 2;
                        m_attack_area.tag = "Player"; // タスク5
                    } else {
                        m_attack_switch = 1;
                        m_attack_area.tag = "PlayerSkill2"; // タスク5
                    }

                    this.transform.LookAt(m_target_object.transform);
                    m_navmesh_agent.ResetPath();
                    m_navmesh_agent.velocity = Vector3.zero;
                    m_animator.SetInteger("animation_attack", m_attack_switch);
                    m_time_count = 0.0f;
                    e_state = STATE.ATTACK;
                } else {
                    m_navmesh_agent.SetDestination(m_target_object.transform.position);
                    m_animator.SetBool("animation_move", true);
                }
            break;

            case STATE.ATTACK:
                m_time_count += Time.deltaTime;

                if ((!m_attack_state) && m_time_count > 0.3f) {
                    m_time_count = 0.0f;
                    m_attack_state = true;
                    m_attack_area.enabled = true;
                }

                if ((m_attack_state) && m_time_count > 0.3f) {
                    m_attack_state = false;
                    m_attack_area.enabled = false;
                    m_animator.SetBool("animation_move", false);
                    m_animator.SetInteger("animation_attack", 0);

                    if (m_attack_switch == 1) {
                        m_navmesh_agent.SetDestination(m_serch_object[m_base_state].transform.position);
                        m_animator.SetBool("animation_move", true);
                    }

                    m_time_count = 0.0f;
                    e_state = STATE.SERCH;
                }
            break;
        }
    }
}

/*
    [規則]
    p_ 外部アクセス
    m_ メンバー変数
    l_ ローカル変数
    a_ 引数

    [説明]
    補足1 数値を下げると行動の切り替えが早くなる(強くなる)

    [バージョン]
    2021-02-25 感知エリアから一番近いオブジェクトを追いかける
    2021-03-03 2種類の攻撃を追加しスタン時は拠点に下がる
    2021-03-12 防衛拠点を配列化(防衛拠点の移行はしない)
    2021-03-17 防衛拠点変更機能を追加

    [タスク]
    (済)タスク1 初期化は指揮官キャラが呼び出す
    (済)タスク2 臨時の動作(対象に向かって歩くだけ)
    タスク3 攻撃キャラは自身から近い対象にする分岐を入れる
    タスク4 臨時防衛拠点数
    タスク5 臨時タグ
*/

指揮官キャラクターの追加

今回から指揮官キャラクターの存在を追加していきます。

m_mage_script.Initialize("Center"); // タスク3

CharacterManager(旧CenterMobEnemy)の初期化時に指揮官キャラクターの初期化をおこない、どのレーンで戦わせるかをセットしています。


for (int l_loop = 0; l_loop < 3; l_loop += 1) {
    var l_instance = Instantiate(m_defense_player, this.transform);
    m_defense_prefab_list.Add(l_instance);
    m_defense_prefab_script.Add(l_instance.GetComponent<DefensePlayer>());
    m_defense_prefab_script[l_loop].Initialize(a_line, l_loop);
}

for (int l_loop = 0; l_loop < 3; l_loop += 1) {
    // タスク4
    GameObject l_object = GameObject.Find(string.Format("{0}Base{1}", a_line, l_loop));
    m_serch_object[l_loop] = l_object.transform.Find("SetPositionL");
}

指揮官キャラの初期化内では自作ゲーム開発10で作成した味方コンピューターを3体生成し配列に格納し自身も含めて防衛すべきラインの拠点も配列に入れています、防衛すべき拠点は引数で指定されています。(今回はCenter)


public void BaseChange(int a_number) {
    m_base_state = a_number;

    for (int l_loop = 0; l_loop < 3; l_loop += 1) {
        m_defense_prefab_script[l_loop].BaseChange(m_base_state);
    }

    m_navmesh_agent.SetDestination(m_serch_object[m_base_state].transform.position);
    m_animator.SetBool("animation_move", true);
    e_state = STATE.SET;
}

CaharacterManagerから拠点移動の関数(BaseChange)が呼ばれた際に自身と部下の防衛拠点の変更をしています。


敵の出現場所移動

味方の防衛拠点が破壊された際に敵の出現位置も移動させるようにしました。

public void MoveObject(Vector3 a_move) {
    StartCoroutine("BaseMove", a_move);
}

味方の防衛拠点が破壊された際にCharacterManagerから関数が呼ばれ引数に入っている位置へ移動するコルーチンを起動しています。


IEnumerator BaseMove(Vector3 a_move) {
    m_move_effect[0].Stop();
    m_move_effect[1].Play();
    yield return new WaitForSeconds(0.5f);
    this.transform.position = a_move;
    m_move_effect[1].Play();
    m_move_effect[0].Play();
}

コルーチンでは消失のエフェクトと移動後のエフェクトを0.5秒の間隔を開けて発生させて移動もしています。


今回使用するアセット


動画


記事のリンク

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です