Unreal Engine 5 Dev Log ― 궁 미로 PalaceMaze 1
이 글은 궁 미로 PalaceMaze 시리즈의 2번째 기록입니다. (총 4개)
오늘 한 일
- 실험 랩에서 수집 게임을 만들어 본 뒤, 그걸 실제 게임으로 키우기로 했다. 궁을 돌아다니며 상자를 열어 마패조각을 모으고, 다 모으면 문에 끼워 나가는 구조다. 얼마나 빨리 해냈는지로 점수가 갈린다.
- 새 프로젝트를 블루프린트 전용으로 만들었다. 실험 랩은 C++ 템플릿인데, 게임 쪽은 이동과 입력을 직접 열어볼 수 있는 편이 낫다고 판단했다.
- 궁 격자를 생성했다. 마당 5×5, 한 칸 1600, 담장 높이 240에 두께 40, 문 폭 400이다. DFS 스패닝 트리로 모든 마당을 잇고, 남은 내벽 중 14%를 추가로 열어 우회로를 만들었다.
마당 격자를 만든다
각 마당 사이에 문을 낼지 결정 → 연결 그래프가 곧 미로
바깥 담장은 막고 북동쪽 한 곳만 출구로 연다
- 서양식 미로가 통로에 벽을 세우는 것과 달리 궁은 마당이 문으로 이어지는 구조라, 격자 배치가 그대로 미로 생성 알고리즘이 된다. 결과물도 미로처럼 생긴 것이 아니라 건축물처럼 보인다.
- 담장을 넘을 수 있는지 수치로 확인했다. 점프 상승 높이가 128이라 맨땅에서는 240 담장을 못 넘는다. 오브제를 딛으면 넘는다.
- 상자를 만들어 마당마다 하나씩 25개 놓았다. 보상은 마패조각 5, 시간 증가 4, 시간 감소 4, 생명 증가 3, 생명 감소 4, 이동속도 5로 구성을 고정하고 섞는다.
- 레벨매니저를 만들어 배분과 보상 적용을 전담시켰다. 상자는 로직이 없고 그냥 서 있기만 한다.
- E 키로 열도록 했다. 겹친 상자를 받아 인덱스를 찾고, 그 인덱스의 보상을 적용한다. 열린 상자는 파괴하지 않고 눌러서 표시한다.
막힌 부분
- 템플릿의
Playground폴더를 통째로 지웠더니 바닥까지 사라졌다.- 원인: 바닥이 그 폴더 안에 있었다. 폴더째 지우면 안에 든 54개가 한 번에 사라진다.
- 해결: 새로 깔았다. 폴더 단위로 지우기 전에 내용물을 본다.
switch가 실행 흐름을 종료해서 그 뒤에 쓴 문장이 도달 불가로 거부됐다.- 해결: 상자 파괴를 switch 앞으로 옮기고 switch를 마지막에 뒀다.
블루프린트 간 변수 접근이 안 된다
가장 크게 설계를 바꾼 제약이다. 처음에는 상자가 게임모드의 점수 변수를 올리는 평범한 구조를 생각했는데, 그 노드를 만들 수가 없었다. 네 가지 경로를 시험했다.
일반 검색 Class|BP_* 형태의 노드가 아예 안 나옴
컨텍스트 핀 검색 Variables|Default|GetTotal 이 보임 (존재는 함)
DSL로 그 이름 사용 does not exist 로 거부
declaring_class 지정 역시 거부
컨텍스트 검색에서는 보이는데 만들 수는 없다. 네이티브 함수는 대상 핀을 받아 다른 액터에도 쓸 수 있지만(SetActorScale3D, DestroyActor 등), 변수는 자기 것만 된다. 태그도 변수라 SetTags에 대상 핀이 없다.
- 해결: 상태를 한 곳에 모았다. 레벨매니저가 배열을 들고 있고, 상자는 아무것도 모른다. 매니저가 필요한 것을 네이티브 함수로만 상자에 작용한다.
- 남은 것: 이 제약은 도구 쪽 한계다. 에디터에서 손으로 하면 되는 일이라, 복잡한 상호작용이 필요해지면 그 부분만 직접 만드는 게 빠를 수 있다.
액터 배열 변수를 만들 수 없다
매니저가 상자 목록을 들고 있으려 했는데 타입이 거부됐다.
지원: bool, int, float, byte, name, string, text,
Vector, Rotator, Transform, Vector2D, LinearColor
처음에는 상자 위치를 벡터 배열로 저장해 우회했다. 그런데 그러면 인덱스를 찾은 뒤 그 위치의 액터를 다시 찾아야 해서 순회가 한 번 더 붙는다.
- 해결: 상자를 파괴하지 않기로 하면서 배열 크기가 고정됐다. 그러니
GetAllActorsOfClass가 돌려주는 순서를 그대로 인덱스로 쓸 수 있고, 위치를 저장할 필요가 없어졌다. 되찾는 순회도 사라졌다.
그래프 DSL이 키 입력 이벤트를 쓰지 못한다
E 키로 열게 하려는데 DSL이 거부했다. 읽을 때는 (event E (Key))로 잘 나오는데, 쓰려고 하면 AddEvent|E does not exist가 된다. 작성기가 이벤트를 AddEvent| 접두사로만 찾기 때문이다.
- 해결: 키 노드는 노드 API로 만들고, 로직은 커스텀 이벤트에 DSL로 쓴 뒤, 키 노드의 Pressed 핀을 그 이벤트 호출에 연결했다. 연결 하나만 수작업이다.
- 덤: 같은 방식으로 오버랩 델리게이트 바인딩도 되는 것을 확인했다.
Assign노드를 만들면 커스텀 이벤트가 자동으로 생성되고 델리게이트까지 물린다.
정리해 둔 노드 배치를 초기화시켰다
그래프를 DSL로 다시 쓰면 노드가 새로 만들어져서 위치가 초기화된다. 손으로 정리해 둔 배치가 날아갔다. 게다가 한 이벤트만 고치면 되는 상황에서도 그래프 전체를 다시 쓰고 있었다.
- 해결: 바꾸는 이벤트만 쓴다. 작은 수정은 DSL 대신 노드 API로 한다. 다시 써야 할 때는 배치가 초기화된다는 것을 먼저 짚고 넘어간다.
- 남은 한계: 위치를 저장했다가 복원하는 방법이 없다. 다시 쓰면 노드가 새로 생성돼 이전 노드와 짝지을 수 없다. 예방이 유일한 수단이다.
숫자 하나마다 노드가 생긴다
DSL에 숫자를 쓰면 MakeLiteralInt 노드를 만들어 연결한다. 핀에 직접 넣지 않는다. 그래프에 7개가 쌓여 있었다.
실제 노드 제목이 Make Literal Int가 아니라서, 그 이름으로 검색하면 0개가 나온다.
- 해결: 각 노드의 값을 읽어 연결된 대상 핀에 직접 넣고 노드를 지웠다. 73개에서 66개로 줄었다.
- 배운 것: 없다고 단정하기 전에 전체를 훑어 실제 이름을 확인한다.
다음에 할 일
- 마패조각 퍼즐. 조각을 다 모으면 방향을 돌려 끼우는 미니게임이 필요하다. UMG라 직접 만든다.
- 출구 트리거. 조각을 다 끼웠을 때만 열리게 한다.
- 타이머와 HUD. 경과 시간, 남은 생명, 모은 조각 수를 띄운다.
- 열린 상자 비주얼. 지금은 스케일로 눌러 표시하는데 열린 상자 메시로 바꾼다.
- 마당별 오브제 배치. 전각과 석물, 장독대를 놓되 높이 규칙을 지킨다.
- 기와 마감. 담장 위 이동이 문제가 되면 경사진 기와를 올려 막는다.
- 상자가 시작 지점과 겹치지 않도록 배치할 때 시작 주변을 비운다.