『UNITYゲーム プログラミング・バイブル』「ENTRY No.05 3D 迷路」学習記録1

Unityゲーム プログラミング・バイブル

Unityゲーム プログラミング・バイブル

序文

『UNITYゲーム プログラミング・バイブル』12日目。

進捗

(学習時間:???)

コード実装部分

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

public class MazeMake : MonoBehaviour {
    [SerializeField]int mapSize = 5;      // マップサイズ(必ず奇数値)
    int[,] maze;           // マップ生成用(0=壁  1=通路)
    int cnt = 0;          // 迷路の壁を生成した回数

    [SerializeField]
    GameObject wallObject;  // 壁オブジェクトを設定

    [SerializeField]
    GameObject groundObject;// 地面オブジェクトを設定

    [SerializeField]
    GameObject GoalObject;  // ゴールオブジェクトを設定

    void Start () {

        // マップ生成終了条件を算出(マップ内で通路が置ける場所-1)
        // 3×3の場合、2×2-1=3
        // 5×5の場合、3×3-1=8
        // 7×7の場合、4×4-1=15
        int endNum = ((mapSize + 1) / 2) * ((mapSize + 1) / 2) - 1;

        // 迷路マップ
        // +2は外周の壁分
        maze = new int[mapSize+2, mapSize+2];

        while(endNum > cnt)
        {
            //Debug.Log(cnt);
            // ランダムで迷路を掘る場所を決める(必ず偶数になるようにする)
            // 3×3の場合、0,2
            // 5×5の場合、0,2,4
            // 7×7の場合、0,2,4,6
            int x = Random.Range (0, (mapSize + 1) / 2) * 2;
            int y = Random.Range (0, (mapSize + 1) / 2) * 2;


            if (cnt == 0)
            {
                // 外周分を考慮して+1
                maze[x + 1, y + 1] = 1;                     // 初回だけ予め穴を開けておく
            }

            // 外周分を考慮して+1
            // 開始地点が通路ならその地点を起点として穴を堀り始める
            if (maze [x+1, y+1] == 1)
                WallDig (x, y, 0);

        }
            
        // パズル画面の表示
        Output ();

        // ゴールを描画
        Instantiate (GoalObject, new Vector3 (mapSize, 0, mapSize), Quaternion.identity);
    }



    // 迷路生成(穴掘り法)
    // 引数のx,yは外周を考慮していない
    void WallDig(int x, int y, int oldVec){
        // [0]:上
        // [1]:右
        // [2]:下
        // [3]:左
        int[] vx = { 0, 2, 0, -2 };
        int[] vy = { -2, 0, 2, 0 };

        bool retFlg = false;

        // 四方向をランダムで選んで2マス先が掘れるかどうかをチェック
        // r = 0:上
        // r = 1:右
        // r = 2:下
        // r = 3:左
        int r = Random.Range (0, 4);

        // マップの端は掘ることが出来ない
        // y = 0は一番上だから上に掘ることはできない
        if (r == 0 && y <= 0) retFlg = true;
        // 右に掘ったらマップ外に出る時、右に掘ることはできない
        if (r == 1 && (x+2) >= mapSize) retFlg = true;
        // 下に掘ったらマップ外に出る時、下に掘ることはできない
        if (r == 2 && (y+2) >= mapSize) retFlg = true;
        // x == 0は一番左だから左に掘ることはできない
        if (r == 3 && x <= 0) retFlg = true;

        // 問題があるときはもう一度やり直す
        // 失敗したら違う方向
        if (retFlg) {
            WallDig (x, y, oldVec);
            return;
        }

        // 壁を掘れそうなら掘る
        // (+1は外周分を考慮)
        if (maze [x+1 + vx [r], y+1 + vy [r]] == 0) {
            // 右辺の1は通路を示す
            // 次の地点に穴を掘る
            maze[x+1 + vx [r], y+1 + vy [r]] = 1;
            // 経由地点にも穴を掘る
            maze[x+1 + vx [r] / 2, y+1 + vy [r] / 2] = 1;
            // 穴の数を増やす
            cnt++;

            // 再帰ループ
            // 次の地点から穴を掘る
            WallDig (x + vx[r], y + vy[r], r);
        }

        // 堀った先がすでに通路だったときはreturn
    }




    // パズルをモデルを使って出力
    void Output () {
        GameObject obj = new GameObject();    // 親オブジェクトの中に格納する
        obj.name = "Mize";

        for (int x = 0; x < mapSize+2; x++) {
            for (int y = 0; y < mapSize+2; y++) {   
                if (maze [x, y] == 0) {
                    Instantiate (wallObject, new Vector3 (x, 0, y), Quaternion.identity).transform.parent = obj.transform;
                } else {
                    Instantiate (groundObject, new Vector3 (x, 0, y), Quaternion.identity).transform.parent = obj.transform;
                }
            }
        }
    }
}

実行結果

なし

感想

ノベルゲームのソースコードを読み解くことを諦めて、3D編に戻る。

「穴掘り法」と呼ばれるアルゴリズムで迷路を生成していくらしい。

ソースコードの中で外周の壁を考慮する部分と考慮しない部分が混在していて少し混乱したが、一応理解することができたと思う。

でもなんかアナログなアルゴリズムだねぇ。

カロリーメイトください。

BGM

今夜はブギー・バック / TOKYO No.1 SOUL SETHALCALI www.youtube.com