
Verse 퍼시스턴스가 도입되면서 포크리 '스피드웨이 경주 디자인하기' 템플릿 섬을 UEFN으로 통합해 업그레이드할 수 있게 되었습니다. 시네마틱 및 랜드스케이프 추가와 같은 업데이트부터 트리거에서 Verse로의 전환까지, 이 템플릿이 발전하는 과정을 여러분에게 알려드릴 수 있어 기쁘게 생각합니다. Verse 퍼시스턴스로 업그레이드된 스피드웨이 경주에 오신 것을 환영합니다!
맨 처음 '스피드웨이 경주 디자인하기' 템플릿은 몇 가지 유익한 기능을 바탕으로 새로 마련된 경주 트랙 에셋을 사용해 독특한 경주 경험을 제작할 수 있도록 하기 위해 만들어졌습니다.
최신 업데이트를 통해 전체적인 접근 방식으로 맵 전반에 걸쳐 많은 기능을 교체하고 업그레이드했습니다. 그럼 UEFN의 강력한 기능을 십분 활용한 몇 가지 업데이트에 대해 알아보겠습니다.
오리지널 프로젝트의 기존 낮/밤 주기는 더욱 발전된 포트나이트 배틀로얄 챕터 4 라이팅으로 대체되었습니다. 이러한 새로운 주기에서 루멘을 사용할 수 있게 되면서, 더 부드러운 섀도와 사실적인 글로벌 일루미네이션을 연출할 수 있습니다. 폭포와 같은 기능도 추가하여 트랙에 시각적인 재미 요소도 더했습니다.
Verse 퍼시스턴스는 게임 세션 간에 데이터 추적을 가능하게 하는 솔루션을 제공해 줍니다. 이 기능을 통해 플레이어가 지금까지 기록한 통계를 모니터링하고 로컬 순위표를 만들 수 있게 되었습니다.
이는 랩을 완료할 때 플레이어의 최고 랩타임을 업데이트하는 동시에, 플레이어가 완주할 때마다 얻은 포인트와 승리 를 기록하기 위한 것이었습니다.
랩타임 업데이트는 간단한 작업이었는데, 경주 관리 장치의 LapCompletedEvent가 플레이어 랩 완료 시점마다 그 시간을 탐지하기만 하면 되었습니다. 경주가 시작되면 플레이어별로 실행되는 타이머 장치가 시작됩니다. WaitForPlayerToFinishLap 함수는 플레이어의 랩 완료를 기다리고, 랩타임을 계산하고, 통계 테이블을 업데이트한 후, 다음 랩을 기록하기 위해 타이머를 리셋합니다.
이를 해결하기 위해 ArraySync 함수를 사용했습니다. ArraySync는 전달된 배열의 각 요소에서 비동기 함수를 호출하고 해당 함수 모두가 완료되기를 기다립니다. 플레이어의 배열과 WaitForPlayerToFinishRace 함수를 전달함으로써, 각 플레이어에서 함수를 호출하고 모두가 완주할 때까지 기다릴 수 있었습니다.
이러한 방식으로 플레이어가 완주했을 때 순위에 따라 포인트와 승리를 부여한 다음 통계 테이블을 업데이트할 수 있었습니다. ArraySync가 완료되면 모든 플레이어가 완주했으며 게임을 끝낼 수 있음을 알게 됩니다.
이미 각 플레이어에 대한 퍼시스턴스 통계가 있었기 때문에 통계를 가져와 게시판에 이를 표시할 수 있었습니다. 게임 전 대기 구역에서는, 각 플레이어를 위한 게시판과 플레이어 레퍼런스 장치를 추가해 플레이어의 통계와 해당 시점에 사용된 의상을 보여주었습니다. 하지만 기록에 따라 플레이어를 정렬하는 것은 어려운 작업이었습니다.
이를 위해서는 병합 정렬 알고리즘을 구현했습니다. 병합 정렬은 일반적인 분할 정복 알고리즘으로, 순환 방식으로 배열을 둘로 분할하고 각 하위 배열을 정렬한 다음 이를 다시 병합해 줍니다. 또한 알고리즘에 비교 함수를 전달하는 기능을 추가하고, 서로 다른 플레이어 통계 각각에 대한 비교 함수를 만들었습니다.
플레이어를 정렬해야 할 때마다 각 플레이어의 통계를 통계 테이블로부터 가져와 배열에 모두 추가한 다음 배열과 비교 함수 모두를 병합 정렬 알고리즘에 전달했습니다. 전달할 비교 함수를 선택할 수 있어, 통계가 무엇이든 그에 따라 정렬된 플레이어 배열을 얻을 수 있었습니다. 병합 정렬 알고리즘과 테스트 파일 모두 새로운 템플릿에 추가해 뒀으므로, 알고리즘을 직접 테스트하고 자신의 함수에 맞게 커스터마이징할 수 있습니다!
정렬 메커니즘이 정립되면서 순위표를 완성할 수 있었습니다. 게임의 첫 번째 라운드 동안 플레이어는 게임 전 대기 구역에서 플레이어 레퍼런스 및 게시판과 함께 생성됩니다. 플 레이어 레퍼런스를 각 플레이어가 지금까지 기록한 포인트별로 정렬하고, 각 플레이어의 통계를 플레이어 앞 게시판에 표시합니다. 이를 통해 플레이어는 경주에 앞서 경쟁 상대를 살펴보고, 승리하기 위해서 누구를 주의 깊게 살펴야 하는지 알 수 있습니다.
계속 플레이하여 순위표의 첫 번째 자리를 차지하세요!

이를 위해 CircuitInfo라는 이름의 두 번째 플레이어 Weak 맵 변수를 만들었습니다. 이 CircuitInfo 변수는 게임이 종료될 때 또는 플레이어가 떠날 때 리셋해야 하는 모든 정보를 담고 있습니다.
두 번째 Weak 맵 변수를 사용하여 어떤 정보를 리셋해야 하는지(서킷 정보), 어떤 정보가 영원히 유지되어야 하는지(플레이어 통계)를 명확히 했습니다.
각 플레이어에 대해 CircuitInfo 플레이어 Weak 맵 변수에는 다음 정보를 기록합니다.
Verse 장치의 OnBegin 함수(매 라운드 시작 시 실행됨)에서는 모든 활성 플레이어에 대해 반복작업을 통해 퍼시스턴스 데이터에서 마지막 완주 라운드 중 가장 높은 값을 가져오는 방식으로 현재 어떤 라운드인지를 파악합니다. 라운드당 한 번만 이 작업을 수행하고 라운드 정보를 세션 Weak 맵 변수에 기록합니다. 그러면 프로젝트의 모든 Verse 코드가 다시 계산하지 않아도 언제든지 이 값에 액세스할 수 있습니다.
플레이어가 완주하면 경주 관리 장치의 RaceCompletedEvent가 대기 상태에서 이를 감지하고, 해당 플레이어에 연결된 CircuitInfo Weak 맵 변수에 플레이어의 완주 순서와 현재 라운드를 기록합니다. 다음과 같은 두 가지 상황에서는 이 정보를 리셋합니다.
업데이트된 템플릿에서는 파동 트리거 장치를 시퀀서로 대체하여 오프닝 시네마틱을 완성했습니다. 시퀀서를 사용하여 다양한 카메라, 헤드업 디스플레이(HUD) 요소 및 활성 플레이어의 수에 따라 조정되는 다이내믹 라인업 뷰를 추가할 수 있었습니다.
파동 트리거 장치를 환경설정한 방식과 비슷하게, 중요한 순간에 장치를 시퀀스에 연동하여 다음 플레이어의 점수를 표시할 시점이나 인트로에서 전환할 시점을 결정했습니다.

디자인을 향상시키는 새로운 기능이 또 출시되면 이 템플릿을 빠르게 업데이트할 수 있길 기대하고 있습니다. 그 동안 UEFN의 템플릿 탭에서 이번 템플릿을 다운로드하여 컴포넌트를 탐색하고 해당 기능을 프로젝트에 통합해 보세요. 경주, 순위표 등이 제대로 구현된 여러분의 섬을 빨리 만나보고 싶습니다!
맨 처음 '스피드웨이 경주 디자인하기' 템플릿은 몇 가지 유익한 기능을 바탕으로 새로 마련된 경주 트랙 에셋을 사용해 독특한 경주 경험을 제작할 수 있도록 하기 위해 만들어졌습니다.
최신 업데이트를 통해 전체적인 접근 방식으로 맵 전반에 걸쳐 많은 기능을 교체하고 업그레이드했습니다. 그럼 UEFN의 강력한 기능을 십분 활용한 몇 가지 업데이트에 대해 알아보겠습니다.
비주얼 디자인
UEFN의 랜드스케이프 모드를 통해 크리에이터는 경주의 기본으로 돌아가 새로운 랜드스케이프 편집 툴로 오프로드 트랙을 만들 수 있게 해줍니다. 초기 섬의 배경에 있던 암석 에셋으로 이루어진 산은 보다 자연스러운 랜드스케이프 디자인으로 대체되었습니다.오리지널 프로젝트의 기존 낮/밤 주기는 더욱 발전된 포트나이트 배틀로얄 챕터 4 라이팅으로 대체되었습니다. 이러한 새로운 주기에서 루멘을 사용할 수 있게 되면서, 더 부드러운 섀도와 사실적인 글로벌 일루미네이션을 연출할 수 있습니다. 폭포와 같은 기능도 추가하여 트랙에 시각적인 재미 요소도 더했습니다.
Verse 퍼시스턴스: 개선된 순위표
오리지널 맵에서 플레이어 레퍼런스 장치를 사용하는 타워는 1위 플레이어와 플레이어가 득점한 점수를 표시했습니다. 하지만 세션 간에 데이터는 유지되지 않았습니다.Verse 퍼시스턴스는 게임 세션 간에 데이터 추적을 가능하게 하는 솔루션을 제공해 줍니다. 이 기능을 통해 플레이어가 지금까지 기록한 통계를 모니터링하고 로컬 순위표를 만들 수 있게 되었습니다.
플레이어 통계 추적하기
에픽에서는 플레이어가 지금까지 기록한 승리, 최고 랩타임, 완주별로 획득한 포인트를 추적하는 퍼시스턴스 플레이어 통계 테이블 클래스를 개발했습니다. 또한 PlayerStatsMap이라는 퍼시스턴스 Weak 맵을 사용하여 플레이어를 플레이어 통계 테이블에 매핑하기도 했는데, 이를 통해 해당 통계를 라운드와 세션 간에 유지할 수 있습니다. 이후 플레이어별로 이러한 통계의 초기화, 가져오기 및 업데이트를 처리해 주는 통계 매니저 클래스를 만들었습니다.이는 랩을 완료할 때 플레이어의 최고 랩타임을 업데이트하는 동시에, 플레이어가 완주할 때마다 얻은 포인트와 승리 를 기록하기 위한 것이었습니다.
랩타임 업데이트는 간단한 작업이었는데, 경주 관리 장치의 LapCompletedEvent가 플레이어 랩 완료 시점마다 그 시간을 탐지하기만 하면 되었습니다. 경주가 시작되면 플레이어별로 실행되는 타이머 장치가 시작됩니다. WaitForPlayerToFinishLap 함수는 플레이어의 랩 완료를 기다리고, 랩타임을 계산하고, 통계 테이블을 업데이트한 후, 다음 랩을 기록하기 위해 타이머를 리셋합니다.
승리 및 포인트 기록하기
승리 및 포인트를 기록하는 것은 더 복잡한 작업이었습니다. 유사한 'WaitForPlayerToFinishRace' 함수를 사용하여 경주 관리 장치의 'RaceCompletedEvent'를 기다려 플레이어의 완주 시점을 파악할 수 있다는 것은 알고 있었습니다. 하지만 모든 플레이어가 완주할 때만 게임이 종료되기를 원했고, 경주 관리 장치에는 이를 추적하거나 해당 이벤트가 발생했을 때 게임을 종료할 방법이 없었습니다.이를 해결하기 위해 ArraySync 함수를 사용했습니다. ArraySync는 전달된 배열의 각 요소에서 비동기 함수를 호출하고 해당 함수 모두가 완료되기를 기다립니다. 플레이어의 배열과 WaitForPlayerToFinishRace 함수를 전달함으로써, 각 플레이어에서 함수를 호출하고 모두가 완주할 때까지 기다릴 수 있었습니다.
이러한 방식으로 플레이어가 완주했을 때 순위에 따라 포인트와 승리를 부여한 다음 통계 테이블을 업데이트할 수 있었습니다. ArraySync가 완료되면 모든 플레이어가 완주했으며 게임을 끝낼 수 있음을 알게 됩니다.
결과 표시하기
통계를 기록하는 것도 쉽지 않았지만, 해당 통계를 플레이어에게 표시할 방법 역시 필요했습니다. 레벨 내에서 확인 가능한 순위표를 만들고, 지금까지 기록한 포인트에 따라 플레이어를 정렬하여 상위 플레이어를 하이라이트하고자 했습니다.이미 각 플레이어에 대한 퍼시스턴스 통계가 있었기 때문에 통계를 가져와 게시판에 이를 표시할 수 있었습니다. 게임 전 대기 구역에서는, 각 플레이어를 위한 게시판과 플레이어 레퍼런스 장치를 추가해 플레이어의 통계와 해당 시점에 사용된 의상을 보여주었습니다. 하지만 기록에 따라 플레이어를 정렬하는 것은 어려운 작업이었습니다.
이를 위해서는 병합 정렬 알고리즘을 구현했습니다. 병합 정렬은 일반적인 분할 정복 알고리즘으로, 순환 방식으로 배열을 둘로 분할하고 각 하위 배열을 정렬한 다음 이를 다시 병합해 줍니다. 또한 알고리즘에 비교 함수를 전달하는 기능을 추가하고, 서로 다른 플레이어 통계 각각에 대한 비교 함수를 만들었습니다.
플레이어를 정렬해야 할 때마다 각 플레이어의 통계를 통계 테이블로부터 가져와 배열에 모두 추가한 다음 배열과 비교 함수 모두를 병합 정렬 알고리즘에 전달했습니다. 전달할 비교 함수를 선택할 수 있어, 통계가 무엇이든 그에 따라 정렬된 플레이어 배열을 얻을 수 있었습니다. 병합 정렬 알고리즘과 테스트 파일 모두 새로운 템플릿에 추가해 뒀으므로, 알고리즘을 직접 테스트하고 자신의 함수에 맞게 커스터마이징할 수 있습니다!
정렬 메커니즘이 정립되면서 순위표를 완성할 수 있었습니다. 게임의 첫 번째 라운드 동안 플레이어는 게임 전 대기 구역에서 플레이어 레퍼런스 및 게시판과 함께 생성됩니다. 플 레이어 레퍼런스를 각 플레이어가 지금까지 기록한 포인트별로 정렬하고, 각 플레이어의 통계를 플레이어 앞 게시판에 표시합니다. 이를 통해 플레이어는 경주에 앞서 경쟁 상대를 살펴보고, 승리하기 위해서 누구를 주의 깊게 살펴야 하는지 알 수 있습니다.
계속 플레이하여 순위표의 첫 번째 자리를 차지하세요!

출발선에서 레이서 순서
맵의 포크리 버전에서는 각 게임 시작 시 플레이어 순서를 랜덤으로 지정합니다. 이후 라운드에서는 순위표에서 더 높은 순위를 달성하도록 플레이어에게 동기를 부여하기 위해, 이전 라운드 완료 순위에 따라 출발선 순서를 설정했습니다.라운드 정보 추적하기
순위표에 사용한 퍼시스턴스 데이터와는 다르게, 레이서 순서와 서킷 정보는 모든 라운드 간에는 유지되어야 하지만 게임 세션 모두에서 유지되어야 하는 것은 아니었습니다. Verse의 세션 Weak 맵 변수는 라운드마다 데이터를 리셋하므로, 이 정보를 플레이어별로 저장하고 게임이 종료된 후 리셋해야 합니다.이를 위해 CircuitInfo라는 이름의 두 번째 플레이어 Weak 맵 변수를 만들었습니다. 이 CircuitInfo 변수는 게임이 종료될 때 또는 플레이어가 떠날 때 리셋해야 하는 모든 정보를 담고 있습니다.
두 번째 Weak 맵 변수를 사용하여 어떤 정보를 리셋해야 하는지(서킷 정보), 어떤 정보가 영원히 유지되어야 하는지(플레이어 통계)를 명확히 했습니다.
각 플레이어에 대해 CircuitInfo 플레이어 Weak 맵 변수에는 다음 정보를 기록합니다.
- 완주 순서
- 마지막 완 주 라운드
라운드별 로직
라운드별 로직(첫 번째 라운드가 아닌 경우 마지막 완주 순서로 플레이어 순서 결정 등)을 적용하기 위해서는 현재 어떤 라운드인지 알아야 하고, 언제 플레이어 데이터를 리셋해야 하는지를 알아야 합니다. 현재는 라운드를 가져올 수 있는 API가 없기 때문에, 이를 각 플레이어의 퍼시스턴스 데이터에 기록해야 합니다.Verse 장치의 OnBegin 함수(매 라운드 시작 시 실행됨)에서는 모든 활성 플레이어에 대해 반복작업을 통해 퍼시스턴스 데이터에서 마지막 완주 라운드 중 가장 높은 값을 가져오는 방식으로 현재 어떤 라운드인지를 파악합니다. 라운드당 한 번만 이 작업을 수행하고 라운드 정보를 세션 Weak 맵 변수에 기록합니다. 그러면 프로젝트의 모든 Verse 코드가 다시 계산하지 않아도 언제든지 이 값에 액세스할 수 있습니다.
플레이어가 완주하면 경주 관리 장치의 RaceCompletedEvent가 대기 상태에서 이를 감지하고, 해당 플레이어에 연결된 CircuitInfo Weak 맵 변수에 플레이어의 완주 순서와 현재 라운드를 기록합니다. 다음과 같은 두 가지 상황에서는 이 정보를 리셋합니다.
- 플레이어가 게임 도중 떠납니다. 플레이 공간의 PlayerRemovedEvent에 등록하여 플레이어가 떠나는 시점을 파악하고 ResetCircuitInfo 함수를 호출합니다.
- 각 라운드 시작 시 마지막 완주 라운드를 계산합니다. 최종 라운드인 경우 ResetCircuitInfo를 호출하여 플레이어의 데이터를 새로 고칩니다. Verse 장치에 게 임의 총 라운드 수로 설정된 편집 가능한 프로퍼티가 있어 라운드 수를 알 수 있습니다. 섬 크리에이터는 이 프로퍼티가 라운드 설정과 일치하는지 확인해야 합니다.
세션 Weak 맵을 사용해야 하는 경우와 플레이어 Weak 맵을 사용해야 하는 경우 비교
새로운 스피드웨이 경주는 코드에서 세션 Weak 맵 변수와 플레이어 Weak 맵 변수를 사용하는 경우의 차이점과 그 이유를 보여주는 좋은 예입니다.- 세션 Weak 맵 변수는 싱글톤과 매번 다시 계산하길 원치 않는 현재 라운드의 데이터를 저장하는 데 있어 유용합니다.
- 플레이어 Weak 맵 변수는 여러 라운드와 게임 세션 간에 유지되어야 하지만 개별 플레이어와 연결되어 있어야 하는 정보를 위해 마련되었습니다.
오프닝 시퀀스의 파동 트리거 장치
오리지널 버전 스피드웨이 경주 템플릿에서는 파동 트리거 장치를 활용하여 경주의 '제자리 - 준비 - 출발' 부분을 구성했습니다. 파동 트리거 장치는 트리거를 활성화하여 텍스트를 표시하고 출발선에서 라이트를 활성화하는 방식으로 정해진 시간 동안 일련의 이벤트를 재생했습니다.업데이트된 템플릿에서는 파동 트리거 장치를 시퀀서로 대체하여 오프닝 시네마틱을 완성했습니다. 시퀀서를 사용하여 다양한 카메라, 헤드업 디스플레이(HUD) 요소 및 활성 플레이어의 수에 따라 조정되는 다이내믹 라인업 뷰를 추가할 수 있었습니다.
파동 트리거 장치를 환경설정한 방식과 비슷하게, 중요한 순간에 장치를 시퀀스에 연동하여 다음 플레이어의 점수를 표시할 시점이나 인트로에서 전환할 시점을 결정했습니다.

앞으로의 계획
UEFN이 계속 발전함에 따라 이 템플릿도 진화해 가기를 바랍니다. 저희 목표는 UEFN으로 더 정교한 콘텐츠를 계속 만들 수 있도록 지원하고, 크리에이터가 재미있고 매력적인 섬을 제작할 수 있는 최신 기능을 활용하는 것입니다.디자인을 향상시키는 새로운 기능이 또 출시되면 이 템플릿을 빠르게 업데이트할 수 있길 기대하고 있습니다. 그 동안 UEFN의 템플릿 탭에서 이번 템플릿을 다운로드하여 컴포넌트를 탐색하고 해당 기능을 프로젝트에 통합해 보세요. 경주, 순위표 등이 제대로 구현된 여러분의 섬을 빨리 만나보고 싶습니다!