flood-fill algorithm을 활용한 미로맵 만들기 - 2
이전시간과 이어서 미로를 만드는데 이번에는 벽의 높낮이와 좌표의 기록을 고정으로 하였는데 유연하게 바꿔주게 하고
월드 벽을 생성해 맵밖으로 나가는 일을 없도록 만들어주는 것이 목표입니다.
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를 해주면 중앙을 잡을 수 있기 때문에 저러한 공식이 나오게 되었습니다.