추석을 걸치고 일주일 만의 TIL이다.
일주일 간의 과정으로 2D 로그라이크 게임 프로젝트가 완료됐고, 아래의 깃헙 링크는 그 결과물이다.
https://github.com/GYALLERHORN/A07_RoguelikeProject
GitHub - GYALLERHORN/A07_RoguelikeProject
Contribute to GYALLERHORN/A07_RoguelikeProject development by creating an account on GitHub.
github.com
짧게 이진 공간 분한(BSP)알고리즘으로 작성한 맵 생성 기능에 대해 쓰자면, 하나의 공간을 2^n + @의 공간으로 나누어 여러 개의 분할된 공간을 생성하고, 각각의 공간에 적절한 크기의 맵을 구현하는 알고리즘이다.
아래는 BSP를 이용한 맵의 예시다.

이전의 RandomWalk 알고리즘으로 생성된 맵과 비교하면 하나의 방이 불규칙하게 생성되는 것을 볼 수 있다.
아래는 RandomWalk 알고리즘을 구현한 스크립트다. 이전의 코드와 호환하지 않는다.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class RoomFirstDungeonGenerator : SimpleWalkDungeonGenerator // BSP 맵 생성 스크립트
{
[SerializeField]
private int minRoomWidth = 4, minRoomHeight = 4;
[SerializeField]
private int dungeonWidth = 20, dungeonHeight = 20;
[SerializeField]
[Range(0,10)]
private int offset = 1;
[SerializeField]
private bool randomWalkRooms = false;
protected override void RunProceduralGeneration()
{
CreateRooms();
}
private void CreateRooms() // BSP 방 생성 스크립트
{
var roomsList = ProceduralGenerationAlgorithms.BinarySpacePartitioning(new BoundsInt((Vector3Int)startPosition,
new Vector3Int(dungeonWidth, dungeonHeight, 0)), minRoomWidth, minRoomHeight);
var floor = new HashSet<Vector2Int>();
if (randomWalkRooms)
{
floor = CeateRoomsRandomly(roomsList);
}
else
{
floor = CreateSimpleRooms(roomsList);
}
var roomCenters = new List<Vector2Int>();
foreach (var room in roomsList)
{
roomCenters.Add(Vector2Int.RoundToInt(room.center));
}
HashSet<Vector2Int> corridors = ConnectRooms(roomCenters);
floor.UnionWith(corridors);
tilemapVisualizer.PaintFloorTile(floor);
WallGenerator.CreateWalls(floor, tilemapVisualizer);
}
private HashSet<Vector2Int> CeateRoomsRandomly(List<BoundsInt> roomsList)
{
HashSet<Vector2Int> floor = new HashSet<Vector2Int>();
for (int i = 0; i < roomsList.Count; i++)
{
var roomBounds = roomsList[i];
var roomCenter = new Vector2Int(Mathf.RoundToInt(roomBounds.center.x), Mathf.RoundToInt(roomBounds.center.y));
var roomFloor = RunRandomWalk(randomWalkParameters, roomCenter);
foreach (var position in roomFloor)
{
if (position.x >= (roomBounds.xMin + offset) && position.x <= (roomBounds.xMax - offset)
&& position.y >= (roomBounds.yMin + offset) && position.y <= (roomBounds.yMax - offset))
{
floor.Add(position);
}
}
}
return floor;
}
private HashSet<Vector2Int> ConnectRooms(List<Vector2Int> roomCenters) // BSP 복도 생성 스크립트
{
var corridors = new HashSet<Vector2Int>();
var currentRoomCenter = roomCenters[Random.Range(0, roomCenters.Count)];
roomCenters.Remove(currentRoomCenter);
while (roomCenters.Count > 0)
{
Vector2Int closest = FindClosestPointTo(currentRoomCenter, roomCenters);
roomCenters.Remove(closest);
HashSet<Vector2Int> newCorridor = CreateCorridor(currentRoomCenter, closest);
currentRoomCenter = closest;
corridors.UnionWith(newCorridor);
}
return corridors;
}
private Vector2Int FindClosestPointTo(Vector2Int currentRoomCenter, List<Vector2Int> roomCenters)
{
Vector2Int closest = Vector2Int.zero;
float distance = float.MaxValue;
foreach (var position in roomCenters)
{
float currentDistance = Vector2.Distance(position, currentRoomCenter);
if (currentDistance < distance)
{
distance = currentDistance;
closest = position;
}
}
return closest;
}
private HashSet<Vector2Int> CreateCorridor(Vector2Int currentRoomCenter, Vector2Int destination)
{
var corridor = new HashSet<Vector2Int>();
var position = currentRoomCenter;
corridor.Add(position);
while (position.y != destination.y)
{
if (destination.y > position.y)
{
position += Vector2Int.up;
}
else if (destination.y < position.y)
{
position += Vector2Int.down;
}
corridor.Add(position);
}
while (position.x != destination.x)
{
if (destination.x > position.x)
{
position += Vector2Int.right;
}
else if (destination.x < position.x)
{
position += Vector2Int.left;
}
corridor.Add(position);
}
return corridor;
}
private HashSet<Vector2Int> CreateSimpleRooms(List<BoundsInt> roomList)
{
var floor = new HashSet<Vector2Int>();
foreach (var room in roomList)
{
for (int col = offset; col < room.size.x - offset; col++)
{
for (int row = offset; row < room.size.y - offset; row++)
{
Vector2Int position = (Vector2Int)room.min + new Vector2Int(col, row);
floor.Add(position);
}
}
}
return floor;
}
}
시간이 없어서 세부 설명 주석을 적지 못했다.
아래는 프로젝트 맵 작성 내내 참고한 유튜브 링크다. 맵 작성~벽 작성까지 전부 참고했다.
풀 영어이긴 하지만, 재밌는거 많다. 나중에 도움될 내용이 있으면 지속적으로 참고하지 않을까 생각한다.
https://www.youtube.com/@SunnyValleyStudio
Sunny Valley Studio
Hi! I'm Peter 👋 I am a game developer / programmer. Also Unity insider 😁 If you want to learn how to make games in Unity I do my best to share my knowledge about coding in C# and creating game mechanics in my video tutorials and in my courses at (htt
www.youtube.com
'UNITY' 카테고리의 다른 글
| UNITY_20231006[유니티 3D 강의, 알고리즘 문제 풀이] (0) | 2023.10.06 |
|---|---|
| UNITY_20231005[유니티 3D 강의 시작, 알고리즘 문제 풀이] (0) | 2023.10.05 |
| UNITY_20231002[팀 과제-로그라이크 RPG - 벽, 방, 복도 만들기] (0) | 2023.09.27 |
| UNITY_20230926[팀 과제-로그라이크 RPG - RandomWalk 알고리즘] (0) | 2023.09.26 |
| UNITY_20230925[팀 과제-로그라이크 RPG, 디자인 패턴-전략 패턴] (1) | 2023.09.25 |