読者です 読者をやめる 読者になる 読者になる

スマゲ

スマートなゲームづくりを目指して日々精進

Unityで迷路の生成フローを可視化する

前回紹介した迷路の自動生成アルコリズムを可視化してみました。
smartgames.hatenablog.com

今回は前回使用した穴掘り法の他に棒倒し法も可視化してみます。

■実装(穴掘り法)
コードの中身は前回のものとあまりかわりません

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

public class MazeBuilder : MonoBehaviour {

    [SerializeField]
    private GameObject ground;
    [SerializeField]
    private GameObject wallPrefab;
    [SerializeField]
    private Camera mainCamera;

    private int mazeHeight;
    private int mazeWidth;
    private bool[,] maze;
    private GameObject[,] mazeObject;
    private List<Vector2> searchDirections = new List<Vector2> { Vector2.up, Vector2.down, Vector2.right, Vector2.left };

    private void Start()
    {
        CreateMaze(21, 21);
    }

    private void CreateMaze(int height, int width)
    {
        mazeWidth = width;
        mazeHeight = height;

        InitializeMaze();

        StartCoroutine(DigMaze());
    }

    private void InitializeMaze()
    {
        maze = new bool[mazeWidth, mazeHeight];
        mazeObject = new GameObject[mazeWidth, mazeHeight];
        for (int i = 0; i < mazeWidth; i++)
        {
            for (int j = 0; j < mazeHeight; j++)
            {
                maze[i, j] = true;
                mazeObject[i, j] = Instantiate(wallPrefab, new Vector3(i, 0, j), Quaternion.identity) as GameObject;
            }
        }

        var centered = new Vector3(mazeWidth / 2f - 0.5f, 0, mazeHeight / 2f - 0.5f);
        ground.transform.localScale = new Vector3(mazeWidth, 1, mazeHeight);
        ground.transform.position = centered + Vector3.down * 0.5f;

        mainCamera.transform.position = centered + Vector3.up * ((mazeWidth + mazeHeight) / 2f);
    }

    private IEnumerator DigMaze()
    {
        int startPosW = Enumerable.Range(0, mazeWidth).Where(i => i % 2 != 0).OrderBy(i => Guid.NewGuid()).First();
        int startPosH = Enumerable.Range(0, mazeHeight).Where(i => i % 2 != 0).OrderBy(i => Guid.NewGuid()).First();
        yield return Dig(new Vector2(startPosW, startPosH));
    }

    private IEnumerator Dig(Vector2 point)
    {
        yield return RemoveWall(point);

        foreach (var dir in searchDirections.OrderBy(i => Guid.NewGuid())){
            var checkPos = point + dir * 2;
            if (IsInBoard(checkPos) && maze[(int)checkPos.x, (int)checkPos.y])
            {
                yield return RemoveWall(point + dir);
                yield return Dig(checkPos);
            }
        }
    }

    private IEnumerator RemoveWall(Vector2 point)
    {
        var w = (int)point.x;
        var h = (int)point.y;
        maze[w, h] = false;
        Destroy(mazeObject[w, h]);
        yield return new WaitForSeconds(0.05f);
    }

    private bool IsInBoard(Vector2 pos)
    {
        return pos.x >= 0 && pos.y >= 0 && pos.x < mazeWidth && pos.y < mazeHeight;
    }
}

■実装(棒倒し法)

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

public class MazeBuilder2 : MonoBehaviour
{

    [SerializeField]
    private GameObject ground;
    [SerializeField]
    private GameObject wallPrefab;
    [SerializeField]
    private Camera mainCamera;

    private int mazeHeight;
    private int mazeWidth;
    private bool[,] maze;
    private List<Vector2> searchDirectionsFirst = new List<Vector2> { Vector2.up, Vector2.down, Vector2.right, Vector2.left };
    private List<Vector2> searchDirections = new List<Vector2> { Vector2.up, Vector2.right, Vector2.left };

    private void Start()
    {
        CreateMaze(21, 21);
    }

    private void CreateMaze(int height, int width)
    {
        mazeWidth = width;
        mazeHeight = height;

        InitializeMaze();

        StartCoroutine(CreateMaze());
    }

    private void InitializeMaze()
    {
        maze = new bool[mazeWidth, mazeHeight];

        var centered = new Vector3(mazeWidth / 2f - 0.5f, 0, mazeHeight / 2f - 0.5f);
        ground.transform.localScale = new Vector3(mazeWidth, 1, mazeHeight);
        ground.transform.position = centered + Vector3.down * 0.5f;

        mainCamera.transform.position = centered + Vector3.up * ((mazeWidth + mazeHeight) / 2f);
    }

    private IEnumerator CreateMaze()
    {
        var startPositions = new List<Vector2>();
        for (int i = 0; i < mazeWidth; i++)
        {
            for (int j = 0; j < mazeWidth; j++)
            {
                if (i == 0 || j == 0 || i == mazeWidth - 1 || j == mazeWidth - 1 )
                {
                    Instantiate(wallPrefab, new Vector3(i, 0.5f, j), Quaternion.identity);
                }
                else if (i % 2 == 0 && j % 2 == 0)
                {
                    Instantiate(wallPrefab, new Vector3(i, 0.5f, j), Quaternion.identity);
                    startPositions.Add(new Vector2(i, j));
                }
            }
        }

        foreach (var pos in startPositions)
        {
            if (pos.x == 2)
            {
                foreach (var dir in searchDirectionsFirst.OrderBy(i => Guid.NewGuid()))
                {
                    var checkPos = pos + dir;
                    if (maze[(int)checkPos.x, (int)checkPos.y])
                    {
                        continue;
                    }
                    else
                    {
                        yield return CreateWall(checkPos);
                        break;
                    }
                }
            }
            else
            {
                foreach (var dir in searchDirections.OrderBy(i => Guid.NewGuid()))
                {
                    var checkPos = pos + dir;
                    if (maze[(int)checkPos.x, (int)checkPos.y])
                    {
                        continue;
                    }
                    else
                    {
                        yield return CreateWall(checkPos);
                        break;
                    }
                }
            }
        }

        yield break;
    }

    private IEnumerator CreateWall(Vector2 position)
    {
        maze[(int)position.x, (int)position.y] = true;
        Instantiate(wallPrefab, new Vector3(position.x, 0.5f, position.y), Quaternion.identity);
        yield return new WaitForSeconds(0.05f);
    }
}

■実行(穴掘り法)
f:id:sanukin39:20170511235404g:plain

■実行(棒倒し法)
f:id:sanukin39:20170511235423g:plain

■まとめ
迷路が作られていく様子は見ていて楽しい

■参考
自動生成迷路

■関連
Unityのナビゲーションを使って自動生成した迷路を解かせる - スマゲ