일단 본인은 코딩을 시작한지 13일 정도 됐다. 코딩 함수도 많이 모른다. 그런데 코딩 어떻게 하냐면 AI빨로 버티는중이다.
코딩 실력은 개판이지만 CHATGPT를 어떻게든 같이 써서 꾸역꾸역 만들고는 있다.
여태까지는 그나마 겨우겨우 만들어왔지만.
아이작의 랜덤 방 생성 시스템은 진짜 씨발소리가 절로 나오더라.
지금 만들고 있는 게임은 로그라이크 형식이라 이 시스템을 만들 필요가 있었다.
일단 전체 결과물은 이렇다.
같은 모양의 방 랜덤 배열 - 다른 모양의 방 랜덤 배열 - 다른 모양, 다른 비율의 방 랜덤 배열 순이다.
첫 번째로 시도했던 건 검색이었다. 다른 사람들은 어떻게 했는지를 찾아봤다.
그런데 무슨 BSP 알고리즘이라는 듣도보도못한 이상한 걸 알아야 하더라.
화면을 분할하고 어쩌고저쩌고라는데 뭐라는지 전혀 모르겠더라.
두 번째로 시도했던 건 일단, 같은 모양의 방(1칸)을 랜덤하게 만드는 것이였다. 다른 사람들이 말하는 거 찾아봐야 이해도 못해서 내가 야매로 만들었다.
로직은 생각보다 간단했다.
일단 시작 방을 원점으로 고른다. 앞뒤는 z, 양옆은 x축으로 했다. y는 무조건 0.
그래서 원점은 Vector3(0,0,0).
방은 무조건 시작 방에서 가로 또는 세로 1칸 차이나는 곳에 생겨나도록 했다.
단, 이미 방이 있다면 생성하지 않는다. 방이 겹치면 안되니까.
이걸 반복해서 원하는 개수가 나오면 끝내도록 했다.
그 뒤, 인접한 방들의 좌표를 계산해서, 두 방 사이 거리가 1이라면 그 중간에 다리(문)을 놓는 식으로 만들었다.
두 방의 x축이 같다면 가로로, z축이 같다면 세로로 회전되도록 했다.
마지막으로 오브젝트를 소환하기 전에, x는 13, z는 9쯤 곱했는지 가로로 길게 만들어지도록 했다.
이렇게 만들어달라고 하니까 만들어주긴 하는데 제대로 작동을 안했다. 최대치를 늘려도 늘어나지도 않고 그러더라.
그래서 CHATGPT 4O With Canvas (코딩에 좀더 최적화된 버전) 을 쓰니까 바로 제대로 만들어주더라.
그래서 구현된 건 이렇다.
일단 성공적이다.
뭐 저기에 이제 랜덤으로 방을 선택하던지, 특수 방을 넣던지 하면 되겠지 하고 생각했다.
그런데 이제 지옥인 것은
다른 모양의 방들을 랜덤배치하는 거였다.
다른 모양의 방들은 1칸처럼 배치하다가는 겹쳐버려서 개판이 될 것이다.
그래서 일단 나 나름대로 알고리즘을 짜 봤다.
- ChatGPT한테 요청할 것
n 유니티에서, 방을 순차적으로 생성하는 스크립트를 짜 줘.
n 내가 말하는 순서대로 작동해야 돼.
n
u 1. 방은 4가지의 종류로 구성되어 있어. cell1, cell2, cell3, cell4. 방의 최소 개수를 정할 직렬화된 int 변수 minRoom, 방의 최대 개수를 정할 직렬화된 int 변수 maxRoom이 있어야 해. 이 두 값은 최소가 5이고, 최대가 128이야. 기본값으로 각각 7, 100으로 해. minroom보다 크고 maxRoom보다 작은 범위의 정수 중 하나를 무작위로 정해. 이 값을 roomNum이라고 할게. 그리고 지금 현재 존재하는 방의 개수를 세는 int 변수 roomCount가 있어야 해. 기본값은 0으로 해. 방이 생성될 때, 직렬화된 프리팹 변수들이 있어야 하는데, 그 변수들은 각각 roomx1, roomx2n1, roomx2n2, roomx3n1, roomx3n2, roomx3n3, roomx3n4, roomx4야. 이 각각의 변수 안에 여러 개의 프리팹을 넣을 수 있도록 만들어. 그리고 방과 방으로 이동하는 통로도 있어야 하기 때문에, 프리팹 doorWay를 직렬화된 변수로 작성해. doorWay 또한 변수 안에 여러 개의 프리팹을 넣을 수 있도록 작성해 줘.
u 2. 4가지 종류의 방은 각각 여러 개의 프리팹과, 프리팹에 부여될 좌표 리스트, 그리고 그에 따른 좌표들을 가지고 있어. 이 좌표들을 InterDot이라고 할게. (InterDot에는, 방의 생성 좌표인 (x, 0, z) 가 있어. 이 좌표를 SpawnDot이라고 할게.) 프리팹들은 다음과 같아:
u
l 1) cell1 에는 roomx1이라는 1종류의 프리팹이 들어가. 1개의 InterDot를 차지하며, 그 좌표는 좌표 리스트 A[(x, 0, z)]야. 그래서 방의 종류가 총 1개야.
l 2) cell2 에는 roomx2n1, roomx2n2로, 총 2종류의 프리팹이 들어가. 두 개의 InterDot를 차지하며, roomx2n1은 좌표 리스트 A [(x, 0, z), (x, 0, z-1)] 또는 좌표 리스트 B [(x, 0, z), (x, 0, z+1)]의 좌표를 가지고, roomx2n2는 좌표 리스트 A [(x, 0, z), (x-1, 0, z)] 또는 좌표 리스트 B[(x, 0, z), (x+1, 0, z)]의 좌표를 가져. 그래서 방의 종류가 총 4개야.
l 3) cell3 에는 roomx3n1, roomx3n2, roomx3n3, roomx3n4로, 총 4종류의 프리팹이 들어가. 세 개의 InterDot를 차지하며, roomx3n1은 좌표 리스트 A [(x, 0, z), (x+1, 0, z), (x+1, 0, z-1)] 또는 좌표 리스트 B [(x, 0, z), (x-1, 0, z), (x, 0, z-1)] 또는 좌표 리스트 C [(x, 0, z), (x, 0, z+1), (x-1, 0, z+1)]의 좌표를 가지고, roomx3n2는 좌표 리스트 A [(x, 0, z), (x+1, 0, z), (x+1, 0, z+1)] 또는 좌표 리스트 B [(x, 0, z), (x-1, 0, z), (x, 0, z+1)] 또는 좌표 리스트 C [(x, 0, z), (x, 0, z-1), (x-1, 0, z-1)] 의 좌표를 가지고, roomx3n3은 좌표 리스트 A [(x, 0, z), (x, 0, z-1,) (x+1, 0, z-1)] 또는 좌표 리스트 B [(x, 0, z), (x, 0, z+1), (x+1, 0, z)] 또는 좌표 리스트 C [(x, 0, z), (x-1, 0, z), (x-1, 0, z+1)] 의 좌표를 가지고, roomx3n4는 좌표 리스트 A [(x, 0, z), (x, 0, z+1), (x+1, 0, z+1)] 또는 좌표 리스트 B [(x, 0, z), (x, 0, z-1), (x+1, 0, z)] 또는 좌표 리스트 C [(x, 0, z), (x-1, 0, z), (x-1, 0, z-1)]의 좌표를 가져. 그래서 방의 종류가 총 12개야.
l 4) cell4 에는 roomx4라는 1종류의 프리팹이 들어가. 4개의 InterDot을 차지하며, 그 좌표는 좌표 리스트 A [(x, 0, z), (x, 0, z+1), (x+1, 0, z), (x+1, 0, z+1)] 또는 좌표 리스트 B [(x, 0, z), (x, 0, z-1), (x+1, 0, z), (x+1, 0, z-1)] 또는 좌표 리스트 C [(x, 0, z), (x-1, 0, z), (x, 0, z+1), (x-1, 0, z+1)] 또는 좌표 리스트 D [(x, 0, z), (x-1, 0, z), (x, 0, z-1), (x-1, 0, z-1)]의 좌표를 가져. 그래서 방의 종류가 총 4개야.
u 3. 이 좌표들은 각각 OuterDot이라는 또 다른 좌표들을 가지는데, OuterDot들은 InterDot으로부터 x축으로 1 또는 -1 또는 z축으로 1 또는 -1을 이동하여 만들어지는 좌표들인데, 중요한 건 이미 존재하는 InterDot들과 같은 값을 가지지 않아야 해. 만약 그런 OuterDot이 있다면, 그건 제거해야 돼.
l 예를 들면 roomx1은 InterDot이 1개 있고, 그 좌표는 (x, 0, z)니까, OuterDot은 (x+1, 0, z), (x-1, 0, z), (x, 0, z+1), (x, 0, z-1)야.
l 예를 들면 roomx2n1 중 (x, 0, z), (x-1, 0, z) 가 있다면, OuterDot은 두 InterDot으로부터 x 방향으로 1 또는 -1 또는 z 방향으로 1 또는 -1을 이동한 좌표들이지만, (x, 0, z)에서 x 방향으로 -1칸 만큼 이동한 좌표는 이미 InterDot (x-1, 0, z)이 차지하고 있는 좌표이기 때문에, 이 좌표는 OuterDot에서 삭제해야 해.
u 4. 방이 생성되는 시작점은 (0, 0, 0)이야. 이 방은 반드시, cell1의 roomx1 프리팹이 소환되어야 해. 이 방을 시작 방 구역이라고 할게. 시작 방이 소환되었다면, roomCount 변수에 1을 더해. 시작 방으로부터 4개의 OuterDot이 생겼을 거야. 여기서 알려주자면, OuterDot은 다음 방이 소환될 InterDot들의 시작 좌표 (x, 0, z)라고 생각하면 돼.
u 5. 이 시작방의 4개의 OuterDot에서, cell1 또는 cell2 또는 cell3 또는 cell4가 생성될지, 또는 생성되지 않을지를 정해. 단, OuterDot의 위치에서 생성될 때, 그 위치가 이미 다른 방의 InterDot이 차지하고 있다면, 그 위치에서는 더 이상 방을 생성하지 않아야 해. (cell1, cell2, cell3, cell4가 생성될 확률은 각각 50%, 30%, 10%, 10%야. 그리고 이 확률을 모두 합쳐서, 생성될 확률이 1이라고 한다면, 방이 생성되지 않을 확률을 직렬화 변수로 만들고(범위 0~1), 생성될 확률들에 (1-방이 생성되지 않을 확률)을 곱해서, 방이 생성되지 않을 확률을 제외한 나머지 확률을 정해.) OuterDot에서 어떤 종류의 방이 생성될지 정해졌다면, 그 다음은 그 종류의 방들 중 어떤 방이 생성될지를 결정해야 돼. 무작위로 결정되지만, 만약 결정된 방의 InterDot이 다른 방의 InterDot과 겹쳐진다면, 그 방은 제외한 채로 다시 무작위로 방을 결정해야 해. 방이 결정될 때마다 roomCount 변수에 1을 더하고, 그에 상응하는 좌표 리스트를 만들어. 예를 들어서, 4번째로 만들어진 방이라면, RoomListInter004[]라는 리스트를 만들고, 그 리스트에는 4번째로 만들어진 방의 InterDot 좌표들을 집어넣어. 25번째로 만들어진 방이라면, RoomListInter025[]라는 리스트를 만들고, 그 리스트에는 25번째로 만들어진 방의 InterDot 좌표들을 집어넣어.
u 6. 모든 OuterDot들에 대한 방 생성 여부가 정해지고, 어떤 방이 생성될지 정해졌다면, 방 종류 시작 방의 OuterDot들에 각각 SpawnDot을 배치하고, 그 SpawnDot들을 기준으로 정해진 방의 InterDot들과 OuterDot들을 배치해. 예를 들어서, 어떤 OuterDot에 SpawnDot을 놓고 InterDot들을 배치할 때, 그 방의 종류가 roomx3n1이고, 좌표 리스트 A를 가진 방이라면, 그 좌표들의 x값과 z값에 SpawnDot의 x값과 z값을 대입한 뒤 좌표를 소환하는 거야. 배치한 InterDot들과 겹쳐진 OuterDot들은 삭제해. 방이 생성되지 않는다고 정해진 OuterDot들도 삭제해.
u 7. 그러면 남은 OuterDot들에서 또 다시 5번처럼, 방을 생성할지 말지를 결정하고, 방 종류를 다시 무작위로 선택하고, 선택될 때마다 roomCount 변수에 1을 더해. 6번처럼 다시 정해진 방의 InterDot들과 OuterDot들을 배치해. 그리고 다시 새로운 InterDot들과 겹쳐진 OuterDot들, 방이 생성되지 않는다고 정해진 OuterDot들을 삭제해.
u 8. 7번을 반복해서, 방의 개수 roomCount가 roomNum과 같아질 때까지 생성해. 만약 방의 개수가 roomNum보다 작다면, 같아질 때까지 생성하고, roomNum보다 크다면 가장 나중에 생성된 방부터 삭제해.
u 9. roomCount가 roomNum과 같아졌다면, 방을 생성하는 것을 멈춰. 이제 프리팹을 배치할 거야. 방 리스트에 있는 좌표가 1개라면, 그 좌표에 프리팹을 소환하고, 방 리스트에 있는 좌표가 2개라면, 2개의 중간 지점에 프리팹을 소환하고, 방 리스트에 있는 좌표가 3개라면, 서로 x값, z값이 각각 1, 1씩 차이나는 두 좌표의 중간 지점에 소환하고, 좌표가 4개라면, 서로 x값, z값이 각각 1, 1씩 차이나는 두 쌍의 좌표들 중 하나를 골라서 그 두 좌표의 중간 지점에 프리팹을 소환해. 프리팹을 배치할 땐 각각의 종류에 대한 변수에 들어간 프리팹들 중 하나를 무작위로 골라서 배치해. 배치되는 프리팹들은 Quaternion.Euler(90, 0, 0)의 회전값을 가져야 해.
u 10. 프리팹이 모두 배치되었으면, 서로 다른 리스트에 있으면서, x값이 같고 z값이 1 차이이거나, z값이 같고 x값이 1 차이인 두 InterDot을 전부 찾아내. 두 InterDot이 서로 같은 리스트에 있다면 그건 제외해야 해.
u 11. 찾은 InterDot들의 위치에 doorWay 프리팹을 배치할 건데, 만약 두 InterDot들의 z값이 같다면, Quaternion.Euler(90, 0, 90)의 회전값으로 소환하고, 두 InterDot들의 x값이 같다면, Quaternion.Euler(90, 0, 0)의 회전값으로 소환해. doorWay을 소환할 때 직렬화로 연결된 프리팹들 중 무작위로 하나를 골라서 소환해.
u 12. 소환했으면 이제 작업이 끝난 거야.
이걸 써서 주고 끝날 줄 알았는데, 여기서 또 수정을 엄청나게 많이 했다. 당연히 오류가 ㅈㄴ 많았으니까.
겹치지 않게, 소환 위치 조정하게 여러 장치를 또 넣어야 했고,
무엇보다 이렇게 길게 써놓으니까 GPT도 일부만 만들어 주더라.
그래서 코드를 몇 번 GPT한테 담갔다 꺼냈다를 반복해야 했다.
그래서 나온 게 이거다.
좀 불만인 건 너무 밀집되어있다는 건데, 이거 해결하려고 하다가 코드가 날아갈 뻔해서 그냥 뒀다.
게이머 입장에서도 방이 너무 뻗어나가있으면 플레이하기 불편할 것 같기도 하고 해서 일단은 이대로 하기로 했다.
이 알고리즘 짜는데에만 7시간 정도 걸린 것 같다.
아무튼 이렇게 하고 나서 나머지는 비율을 맞추는 거였다.
이건 그렇게 어렵지 않았다. 스프라이트 따로 만들고, 그거에 맞춰서 좌표에 가로 따로 세로 따로 다른 값을 곱해주면 됐으니까.
결과물은 이렇다.
솔직히 마음에 안든다.
근데 이만큼 구현한것도 너무 힘들어 죽을거같아서 이정도로도 감지덕지로 생각해야 한다...
코딩의 기본을 배우고 AI를 쓰면 더 좋을 것 같기도 하다.
앞으로 함수 공부도 많이 해야겠다.
와 이게되네 ㅋㅋㅋ - dc App
GPT에게 질문잘하는것도 능력임
GPT로 김장했네
GPT가 저걸 처리해준다고..? 난 ㄹㅇ 한 두문장으로 질문하는데
통로가 너무 많은것만 해결하면 되겠네
이게 된다고??
ㅁㅊ 코딩 2주만에 랜덤맵 ㅋㅋㅋㅋㅋ - dc App