씬 그래프는 엔티티와 컴포넌트 간의 다양한 상호작용을 통해 유연하고 동적인 게임플레이를 다양하게 만들 수 있게 해 줍니다. 이 템플릿에서 엔티티를 생성하고 제거하여 런타임 시 엔티티를 조작할 수 있는 몇 가지 방법, 컴포넌트 이벤트 및 쿼리를 사용하여 엔티티 간에 통신하는 방법, 재사용 가능한 게임플레이 요소를 만드는 방법을 보여줍니다.
프리팹 생성 및 제거하기
두 번째 홀에서 상호작용 가능한 패드를 확인해 보세요. 플레이어가 패드를 밟으면 프리팹 랜턴이 씬에 추가되고, 플레이어가 패드에서 내려가면 제거됩니다.
SpawnPrefabDevice.verse를 열어 이 코드가 작동하는 방식을 확인해 보세요. 이는 플레이어가 트리거에 들어가는 시점을 알기 위해 볼륨 장치를 사용하는 포크리 장치인데, 플레이어가 들어가면 프리팹을 생성하고 플레이어가 트리거에서 내려가면 제거합니다. SpawnPrefabDevice 클래스 정의의 상단에는 편집 가능한 TriggerVolume이 레벨의 트리거 볼륨을 참조합니다.
SpawnPrefabDevice := class(creative_device):
@editable
TriggerVolume:volume_device = volume_device{}게임이 시작될 때 이 장치가 생성할 새 가로등 프리팹과 생성되는 위치를 정의합니다.
OnBegin<override>()<suspends>:void =
PrefabToSpawn:entity = LightPost.P_LightPost{}
SpawnTransform:transform = transform:
Translation := vector3:
Left := -9084.0
Up := -8.0
Forward := -919.0그런 다음 시뮬레이션 엔티티를 가져와 작동시킬 일련의 실패 컨텍스트를 생성합니다. 먼저 loop를 사용하여 내부의 코드를 반복적으로 실행하고, 이어서 두 block 명령문 사이에서 race가 실행됩니다.
if:
SimulationEntity := GetSimulationEntity[]
then:
loop:
race:
block:
TriggerVolume.AgentEntersEvent.Await()
SimulationEntity.AddEntities(array{ PrefabToSpawn })
PrefabToSpawn.SetGlobalTransform(SpawnTransform)
첫 번째 block 명령문은 에이전트가 볼륨 장치에 들어갈 때까지 대기합니다. 에이전트가 볼륨 장치에 들어가면 AddEntities()를 사용하여 랜턴 프리팹을 씬에 추가하고 SetGlobalTransform()을 사용하여 위치를 올바르게 지정합니다. 두 번째 블록은 에이전트가 볼륨에서 나갈 때까지 대기하고, 부모(여기서는 시뮬레이션 엔티티)로부터 프리팹을 제거합니다.
이 두 블록은 루프에서 경쟁 상태이므로 둘 중 하나가 항상 실행되며, 플레이어가 볼륨에 들어가거나 나오면 가로등이 몇 번이나 재생성됩니다!
전체 스크립트
아래는 씬에 엔티티를 추가 및 제거하는 완성된 스크립트입니다.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/SceneGraph }
using { /Verse.org/SpatialMath }
using { /UnrealEngine.com/Temporary/Diagnostics }
SpawnPrefabDevice := class(creative_device):
엔티티 오버랩
두 번째 홀 역시 엔티티가 상호작용할 수 있는 다양한 방법 중 일부를 보여주는 일련의 예시가 있습니다. 예를 들어 메시 컴포넌트가 있는 엔티티는 다른 엔티티와 오버랩될 때 메시 컴포넌트의 EntityEnteredEvent 및 EntityExitedEvent를 통해 알림을 받을 수 있습니다. 이 기능이 어떻게 작동하는지 보려면 두 번째 홀의 UFO 예시를 확인하세요.
게임이 시작되면 소가 UFO에 의해 납치되는 것처럼 소가 들어 올려지는 애니메이션이 재생됩니다. 소는 메시 컴포넌트의 EntityEnteredEvent가 사용하므로 UFO와 언제 오버랩되는지를 알 수 있으며, 오버랩될 때 마치 납치된 것처럼 자신을 보이지 않게 설정합니다.
이 예시의 코드는 EntityEnteredExampleComponent.verse에 정의되어 있습니다. 이 파일을 Verse 익스플로러(Verse Explorer)에서 여세요.
entity_entered_example_component 클래스는 소 납치 애니메이션을 빌드할 편집 가능한 Keyframes 배열을 정의하는 것부터 시작합니다. 또한 StartTransform 변수를 정의하여 씬에서 소의 시작 위치를 지정합니다.
entity_entered_example_component<public> := class<final_super>(component):
@editable
Keyframes:[]keyframed_movement_delta = array{}
var StartTransform:transform = transform{}OnBeginSimulation()에서는 스크립트가 fort_round_manager를 가져오는 것부터 시작합니다. 이는 포트나이트 라운드 관리 장치의 인터페이스로, 라운드의 시작과 끝에 이벤트를 등록하는 데 사용할 수 있습니다. 이 예시의 스크립트에서는 SubscribeRoundStarted()를 사용하여 OnRoundStarted() 함수를 라운드의 시작에 등록합니다. 따라서 엔티티가 시뮬레이션을 시작할 때가 아닌 게임이 시작될 때만 함수가 실행됩니다.
OnBeginSimulation<override>():void =
(super:)OnBeginSimulation()
if:
FortRoundManager := Entity.GetFortRoundManager[]
then:
FortRoundManager.SubscribeRoundStarted(OnRoundStarted)그런 다음 OnRoundStarted()에서는 스크립트가 StartTransform을 엔티티의 글로벌 트랜스폼으로 설정하는 것부터 시작합니다. 그러고 나서 메시 컴포넌트의 EntityEnteredEvent를 새 함수 OnEntityEntered()에 등록하는데, 이 함수는 다른 메시가 엔티티와 오버랩될 때마다 트리거됩니다. 이어서 소를 들어 올리기 시작하는 PlayCowAbductionAnimation() 함수를 생성합니다.
OnRoundStarted():void =
set StartTransform = Entity.GetGlobalTransform()
if:
Mesh := Entity.GetComponent[mesh_component]
then:
Mesh.EntityEnteredEvent.Subscribe(OnEntityEntered)
spawn { PlayCowAbdcutionAnimation() }PlayCowAbductionAnimation() 함수는 단순히 소의 글로벌 트랜스폼을 시작 트랜스폼으로 설정하고 짧은 시간 동안 대기합니다. 그런 다음 엔티티에서 메시 및 키프레임 이동 컴포넌트를 가져오고, 소가 보이도록 메시를 활성화한 후 키프레임이 지정된 이동 컴포넌트에서 애니메이션을 설정하고 재생합니다.
PlayCowAbdcutionAnimation()<suspends>:void =
Entity.SetGlobalTransform(StartTransform)
Sleep(2.0)
if:
Mesh := Entity.GetComponent[mesh_component]
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
set Mesh.Visible = true
MovementComponent.SetKeyframes(Keyframes, oneshot_keyframed_movement_playback_mode{})
MovementComponent.Play()마지막으로, OnEntityEntered() 함수를 사용하여 소가 다른 엔티티(여기서는 UFO)와 오버랩될 때마다 코드를 실행합니다. 코드가 실행되면 다시 메시 및 키프레임이 지정된 이동 컴포넌트를 가져오지만, 이번에는 이를 사용하여 재생 중인 애니메이션을 정지하고 납치된 것처럼 소가 보이지 않게 설정합니다. 그러고 나면 PlayCowAbductionAnimation()의 새 인스턴스를 생성하여 프로세스를 반복합니다.
OnEntityEntered(OtherEntity:entity):void =
if:
Mesh := Entity.GetComponent[mesh_component]
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
MovementComponent.Stop()
set Mesh.Visible = false
spawn { PlayCowAbdcutionAnimation() }전체 스크립트
아래는 UFO 메시로 소 메시를 납치하는 완성된 스크립트입니다.
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Verse.org/SceneGraph/KeyframedMovement }
using { /Verse.org/SpatialMath }
using { /Fortnite.com/Game }
entity_entered_example_component<public> := class<final_super>(component):
오버랩 히트 쿼리하기
엔티티는 오버랩 쿼리를 사용하여 다른 엔티티를 쿼리할 수도 있습니다. 엔티티가 특정 메시에 들어가거나 나가는 경우를 탐지하는 대신, 오버랩 쿼리를 사용하여 특정 영역과 오버랩되는 모든 엔티티와 컴포넌트를 반환할 수 있습니다.
이 영역은 엔티티 그 자체가 될 수도 있고, 스피어나 박스 같이 주어진 콜리전 볼륨, 또는 엔티티를 시뮬레이션할 위치가 될 수도 있습니다. 쿼리하면 영역에서는 overlap_hit 목록을 반환합니다. 각 overlap_hit는 소스 볼륨과 오버랩되는 컴포넌트 또는 볼륨에 관한 정보를 제공하고, 이러한 컴포넌트를 쿼리하여 연결된 엔티티를 찾을 수 있습니다.
예를 들어 FindOverlapHits 예시에서 앞에 있는 상호작용 가능한 패드를 밟아보세요. 패드와 상호작용하면 UFO가 보이지 않는 콜리전 볼륨을 생성하고 이를 사용하여 오버랩 쿼리를 수행합니다. 콜리전 볼륨이 소와 오버랩되면 UFO가 소를 납치합니다!
이 예시의 코드는 FindOverlapHitsExampleComponent.verse에 정의되어 있습니다. 이 파일을 Verse 익스플로러에서 여세요.
이 클래스는 플레이어가 밟고 있는 상호작용 가능한 패드를 참조하기 위해 Trigger로 명명된 편집 가능한 볼륨 장치를 정의하는 것부터 시작하고, 소가 납치 중인지를 판단하는 logic 변수 IsAbducting도 정의합니다. 컴포넌트가 시뮬레이션을 시작하면 OnRoundStarted() 함수를 라운드 관리 장치의 라운드 시작에 등록합니다. 마찬가지로, OnRoundStarted() 함수는 Trigger의 AgentEntersEvent 및 AgentExitsEvent를 OnTriggerEntered() 및 OnTriggerExited() 함수에 각각 등록합니다.
find_overlaphits_example_component<public> := class<final_super>(component):
@editable
Trigger:volume_device = volume_device{}
var IsAbducting:logic = false
OnBeginSimulation<override>():void =
플레이어가 패드를 밟을 때 UFO가 현재 소를 납치하고 있는 상태가 아니면 OnTriggerEntered() 함수가 호출되어 keyframed_movement_component를 가져오고 Pause()를 호출하여 UFO가 수행하는 동작을 일시정지합니다. 그런 다음, EnableAbductionBeam() 및 PerformOverlapCheck()를 호출하여 납치 광선을 활성화하고 소가 UFO 아래에 있는지 확인합니다.
OnTriggerEntered(Agent:agent):void=
if:
not IsAbducting?
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
MovementComponent.Pause()
EnableAbductionBeam()
PerformOverlapCheck()오버랩 확인의 실제 로직은 PerformOverlapCheck() 함수에 저장되어 있습니다. 납치 광선을 시뮬레이션하기 위해 이 함수는 콜리전 캡슐을 생성하고 UFO 바로 아래로 설정되는 CollisionTransform을 정의합니다.
PerformOverlapCheck():void =
CollisionCapsule := collision_capsule{Radius := 36.0, Length := 328.0}
var CollisionTransform:transform = Entity.GetGlobalTransform()
set CollisionTransform.Translation.Up = CollisionTransform.Translation.Up - 248.0다음으로, for 표현식에서 함수가 FindOverlapHits()를 호출하여 컴포넌트 또는 볼륨을 찾아 반환합니다. 그리고 콜리전을 확인할 볼륨으로 CollisionCapsule을, 콜리전을 시뮬레이션할 위치로 CollisionTransform을 전달합니다. 그런 다음 각 오버랩에서 반복작업을 수행해 오버랩된 컴포넌트가 메시 컴포넌트, 특히 소 엔티티의 SM_Toy_Cow 메시였는지를 확인합니다. 해당 메시인 경우 소가 납치되도록 전달하는 AbductCow() 함수를 호출합니다.
# Perform the overlap check from the entity that contains the mesh_component
for:
Overlap : Entity.FindOverlapHits(CollisionTransform, CollisionCapsule)
# Cast to see if what was overlapped was the Cow
CowMesh := Meshes.SM_Toy_Cow[Overlap.TargetComponent]
CowPrefab := CowMesh.Entity
do:
spawn { AbductCow(CowPrefab) }소 납치를 시뮬레이션하기 위해 엔티티가 애니메이션을 빌드한 후 위 엔티티 오버랩 예시와 유사한 방식으로 소 엔티티에서 애니메이션을 재생합니다. 이 코드는 소가 아닌 UFO 엔티티에서 호출하므로, 코드는 소 엔티티에서 컴포넌트를 가져온 후 애니메이션을 전달하여 재생해야 합니다. 코드는 소의 메시 및 키프레임이 지정된 이동 컴포넌트를 가져온 후 IsAbducting을 true로 설정하는 것부터 시작합니다.
AbductCow(CowEntity:entity)<suspends>:void =
# Get the components on the Cow Prefab
if:
CowMesh := CowEntity.GetComponent[mesh_component]
MovementComponent := CowEntity.GetComponent[keyframed_movement_component]
then:
set IsAbducting = true소 납치 애니메이션에 사용되는 키프레임은 아웃라이너에 설정되어 있지 않으므로, UFO와 소 사이의 위치 차이에 기반해 코드가 이를 빌드해야 합니다. 코드는 소와 UFO 사이 위치 차이를 가져온 후 이러한 값으로부터 새 keyframed_movement_delta를 빌드하여 이 작업을 진행합니다. 그런 다음 빌드한 키프레임 하나를 키프레임이 지정된 이동 컴포넌트의 배열로 설정하고, 재생을 호출하여 소의 시작 위치와 UFO 사이에서 소에 애니메이션을 적용합니다.
# Get the delta between Cow and UFO
DeltaTransform:transform = transform:
Translation:= Entity.GetGlobalTransform().Translation - CowEntity.GetGlobalTransform().Translation
Scale := vector3{Left:= 0.0, Up:= 0.0, Forward:= 0.0}
# Create a key frame
Keyframe := keyframed_movement_delta:
Transform := DeltaTransform
Duration := 2.0
Easing := ease_in_cubic_bezier_easing_function{}
납치 시 UFO까지 소에 애니메이션이 적용될 수 있지만, 소가 UFO와 오버랩되면 코드는 소가 사라지도록 만들어야 합니다. 이를 위해 코드는 소의 메시 컴포넌트의 EntityEnteredEvent를 대기한 후 RemoveFromParent()를 호출하여 씬에서 소 엔티티를 제거합니다. 소가 사라졌으면 UFO가 다시 순찰을 시작할 수 있습니다. 코드는 UFO의 키프레임이 지정된 이동 컴포넌트에서 재생을 호출하여 UFO가 움직이게 만듭니다.
# Wait for Entity Entered Event
CowMesh.EntityEnteredEvent.Await()
# Remove Cow from world
CowEntity.RemoveFromParent()
# Resume UFO Patrol
set IsAbducting = false
if:
UFOMovementComponent := Entity.GetComponent[keyframed_movement_component]
마지막으로 EnableAdbuctionBeam() 및 DisableAbductionBeam() 함수는 호출될 때마다 UFO의 납치 광선 메시와 스포트 라이트 컴포넌트를 각각 켜고 끄는 간단한 헬퍼 역할을 합니다.
EnableAbductionBeam():void =
for:
Mesh : Entity.FindDescendantComponents(Meshes.S_EV_SimpleLightBeam_01)
do:
Mesh.Enable()
for:
Light : Entity.FindDescendantComponents(spot_light_component)
do:
Light.Enable()
전체 스크립트
아래는 오버랩 히트를 쿼리하는 완성된 스크립트입니다.
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Verse.org/Colors }
using { /Verse.org/SceneGraph/KeyframedMovement }
using { /Verse.org/SpatialMath }
using { /Fortnite.com/Game }
using { /Fortnite.com/Devices }
스윕 히트 쿼리하기
스윕 히트는 엔티티 간 오버랩을 쿼리할 수 있는 또 다른 중요한 방법입니다. 스윕은 특정 벡터를 따라 지정된 거리로 오브젝트를 움직이는 것을 의미합니다. 플랫폼을 따라 블록을 이동해 플레이어를 틈새로 밀거나 미사일을 직선으로 발사해 벽을 파괴하는 것을 예시로 들 수 있습니다.
FindSweepHits() 함수는 sweep_hit 목록을 반환합니다. 각각의 sweep_hit는 overlap_hit와 마찬가지로, 히트되는 컴포넌트나 볼륨, 스윕을 하는 소스 볼륨 또는 컴포넌트 등의 정보를 제공합니다. 또한 접촉 위치, 노멀, 페이스 노멀, 스윕을 따라 히트가 발생하는 거리에 관한 정보도 제공해 줍니다.
이 템플릿에서는 스윕 히트를 사용하여 이전 오버랩 히트 예시보다 발전된 버전을 만들어 보겠습니다. 이 예시를 확인하기 위해 두 번째 홀의 마지막 상호작용 가능한 패드를 밟아보세요. 패드를 밟으면 UFO가 납치 광선을 생성하고, UFO 메시에서 아래로 스윕 히트를 수행하여 스윕이 오버랩되는 첫 번째 엔티티를 확인합니다. 해당 엔티티가 소라면 UFO가 이를 정상적으로 납치합니다. 하지만 소가 구체 모양의 엔티티에 의해 보호되고 있으면 스윕이 먼저 구체 모양을 히트하고 차단되어 UFO가 소를 납치할 수 없게 됩니다.
Verse 익스플로러에서 FindOverlapHitsExampleComponent.verse를 열어 코드를 살펴보세요. 이 설정은 위 오버랩 히트 예시와 매우 비슷한데, 소 납치와 납치 광선 활성화/비활성화에 사용되는 로직도 동일합니다. 주요한 차이점은 플레이어가 예시에서 앞에 있는 상호작용 가능한 패드를 밟으면 실행되는 OnTriggerEntered() 함수에 있습니다.
이 함수의 코드는 오버랩 히트 예시와 비슷하게, 엔티티에서 키프레임이 지정된 이동 컴포넌트를 가져오고 납치 광선을 활성화하는 것부터 시작합니다.
OnTriggerEntered(Agent:agent):void=
# When a cow is inside the abduction area, stop the ship moving and start the abduction beam.
if:
not IsAbducting?
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
MovementComponent.Pause()
EnableAbductionBeam()하지만 이 함수는 오버랩 대신 스윕을 사용하므로 소가 납치 광선 아래에 있는지를 확인하는 로직이 약간 다릅니다. 이 로직은 UFO 엔티티의 첫 번째 자손을 가져오는 것부터 시작하는데, 여기서는 UFO의 메시 엔티티입니다. 그런 다음 스윕을 수행할 디스플레이스먼트 벡터를 생성하여 UFO에서 바로 아래를 향하도록 합니다.
# Perform the sweep from the UFO Mesh
if (Child := Entity.GetEntities()[0]):
DisplacementVector := vector3{Left:=0.0, Up:=-300.0, Forward:=0.0}이어서 이 디스플레이스먼트 벡터를 사용하여 FindFirstSweepHit() 헬퍼 함수를 호출하고, UFO의 메시 및 벡터를 전달합니다. 첫 번째 컴포넌트가 소의 메시 컴포넌트면 소 납치를 시뮬레이션하는 AbductCow() 함수를 생성합니다.
# Perform the sweep from the UFO Mesh
if (Child := Entity.GetEntities()[0]):
DisplacementVector := vector3{Left:=0.0, Up:=-300.0, Forward:=0.0}
FirstSweepHitEntity := FindFirstSweepHit(Child, DisplacementVector)
# If the First Hit Entity is the Cow Mesh, then abduct the Cow
if (HitEntity := FirstSweepHitEntity?; HitEntity.GetComponent[Meshes.SM_Toy_Cow]):
spawn { AbductCow(HitEntity) }FindFirstSweepHit() 함수는 스윕할 엔티티와, 엔티티를 따라 스윕할 디스플레이스먼트 벡터를 가져옵니다. 스윕을 시뮬레이션할 FindSweepHits()를 호출한 다음, `for` 표현식에서 반환된 각 스윕 시트에서 반복작업을 수행합니다. 각 sweep_hit에는 컴포넌트 또는 볼륨이 포함되어 있으므로 TargetComponent 또는 TargetVolume을 쿼리하여 어떤 타입인지 알 수 있습니다. 여기에서는 코드가 TargetComponent의 소유 엔티티를 가져오고 이를 옵션으로 반환합니다. 따라서 스윕이 컴포넌트를 히트하면 `true`를 반환하고 그렇지 않으면 false를 반환합니다.
# Returns the first Entity hit by FindSweepHits
FindFirstSweepHit(InEntity:entity, DisplacementVector:vector3):?entity =
for (SweepHit : InEntity.FindSweepHits(DisplacementVector)):
return option{ SweepHit.TargetComponent.Entity }
return false전체 스크립트
아래는 스윕 히트를 쿼리하는 완성된 스크립트입니다.
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Verse.org/Colors }
using { /Verse.org/SceneGraph/KeyframedMovement }
using { /Verse.org/SpatialMath }
using { /Fortnite.com/Game }
using { /Fortnite.com/Devices }