목표 마커는 많은 게임에서 플레이어에게 다음 목표나 중요 지점을 안내하기 위해 사용됩니다. 이 튜토리얼에서는 지도 표시 장치와 Verse를 사용하여 재사용 가능한 목표 마커를 만드는 방법을 알아봅니다.
사용된 Verse 언어 기능
익스텐션 메서드: 기존 클래스나 타입의 멤버처럼 작동하지만 새 타입이나 서브클래스를 생성하지 않아도 되는 특수한 타입의 함수입니다. 이 가이드에서는 구조체의 익스텐션 메서드를 만듭니다.
사용된 Verse API
creative_prop API:
creative_propAPI는 사물을 움직이기 위한 메서드를 제공합니다.편집 가능 프로퍼티: 빠른 테스트를 위해 장치를 참조하고 변수 값을 업데이트하는 데 몇 가지 프로퍼티가 사용됩니다.
인스트럭션
다음 단계에 따라 다수의 목표 또는 중요 지점으로 이동할 수 있는 단일 목표 마커 장치를 구성하는 방법을 알아보세요. 완성된 스크립트는 이 가이드 끝에 참고용으로 포함되어 있습니다.
레벨 구성하기
이 예시에서는 다음과 같은 사물과 장치를 사용합니다.
건물 사물(1): 지도 표시 장치를 옮기는 데 사용될 사물입니다.
지도 표시 장치(1): 미니맵과 개요 맵에 커스텀 마커를 표시하는 장치입니다.
플레이어 생성 패드 장치(1): 플레이어가 사물 근처에 생성되도록 사물과 가까운 곳에 추가합니다.
사물 API 사용하기
Verse로 장치를 움직이게 하는 첫 단계는 사물 API로 사물을 움직이는 것입니다. 다음 단계에 따라 레벨에서 사물을 움직입니다.
objective_coordinator_device라는 이름의 새 Verse 장치를 생성합니다.
Verse 파일 상단의 기본
using표현식 아래에서 SpatialMath 모듈에 대한using표현식을 추가합니다. 이 모듈에는 사물의 이동을 위해 참고할 코드가 포함되어 있습니다.Verseusing { /UnrealEngine.com/Temporary/SpatialMath }두 개의 편집 가능 프로퍼티를 추가합니다.
움직이는 사물에 대한 레퍼런스를 저장할
creative_prop상수RootProp사물이 움직일 위치를 저장하는
transform상수DestinationVerseobjective_coordinator_device<public> := class<concrete>(creative_device): @editable RootProp<public> : creative_prop = creative_prop{} @editable Destination<public> : transform = transform{}
이 코드를 실행하고 objective_coordinator_device를 레벨로 드래그하면 디테일(Details) 패널에 두 프로퍼티가 표시됩니다.
사물을 실제로 움직이는 메서드는
TeleportTo[]입니다. 이 메서드를 if 표현식에서 호출하고 소괄호 대신 대괄호를 사용합니다.TeleportTo[]는 실패 가능 표현식이기 때문입니다.if는 실패 컨텍스트를 만듭니다.Verseif(RootProp.TeleportTo[Destination.Translation, Destination.Rotation]): Print("Prop move successful") else: Print("Prop move failed")TeleportTo[]의 실행인자는 이동(Translation)과 회전(Rotation)입니다. 둘 다 대상(Destination) 프로퍼티에서 가져옵니다.에디터로 돌아가서 콘텐츠 브라우저(Content Browser)의 Fortnite > 갤러리(Galleries) > 사물(Props) 폴더에서 사물을 드래그합니다. 이 가이드에서는 Coastal Buoy 02B를 사용했지만, 사물 폴더에 있는 어떤 에셋이든 괜찮습니다.
아웃라이너(Outliner)에서 목표 조정기 장치(objective coordinator device)를 선택합니다. 디테일 패널에서 사물에 대한 RootProp을 설정합니다. 이 예시에서는 RootProp을 Coastal Buoy 02B로 설정했습니다.
디테일 패널에서 대상을 펼칩니다. 대상은
transform타입이며, 스케일(Scale), 회전, 이동으로 구성됩니다. 사물을 움직이려면 이동을 변경해야 하므로 이 부분을 펼칩니다. X로 끝나는 필드를 5000.0으로 설정합니다.코드를 테스트할 때는 결과가 눈에 잘 띄도록 값을 크게 변경하는 것이 좋습니다. 작게 변경하면 코드가 의도한 대로 작동하는지 확인하기 어려울 수 있습니다.
Verseusing { /Verse.org/Simulation } using { /Fortnite.com/Devices } using { /UnrealEngine.com/Temporary/SpatialMath } objective_coordinator_device<public> := class<concrete>(creative_device): @editable RootProp<public> : creative_prop = creative_prop{} # Where the marker will be moved toVerse > Verse 코드 빌드(Build Verse Code) > 세션 시작(Launch Session)을 클릭합니다. 마지막으로 게임 시작(Start Game)을 클릭합니다. 사물이 움직이는 것을 볼 수 있습니다.
부모와 구조체
이제 레벨에서 사물이 움직이지만, 진정한 목표는 플레이어가 웨이포인트로 사용할 수 있도록 지도 표시 장치를 움직이는 것입니다. 다음 단계에 따라 건물 사물과 지도 표시 장치를 레벨에 추가하고 장치를 건물 사물에 연결합니다.
콘텐츠 브라우저 내부에서 우클릭하고 컨텍스트 메뉴를 엽니다.
컨텍스트 메뉴에서 블루프린트 클래스(Blueprint Class)를 선택합니다.
부모 클래스 선택(Pick Parent Class) 창에서 건물 사물(Building Prop)을 클릭합니다.
새로운 블루프린트 클래스가 콘텐츠 브라우저에 나타납니다. 이름을 BuildingProp으로 지정합니다.
건물 사물을 레벨로 드래그합니다. 이 사물에는 메시가 없으므로 트랜스폼 기즈모만 보입니다.
아웃라이너에서 지도 표시 장치를 건물 사물로 드래그합니다. 이렇게 하면 건물 사물이 지도 표시 장치의 부모가 됩니다. 이제 건물 사물이 움직일 때 지도 표시 장치도 함께 움직입니다.
Verse를 사용하여 장치를 생성하는 방법은 이미 아시겠지만, 자체적인 장치가 없는 Verse 파일도 생성할 수 있습니다.
새 Verse 파일을 생성하고 objective_marker로 명명합니다. 이 파일은 장치를 생성하지 않습니다. 그 대신 앞서 생성한 Verse 장치에 노출될
struct의 정의를 포함합니다.우선 objective_marker라는 이름의
struct를 선언합니다. 두 개의 멤버RootProp및MapIndicator를 보유하게 됩니다. 두 멤버 모두@editable지정자를 가져야 합니다.Verseobjective_marker<public> := struct<concrete>: @editable RootProp<public> : creative_prop = creative_prop{} @editable MapIndicator<public> : map_indicator_device = map_indicator_device{}
익스텐션 메서드 및 명명된 실행인자
RootProp 멤버 및 연결된 지도 표시 장치를 움직일 단일 메서드 MoveMarker를 선언합니다. 이 메서드는 두 가지 언어 기능, 즉 익스텐션 메서드와 명명된 실행인자를 도입합니다.
(Marker : objective_marker).MoveMarker<public>(Transform : transform, ?OverTime : float)<suspends> : void =익스텐션 메서드:
MoveMarker()메서드를objective_marker구조체에 추가합니다. 익스텐션 메서드는 식별자를 둘러싸는 괄호와 콜론으로 구분되는 타입을 통해 선언됩니다. 이 경우에는(Marker : objective_marker)입니다.명명된 실행인자: 두 번째 실행인자
?OverTime은MoveMarker함수 호출에서 명명되어야 함을 나타내기 위해?를 사용합니다. 이렇게 하면MoveMarker호출을 읽거나 쓰는 개발자가float실행인자의 역할을 이해하는 데 도움이 됩니다.
MoveMarker()는 사물 API로부터 다음 두 메서드 중 하나를 호출합니다. 앞서 사용한 TeleportTo[] 또는 MoveTo()입니다. if..else 블록을 생성하여 파라미터 OverTime이 0.0보다 큰지 테스트합니다. 큰 경우 MoveTo()를 호출합니다. 이렇게 하면 목표가 다음 위치로 순간이동하지 않고 지정한 시간에 걸쳐 이동합니다.
(Marker : objective_marker).MoveMarker<public>(Transform : transform, ?OverTime : float)<suspends> : void =
if (OverTime > 0.0):
Marker.RootProp.MoveTo(Transform.Translation, Transform.Rotation, OverTime)
else:
if:
Marker.RootProp.TeleportTo[Transform.Translation, Transform.Rotation]지금 코드를 컴파일하면 성공하겠지만, 새 장치가 콘텐츠 브라우저의 CreativeDevices 폴더에 보이지 않을 것입니다. objective_marker는 creative_device에서 상속하는 클래스가 아니라 struct이기 때문입니다.
목표 조정기 장치 업데이트하기
이제 참조할 새 타입을 얻었으니 이를 참조하도록 objective_coordinator_device를 업데이트해야 합니다.
RootProp프로퍼티를 삭제하고 이름이PickupMarker, 타입이objective_marker인 프로퍼티로 대체합니다. 앞에서 생성한 타입입니다.MoveMarker()는float타입의 실행인자를 필요로 하므로, 이름이MoveTime인 편집 가능 프로퍼티로 float를 생성합니다.TeleportTo[]호출을 삭제합니다. 그 대신objective_marker를 위해 생성한MoveMarker()메서드를 호출합니다. 명명된 실행인자?OverTime이 필요합니다.Verseobjective_coordinator_device<public> := class<concrete>(creative_device): @editable PickupMarker<public> : objective_marker = objective_marker{} # Where the marker will be moved to @editable Destination<public> : transform = transform{} # How much time the marker should take to reach its new location
이 코드를 컴파일하고 목표 조정기 장치의 디테일을 확인합니다. PickupMarker와 MoveTime 프로퍼티가 보이고, PickupMarker에는 RootProp과 MapIndicator가 있을 것입니다.
RootProp 필드를 BuildingProp으로 설정하고, MapIndicator 필드를 지도 표시 장치(Map Indicator Device)로 설정합니다.
코드를 컴파일하고 세션 시작을 클릭합니다. 게임을 시작한 직후 미니맵에서 움직이는 마커를 볼 수 있습니다.
MoveTime을0.0등 다른 값으로 설정하고 시도해 봅니다. 다양한 시나리오에서 어떤 움직임이 가장 적합할지 생각해 보세요.
GetPlayers() 및 ActivateObjectivePulse()
플레이어가 다음 목표에 도달하도록 추가로 약간의 도움을 주는 방법이 있습니다. 이를 목표 파동(objective pulse)이라고 합니다. 목표 파동을 활성화하면 플레이어로부터 지도 표시 장치까지 이동하는 점선이 표시됩니다. 목표 조정기 장치에 목표 펄스를 추가하려면 다음 설명에 따릅니다.
목표 파동을 활성화하는 데 필요한 메서드는 ActivateObjectivePulse()이며 agent 타입의 실행인자 하나를 필요로 합니다. 우선 플레이어 캐릭터를 나타내는 agent의 인스턴스를 얻기 위한 메서드를 생성합니다.
FindPlayer()라는 함수를<private>으로 설정하여 선언하고 반환 값을void로 설정합니다.Self.GetPlayspace().GetPlayers()로 레벨에 있는 모든 플레이어의 배열을 구합니다. 이 배열을AllPlayers라는 변수에 저장합니다.VerseFindPlayer<private>() : void = AllPlayers := Self.GetPlayspace().GetPlayers()레벨 내 유일한 플레이어에 대한 레퍼런스를 구하기 위해 배열의 첫 번째 엘리먼트를 해당 변수에 할당합니다. 배열에 액세스하는 것은 실패 가능 표현식이므로
if표현식에 배치합니다.Verseif (FirstPlayer := AllPlayers[0]):player를 변수에 할당하는 것은 실패할 수 있으므로, 코드에서 플레이어를 참조할 때는 옵션 타입 변수를 사용하는 것이 좋습니다.?player로 옵션 타입의 플레이어 변수를 선언합니다. 이 변수는 멤버 변수와 함께 사용되어야 합니다.Verseobjective_coordinator_device<public> := class<concrete>(creative_device): var PlayerOpt<private> : ?player = false @editable PickupMarker<public> : objective_marker = objective_marker{} # Where the marker will be moved to @editable Destination<public> : transform = transform{}새 변수를 설정하고 플레이어를 찾지 못하면 알려줄
Print()표현식과 함께else블록을 생성합니다. 이제FindPlayer()함수가 완성되었습니다.VerseFindPlayer<private>() : void = # Since this is a single player experience, the first player [0] # should be the only one available. AllPlayers := Self.GetPlayspace().GetPlayers() if (FirstPlayer := AllPlayers[0]): set PlayerOpt = option{FirstPlayer} Print("Player found")
OnBegin() 함수로 돌아가서 두 가지를 더 변경해야 합니다.
FindPlayer()함수를 호출합니다.VerseOnBegin<override>()<suspends> : void = FindPlayer()MoveMarker()를 호출한 뒤에if표현식을 하나 더 사용하여 옵션 타입 플레이어 변수를 새 변수로 설정하고, 이를PickupMarker.MapIndicator.ActivateObjectivePulse()에 실행인자로 전달합니다.Verseif (FoundPlayer := PlayerOpt?): PickupMarker.MapIndicator.ActivateObjectivePulse(FoundPlayer)
지금 코드를 실행하면 목표 펄스가 캐릭터를 레벨 내 목표 마커 위치로 안내하는 것을 볼 수 있습니다.
완성된 스크립트
Objective_marker.verse
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Fortnite.com/Devices/CreativeAnimation }
objective_marker<public> := struct<concrete>:
# The prop that will be moved
@editable
RootProp<public> : creative_prop = creative_prop{}
Objective_coordinator_device.verse
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
using { /Fortnite.com/Playspaces }
using { /UnrealEngine.com/Temporary/SpatialMath }
objective_coordinator_device<public> := class<concrete>(creative_device):
var PlayerOpt<private> : ?player = false
@editable
직접 해보기
여기서 작성한 이동 코드는 모든 사물에 사용할 수 있습니다. 이동 가능한 사물을 장치의 부모로 만들면, 장치가 사물과 함께 움직일 것입니다. 다른 사물과 장치도 움직여 보고, 이를 활용할 수 있는 다른 게임에 대해서도 생각해 보세요.
다음 단계
이 가이드를 활용해서 픽업/배달 게임을 만들고 있다면, 다음 단계는 카운트다운 타이머 기능을 만드는 방법을 배우는 것입니다.