728x90
BSP 알고리즘을 활용한 맵생성의 과정을 한눈에 보기 위해서 IEnumerator를 한번 활용해 보았다
결과
기초 코드는 아래를 참조하였습니다
Unity2D 절차 지향적 맵 생성
2D 타일맵을 검색해 보면 BSP 알고리즘을 이용한 생성과 간단한 절차 지향적 알고리즘을 이용한 생성을 많이 소개합니다그중 절차 지향적으로 랜덤맵을 생성하는 방법을 시도해 보았습니다 결
rimugiri.tistory.com
1. 맵이 나눠지는 과정을 확인할 수 있는 코드입니다
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace PKW_Tilemap
{
public class TestVisual : DungenGeneratorBase
{
public RandomSquareSO _randomSquareSO;
[Range(1, 10)] public int offset = 1;
public static IEnumerator<List<BoundsInt>> _roomList;
public virtual void Clear()
{
tilemapVisualizer.Clear();
_roomList = null;
}
protected override void RunGenerator()
{
if (_roomList == null)
{
_roomList = BinarySpacePartitioning(new BoundsInt((Vector3Int)startPosition,
new Vector3Int(_randomSquareSO.maxWidth, _randomSquareSO.maxHeight, 0)), _randomSquareSO.minWidth, _randomSquareSO.minHeight);
}
_roomList.MoveNext();
HashSet<Vector2Int> _squareSet = new HashSet<Vector2Int>();
foreach (BoundsInt _room in _roomList.Current)
{
for (int col = offset; col < _room.size.x - offset; col++)
{
for (int row = offset; row < _room.size.y - offset; row++)
{
Vector2Int _pos = new Vector2Int(_room.x + col, _room.y + row);
_squareSet.Add(_pos);
}
}
}
tilemapVisualizer.PaintFloorTile(_squareSet);
WallGenerator.GenerateWall(_squareSet, tilemapVisualizer);
}
IEnumerator<List<BoundsInt>> BinarySpacePartitioning(BoundsInt _spaceSplited, int _minWidth, int _minHeight)
{
Queue<BoundsInt> _roomList = new Queue<BoundsInt>();
List<BoundsInt> _roomListTemp = new List<BoundsInt>();
_roomList.Enqueue(_spaceSplited);
while (_roomList.Count > 0)
{
BoundsInt _room = _roomList.Dequeue();
// 현재 수직 또는 수평으로 나눠준다.
if (_room.size.x >= _minWidth && _room.size.y >= _minHeight)
{
if (Random.value < 0.5f)
{
if (_room.size.x >= _minWidth * 2)
{
SplitVertically(_room, _roomList, _minWidth);
}
else if (_room.size.y >= _minHeight * 2)
{
SplitHorizontally(_room, _roomList, _minHeight);
}
else
{
_roomListTemp.Add(_room);
}
}
else
{
if (_room.size.y >= _minHeight * 2)
{
SplitHorizontally(_room, _roomList, _minHeight);
}
else if (_room.size.x >= _minWidth * 2)
{
SplitVertically(_room, _roomList, _minWidth);
}
else
{
_roomListTemp.Add(_room);
}
}
yield return _roomList.ToList();
}
}
yield return _roomList.ToList();
}
private void SplitHorizontally(BoundsInt _room, Queue<BoundsInt> _roomList, int _minHeight)
{
int _splitPointY = Random.Range(_minHeight, _room.size.y - _minHeight);
BoundsInt _room1 = new BoundsInt(_room.min, new Vector3Int(_room.size.x, _splitPointY, _room.size.z));
BoundsInt _room2 = new BoundsInt(new Vector3Int(_room.min.x, _room.min.y + _splitPointY, _room.size.z),
new Vector3Int(_room.size.x, _room.size.y - _splitPointY, _room.size.z));
_roomList.Enqueue(_room1);
_roomList.Enqueue(_room2);
}
private void SplitVertically(BoundsInt _room, Queue<BoundsInt> _roomList, int _minWidth)
{
int _splitPointX = Random.Range(_minWidth, _room.size.x - _minWidth);
BoundsInt _room1 = new BoundsInt(_room.min, new Vector3Int(_splitPointX, _room.size.y, _room.size.z));
BoundsInt _room2 = new BoundsInt(new Vector3Int(_room.min.x + _splitPointX, _room.min.y, _room.size.z),
new Vector3Int(_room.size.x - _splitPointX, _room.size.y, _room.size.z));
_roomList.Enqueue(_room1);
_roomList.Enqueue(_room2);
}
}
}
2. 길이 생성되는 과정을 나타낸 코드입니다
현재 가장 가까운 점끼리 연결하지만 맵을 관통하는 다리가 발생하는경우가 생깁니다
추후 UnionFind를 이용하여 이점을 수정해보겠습니다
using System.Collections.Generic;
using UnityEngine;
namespace PKW_Tilemap
{
public class TestVisualCorridor : TestVisual
{
public static IEnumerator<HashSet<Vector2Int>> _enumerator;
private HashSet<Vector2Int> _floorPosition;
public override void Clear()
{
tilemapVisualizer.Clear();
_enumerator = null;
List<BoundsInt> _roomList = ProcedualTilemap.BinarySpacePartitioning(new BoundsInt((Vector3Int)startPosition,
new Vector3Int(_randomSquareSO.maxWidth, _randomSquareSO.maxHeight, 0)), _randomSquareSO.minWidth, _randomSquareSO.minHeight);
List<Vector2Int> _roomCenterList = new List<Vector2Int>();
foreach (BoundsInt _room in _roomList)
{
Vector2Int _center = (Vector2Int)Vector3Int.RoundToInt(_room.center);
_roomCenterList.Add(_center);
}
_floorPosition = CreateRooms(_roomList);
_enumerator = ConnectRooms(_roomCenterList);
}
protected override void RunGenerator()
{
_enumerator.MoveNext();
_floorPosition.UnionWith(_enumerator.Current);
tilemapVisualizer.PaintFloorTile(_floorPosition);
WallGenerator.GenerateWall(_floorPosition, tilemapVisualizer);
}
private HashSet<Vector2Int> CreateRooms(List<BoundsInt> _roomList)
{
HashSet<Vector2Int> _squareSet = new HashSet<Vector2Int>();
foreach (BoundsInt _room in _roomList)
{
for (int col = offset; col < _room.size.x - offset; col++)
{
for (int row = offset; row < _room.size.y - offset; row++)
{
Vector2Int _pos = new Vector2Int(_room.x + col, _room.y + row);
_squareSet.Add(_pos);
}
}
}
return _squareSet;
}
IEnumerator<HashSet<Vector2Int>> ConnectRooms(List<Vector2Int> _roomCenterList)
{
HashSet<Vector2Int> _corridor = new HashSet<Vector2Int>();
Vector2Int _curCenter = _roomCenterList[Random.Range(0, _roomCenterList.Count)];
_roomCenterList.Remove(_curCenter);
while (_roomCenterList.Count > 0)
{
Vector2Int _closestCenter = GetClosestCenter(_curCenter, _roomCenterList);
_roomCenterList.Remove(_closestCenter);
HashSet<Vector2Int> _newCorridor = CreateCorridor(_curCenter, _closestCenter);
_curCenter = _closestCenter;
_corridor.UnionWith(_newCorridor);
yield return _corridor;
}
yield return _corridor;
}
private HashSet<Vector2Int> CreateCorridor(Vector2Int _curCenter, Vector2Int _closestCenter)
{
HashSet<Vector2Int> _corridor = new HashSet<Vector2Int>();
Vector2Int _curPos = _curCenter;
_corridor.Add(_curPos);
while (_curPos.y != _closestCenter.y)
{
if (_curPos.y < _closestCenter.y)
{
_curPos.y += Vector2Int.up.y;
}
else
{
_curPos.y += Vector2Int.down.y;
}
_corridor.Add(_curPos);
}
while (_curPos.x != _closestCenter.x)
{
if (_curPos.x < _closestCenter.x)
{
_curPos.x += Vector2Int.right.x;
}
else
{
_curPos.x += Vector2Int.left.x;
}
_corridor.Add(_curPos);
}
return _corridor;
}
private Vector2Int GetClosestCenter(Vector2Int curCenter, List<Vector2Int> roomCenterList)
{
float _distance = float.MaxValue;
Vector2Int _closestCenter = Vector2Int.zero;
foreach (Vector2Int _center in roomCenterList)
{
float _curDistance = Vector2Int.Distance(curCenter, _center);
if (_curDistance < _distance)
{
_distance = _curDistance;
_closestCenter = _center;
}
}
return _closestCenter;
}
}
}
3. Editor입니다
using UnityEditor;
using UnityEngine;
namespace PKW_Tilemap
{
[CustomEditor(typeof(TestVisual), true)]
public class TestVisualEditor : Editor
{
private TestVisual _generator;
private void Awake()
{
_generator = target as TestVisual;
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (GUILayout.Button("Generate"))
{
_generator.GenerateDungen();
}
if (GUILayout.Button("Reset"))
{
_generator.Clear();
}
}
}
}
728x90
'unity > 유니티 기초' 카테고리의 다른 글
keytool 문제 해결방법 - keytool은 내부또는외부명령,실행할수있는프로그램,또는배치파일이아닙니다. (0) | 2024.10.14 |
---|---|
Unity - Button Attribute만들기 (0) | 2024.08.21 |
유니티 vector2int (0) | 2024.08.15 |
Unity2D - Tilemap에서 플레이어가 움직이지 않을경우 (0) | 2024.08.09 |
unity- lerp, lerpAngle (0) | 2024.03.26 |