Unity/기타 - 개발

flood-fill algorithm을 활용한 미로맵 만들기 - 2

SiJun-Park 2025. 3. 28. 05:17

이전시간과 이어서 미로를 만드는데 이번에는 벽의 높낮이와 좌표의 기록을 고정으로 하였는데 유연하게 바꿔주게 하고

월드 벽을 생성해 맵밖으로 나가는 일을 없도록 만들어주는 것이 목표입니다.

 

https://www.youtube.com/watch?v=HuQITd3epIU&list=PLFt_AvWsXl0ctd4dgE1F8g3uec4zKNRV0&index=13

참고한 유튜브 입니다.

 

 

[Serializable]
public class Map
{
    public Coordinate mapSize;
    [Range(0,1)]
    public float wallPercent;
    public int stage;
    public float minWallHeight, maxWallHeight;
    public Color frontcolor, backcolor;
    
    public Coordinate mapCenter=> new Coordinate(mapSize.x / 2, mapSize.y / 2);
     

}

이전과 달라진 점은 mapSize를 Vector2로 고정해서 사용했는데 그러지 말고, 맵 바뀌는 것을 생각해서 flexible하게 하기 위해서 Map Class를 생성해줍니다

 

        System.Random rnd = new System.Random(_CurrentMap.stage);
        for (int i = 0; i < Count; i++)
        {
            Coordinate coordinate = GetRandomCoord();
            if (coordinate == _CurrentMap.mapCenter) continue;
            CreatedWallCoordinate[coordinate.x, coordinate.y] = true;
            wallCount++;
            if (MapAccessible(CreatedWallCoordinate, wallCount)) { 
                Vector3 wall = CreatePosition(coordinate.x, coordinate.y);
                float height = Mathf.Lerp(_CurrentMap.minWallHeight, _CurrentMap.maxWallHeight, (float)rnd.NextDouble());
                GameObject newWall = Instantiate(_WallPrefab, wall + Vector3.up * height/2f, Quaternion.identity);
                newWall.transform.SetParent(_WallParent.transform);
                newWall.transform.localScale = new Vector3((1 - _TileSize) * _TotalSize, height, (1 - _TileSize) * _TotalSize) ;

                // COLOR
                Renderer render = newWall.GetComponent<Renderer>();
                Material material = new Material(render.sharedMaterial);
                float colorpercent = coordinate.y / (float)_CurrentMap.mapSize.y;
                material.color = Color.Lerp(_CurrentMap.frontcolor, _CurrentMap.backcolor, colorpercent);
                render.sharedMaterial = material;

            }
            else
            {
                CreatedWallCoordinate[coordinate.x, coordinate.y] = false;
                wallCount--;

            }
        }

 

이전과 다르게 for문을 나름대로 최적화 되게 바꾸었습니다.

 

여기서는 건물들의 높이가 랜덤으로 되면 좋겠으니, Stage에서 Percent를 랜덤으로 돌려주고 

height를 Lerp 함수를 사용해서 사이 값을 얻어지게 해줍니다. 

 

그 다음에는 한 색으로만 칠하면 높은지 낮은지 모르게 됩니다.

 

그러니 material을 추가해 색깔을 거리가 멀수록 backcolor에 가깝도록 해줍니다.

 

    private void CreateWorldWall(Vector3 direction, float point, float x, float y)
    {
        GameObject wall = Instantiate(_WorldWall, direction * point * _TotalSize, Quaternion.identity, _TileParent.transform);
        wall.transform.localScale = new Vector3(x, 1, y) * _TotalSize;
    }

 

그리고 세계 밖으로 나가지는 것을 방지하기 위해서 벽을 만들어줍니다.

 

        CreateWorldWall(Vector3.left, (_CurrentMap.mapSize.x + _MaxMapSize.x) / 4f, (_MaxMapSize.x - _CurrentMap.mapSize.x) / 2f, _CurrentMap.mapSize.y);
        CreateWorldWall(Vector3.right, (_CurrentMap.mapSize.x + _MaxMapSize.x) / 4f, (_MaxMapSize.x - _CurrentMap.mapSize.x) / 2f, _CurrentMap.mapSize.y);
        CreateWorldWall(Vector3.forward, (_CurrentMap.mapSize.y + _MaxMapSize.y) / 4f, _MaxMapSize.x, (_MaxMapSize.y - _CurrentMap.mapSize.y) / 2f);
        CreateWorldWall(Vector3.back, (_CurrentMap.mapSize.y + _MaxMapSize.y) / 4f, _MaxMapSize.x, (_MaxMapSize.y - _CurrentMap.mapSize.y) / 2f);

사용법은 이러한데, 왜 /4를 해주고, /2를 해주냐?

 

전체 맵 크기를 M이라고하고, 현재 맵사이즈를 X라고하고,

세계 밖으로 못나가게 하기 위해 우측에 벽을 생성한다고 가정을 합니다.

 

그러면 x는 -2 0 2 이기 때문에 우측 점을 가기 위해서는 /2를 해주어 이동을 어디로 했는지 찾아줍니다.

그 다음에 보간을 하기 위해서 전체 맵에서 x를 더해줍니다.

그렇게 된다면 x/2 + (m-x)/4가 됩니다.

결국에는 (2x-m-x)/4 가 되니 

(x-m)/4가 됩니다. 

즉 현재에서 전체 맵을 빼고 나누기 4를 해주면 중앙을 잡을 수 있기 때문에 저러한 공식이 나오게 되었습니다.

 

결과