【自作ゲーム開発1】オブジェクトプーリングと敵モブの作成【Unity】


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

はじめに

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

今回は小型キャラクターの基本動作である移動を実装していきます。

最終的には数が多くなるためオブジェクトプーリングのシステムを導入しつつ移動システムの動作確認をしました。

先にソースコードを公開し説明を加えていきます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CenterMobManagerEnemy : MonoBehaviour
{
    public GameObject p_mob_prefab;

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

    private float m_time_count = 0.0f;

    private short m_mob_count = 0;

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

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

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

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

    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 指定数生成及びプーリング呼び出し

    [タスク]
    タスク1 プレイヤーキャラのステータスを反映させる
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class CenterMobEnemy : MonoBehaviour{
    
    private GameObject[] p_pop_point = new GameObject[5];
    private GameObject[] p_end_point = new GameObject[5];

    private NavMeshAgent m_navmesh_agent;

    private void OnTriggerEnter(Collider a_collider){
        if(a_collider.gameObject.tag == "Finish"){ // タスク2
            MobState(false);
        }
    }

    public void Initialize(){
        m_navmesh_agent = GetComponent<NavMeshAgent>();
        p_pop_point[0] = GameObject.Find("PopPoint"); // タスク3
        p_end_point[0] = GameObject.Find("Goal"); // タスク3
    }

    public void MobState(bool a_bool){
        if(a_bool){
            this.gameObject.transform.position = p_pop_point[0].transform.position; // タスク1
            m_navmesh_agent.enabled = true; // 補足1
            m_navmesh_agent.SetDestination(p_end_point[0].transform.position); // タスク1
        }else{
            m_navmesh_agent.enabled = false; // 補足1
            this.gameObject.SetActive(false);
        }
    }
}

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

    [説明]
    補足1 真だと座標移動時に障害物が干渉して初期位置にズレが発生する

    [バージョン]
    2020-12-12 プーリングとナビゲーション

    [タスク]
    タスク1 拠点の占拠状態で分岐が必要
    タスク2 プーリング動作確認用のため消去する
    タスク3 沸く場所全ての設定が必要
*/

オブジェクトプーリング

生成と初期化

指定数のオブジェクトを作成し初期化(開始位置と到着位置を取得等)

復帰と移動

非アクティブなオブジェクトを起こし座標セットし移動を開始させる

到着と休息

目的地点へ到着した時点で非アクティブに変更する

生成と初期化

public GameObject p_mob_prefab;

予めパブリック変数に使用するプレハブ(モブ)を登録しています。

m_mob_count = 3; // タスク1

for(int l_loop = 0; l_loop < m_mob_count; l_loop += 1){
    var l_instance = Instantiate(p_mob_prefab);
    l_instance.SetActive(false);
    m_mob_prefab_list.Add(l_instance);
    m_mob_prefab_script.Add(l_instance.GetComponent<CenterMobEnemy>());
    m_mob_prefab_script[l_loop].Initialize();
}
public void Initialize(){
    m_navmesh_agent = GetComponent<NavMeshAgent>();
    p_pop_point[0] = GameObject.Find("PopPoint"); // タスク3
    p_end_point[0] = GameObject.Find("Goal"); // タスク3
}

指定された数を生成してリストに格納すると同時にスクリプトを取得し初期化関数を呼び出して様々なデータをプレハブにセットしています。
生成時にプレハブのスタート関数がなぜか呼ばれないため苦戦しています、ここの原因が掴めておらず初期化関数で処理しています。
セットアクティブは偽としており全て休息した状態で生成されます。

復帰と移動

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

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

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;
        }
    }
}

アップデート内で3秒ごとに処理する状態にして関数(ActiveObject)を呼び出しています。

関数内は休息しているモブを探して休息しているのが居れば起こすと同時にモブの関数(MobState)を呼び出しています。

public void MobState(bool a_bool){
    if(a_bool){
        this.gameObject.transform.position = p_pop_point[0].transform.position; // タスク1
        m_navmesh_agent.enabled = true; // 補足1
        m_navmesh_agent.SetDestination(p_end_point[0].transform.position); // タスク1
    }else{
        m_navmesh_agent.enabled = false; // 補足1
        this.gameObject.SetActive(false);
    }
}

休息しているモブを起こす際には引数を真で関数を実行し初期位置のセットと到着地点の設定をおこなっています。

到着と休息

private void OnTriggerEnter(Collider a_collider){
    if(a_collider.gameObject.tag == "Finish"){ // タスク2
        MobState(false);
    }
}

到着は至ってシンプルです。
到着地点のタグ(今回はFinish)が付いたコライダーに接触した際に関数(MobState)を偽で呼び出すだけです。


ナビゲーション

プレハブとして登録しているモブにはNavMeshAgentのコンポーネントを設定します(設定数値は現状初期のままで問題ありません)。

this.gameObject.transform.position = p_pop_point[0].transform.position; // タスク1
m_navmesh_agent.enabled = true; // 補足1
m_navmesh_agent.SetDestination(p_end_point[0].transform.position); // タスク1

ナビゲーションシステムはモブの復帰時に初期位置をセットしてからユニティで用意されている関数(SetDestination)を実行して引数に目的地をセットするだけです。

m_navmesh_agent.enabled = false; // 補足1
this.gameObject.SetActive(false);

到着後はモブを休息状態にするのと同時にモブに付いているコンポーネント(NavMeshAgent)を停止しており、復帰時に起動するようにします。

注意
NavMeshAgentを停止しておかないと初期位置のセットをする際にズレが発生します。

ナビゲーションエリアを設定する場合には地面として使用しているオブジェクトを静的(Static)に設定するようにしてください。

ナビゲーションエリアの設定は上部メニューから追加できます。
タブに追加されるのでBake欄のBakeでナビゲーションエリアの設定が完了します。


動画


記事のリンク

コメントを残す

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