【Unity3D】自動生成する不思議なダンジョンの作り方④「時間経過で敵の自動生成」

2022年8月26日

どうも、だらはです。
前回に引き続き、自動生成する不思議なダンジョンの作り方を紹介していきます。
今回は、時間経過で敵の自動生成です!

スポンサーリンク

概要

記事が長くなると思うので先ずは完成図目次を示します。
目次の順番で自動生成する不思議なダンジョンを作成していきます。

◆完成図

◆目次

  1. 2Dマップを生成する。
  2. 2Dマップをもとに3Dマップを作成する。
  3. キャラクタやアイテムを初期配置する。
  4. 時間経過で敵を生成する。←今ココ!
  5. 現在位置マップを作成する。

④時間経過で敵を生成する。

さて、今回も[ElementGenerator.cs]に追記する形で、敵の自動生成を実装します。
変更点はシンプルで、AppearEnemy()で実現します。

◆完成イメージ

◆変更後の[ElementGenerator.cs]

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

public class ElementGenerator : MonoBehaviour
{
    //リソース格納用 (階層またぎのセーブ/ロードで使用)
    Material material;                                              //壁のマテリアル
    GameObject objGoal;                                             //ゴール
    GameObject[] objEnemyList = new GameObject[1];                  //敵リスト
    public GameObject[] objItemList = new GameObject[1];            //アイテムリスト
    GameObject[] objMapTipList = new GameObject[1];                 //マップチップリスト

    //調整用パラメタ
    [SerializeField] int LimitEnemyInitMin;                         //初期生成の敵の下限数
    [SerializeField] int LimitEnemyInitMax;                         //初期生成の敵の上限数
    [SerializeField] int LimitItemInitMin;                          //初期生成のアイテムの下限数
    [SerializeField] int LimitItemInitMax;                          //初期生成のアイテムの上限数
    
    //2Dマップ生成スクリプト用
    DungeonGenerator dungeonGenerator;
    int[,] map;

    //パス読み込み用
    GameObject objMap2D;                                            //Map2D
    GameObject objPlayer;                                           //プレイヤー

    //生成したマップチップ
    GameObject[,] objMapExist;                                      //フィールド用

    //敵の自動生成用
    [SerializeField] float appearNextTime = 10;                     //次に敵が出現するまでの時間
    [SerializeField] int maxNumOfEnemys = 99;                       //出現する敵の数
    int numEnemys = 0;                                              //今何人の敵を出現させたか(総数)
    float countTime = 0;                                            //時間計測用

    void Awake()
    {
        //リソース読み込み
        ReadResources();

        //壁を生成
        GenerateWall();

        //二次元マップ生成
        GenerateMap2D(map);
    }
    
    void Start()
    {
        //プレイヤーを部屋に配置(予め生成しておく)
        objPlayer = GameObject.Find("Player");
        GenerateObj(map, objPlayer);

        //ゴールを部屋に配置
        GenerateObj(map, objGoal);

        //敵を部屋に配置(初期出現数はランダム)
        int enemyNum = UnityEngine.Random.Range(LimitEnemyInitMin, LimitEnemyInitMax + 1);
        for (int i = 0; i < enemyNum; i++)
        {
            int enemyIdx = UnityEngine.Random.Range(0, objEnemyList.Length);
            GenerateObj(map, objEnemyList[enemyIdx]);
        }

        //アイテムを部屋に配置(初期出現数はランダム)
        int itemNum = UnityEngine.Random.Range(LimitItemInitMin, LimitItemInitMax + 1);
        for (int i = 0; i < itemNum; i++)
        {
            int itemIdx = UnityEngine.Random.Range(0, objItemList.Length);
            GenerateObj(map, objItemList[itemIdx]);
        }
    }

    void Update()
    {
        //時間経過で敵を生成
        AppearEnemy();
    }

    //リソース読み込み
    void ReadResources()
    {
        //壁のマテリアル
        material = (Material)Resources.Load("Material/StoneSurface_edit");

        //ゴール
        objGoal = (GameObject)Resources.Load("Prefab/Stage/Goal_Light");

        //敵リスト
        objEnemyList[0] = (GameObject)Resources.Load("Prefab/Enemy/mutant_edit");

        //アイテムリスト
        objItemList[0] = (GameObject)Resources.Load("Prefab/Item/Magic/Wand1A_edit");

        //2Dのマップチップ読み込み
        objMapTipList[0] = (GameObject)Resources.Load("Prefab/Map2D/MapTip");
    }

    //壁を生成
    void GenerateWall()
    {
        //ダンジョンマップ
        dungeonGenerator = GetComponent<DungeonGenerator>();

        //壁の高さ
        float blockHeight = 4f;

        //2Dマップ生成
        map = dungeonGenerator.Generate();

        //生成する壁の親となるGameObject
        GameObject objWall = GameObject.Find("Wall");

        //自動生成したマップにCubeを配置
        for (int i = 0; i < map.GetLength(0); i++)
        {
            for (int j = 0; j < map.GetLength(1); j++)
            {
                //壁をCubeで作成
                if (map[i, j] == 0)
                {
                    for (int k = 0; k < blockHeight; k++)
                    {
                        GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);

                        //Wall直下に階層を移動
                        cube.transform.parent = objWall.transform;
                        cube.GetComponent<Renderer>().material = material;
                        cube.transform.localScale = new Vector3(1, 1, 1);
                        cube.transform.position = new Vector3(i, k + 0.5f, j);
                    }
                }
            }
        }
        GetComponent<NavMeshSurface>().BuildNavMesh();
    }

    //二次元マップ生成
    void GenerateMap2D(int[,] map)
    {
        objMap2D = GameObject.Find("Map2D").gameObject;
        objMapExist = new GameObject[map.GetLength(0), map.GetLength(1)];

        //0:壁、1:部屋、2:通路
        for (int i = 0; i < 50; i++)
        {
            for (int j = 0; j < 50; j++)
            {
                objMapExist[i, j] = Instantiate(objMapTipList[0]);

                //Map2D直下に階層を移動
                objMapExist[i, j].transform.parent = objMap2D.transform;
                objMapExist[i, j].transform.localScale = new Vector3(1, 1, 1);

                //マップの位置調整 (MapTipの幅が5fのため)
                Vector2 vector2 = new Vector2(2.5f + 5f * i, 2.5f + 5f * j);
                objMapExist[i, j].GetComponent<RectTransform>().anchoredPosition = vector2;

                //初期状態では非表示
                Color color = objMapExist[i, j].GetComponent<Image>().color;
                color.a = 0;
                objMapExist[i, j].GetComponent<Image>().color = color;

                /*
                if ((map[i, j] == 1) || (map[i, j] == 2))
                {
                    //2Dマップ生成のデバッグ用
                    if ((map[i, j] == 1))
                    {
                        objMapExist[i, j].GetComponent<Image>().color = new Color(1, 0, 0, 0.5f);
                    }
                    else if ((map[i, j] == 2))
                    {
                        objMapExist[i, j].GetComponent<Image>().color = new Color(0, 1, 0, 0.5f);
                    }
                }
                */
            }
        }
    }
    
    //オブジェクト生成 (プレイヤー以外)
    void GenerateObj(int[,] map, GameObject obj)
    {
        while (true)
        {
            int mapX = UnityEngine.Random.Range(0, map.GetLength(0) - 1);
            int mapY = UnityEngine.Random.Range(0, map.GetLength(1) - 1);

            if (map[mapX, mapY] == 1)
            {
                //プレイヤーは生成済みのため移動だけ
                if (obj.CompareTag("Player") == true)
                {
                    obj.transform.position = new Vector3(mapX, 0, mapY);
                }
                //その他は生成と移動
                else
                {
                    GameObject objInstant = Instantiate(obj, new Vector3(mapX, 0, mapY), Quaternion.Euler(0f, 0f, 0f));
                }
                break;
            }
        }
    }
    
    //時間経過で敵を生成
    void AppearEnemy()
    {
        //出現上限
        if (numEnemys >= maxNumOfEnemys)
        {
            return;
        }

        //時間経過で敵を生成
        countTime += Time.deltaTime;
        if (countTime > appearNextTime)
        {
            //出現させる敵をランダムに選ぶ
            int enemyIdx = UnityEngine.Random.Range(0, objEnemyList.Length);
            GenerateObj(map, objEnemyList[enemyIdx]);

            //初期化処理
            countTime = 0f;
            numEnemys++;
        }
    }
}

最後に

いかがでしたでしょうか。
今回は非常に簡単な変更内容でした。
次回はラストとなりますが、結構変更点が多いです。(考え方はシンプルですが)
あと少しだけ、頑張ってついてきて下さい!

◆最後「現在位置マップの作成」

以上、だらはでした。

スポンサーリンク

応用

Posted by daraha_gm