이 페이지에서는 새 씬 그래프 컴포넌트 trigger_component를 생성하는 과정을 안내합니다. 이 컴포넌트는 triggerable 인터페이스를 구현하는 씬 그래프 컴포넌트가 있는 엔티티에서 액션을 트리거합니다. trigger_component는 에이전트가 volume_device를 통해 씬 그래프와 상호작용할 수 있게 해 줍니다. 에이전트가 volume_device에 들어가거나 나가면 해당하는 AgentEnteredEvent 및 AgentExitedEvent가 신호를 받고 해당 콜백이 호출되어 씬 그래프 컴포넌트 및 엔티티에서 액션을 수행합니다.
다음 이미지에서 지면의 흰색 받침대는 씬 그래프 엔티티로, trigger_component와 받침대 주변에 invisible volume_device를 포함하고 있습니다.
플레이어가 받침대를 밟으면 trigger_component가 맨 오른쪽 조명 두 개를 트리거합니다.
플레이어가 받침대에서 내려와 volume_device에서 나가도 조명은 켜져 있는 상태를 유지합니다.
이 페이지에서 사용된 Verse 언어 기능에 대한 자세한 내용은 다음을 참조하세요.
트리거 컴포넌트 정의하기
Verse 익스플로러(Verse Explorer)로 이동하여 프로젝트 이름을 우클릭하고 프로젝트에 새로운 Verse 파일 추가(Add new Verse file to project)를 선택합니다.
씬 그래프 컴포넌트(Scene Graph Component) 템플릿을 선택하고 컴포넌트 이름(Component Name)을 trigger_component로 변경합니다.
trigger_component클래스를 정의합니다. 그리고 연결된volume_device를 참조하는 필드, 이 컴포넌트가 자손만 트리거하는지 또는 트리거 가능한 모든 후손을 트리거하는지를 나타내는 필드, 이 컴포넌트와 상호작용하는 모든 에이전트를 참조하는 배열을 추가합니다.Versetrigger_component := class<final_super>(component): # volume_device associated with this trigger_component @editable InteractionVolume<private>:volume_device = volume_device{} # whether or not this triggers on components on child entities @editable OnlyTriggerChildren:logic = false에이전트가 이 컴포넌트에 연결된
volume_device에 들어가고 나갈 때 어떤 작업이 일어나는지를 정의하는OnAgentEntersVolume및OnAgentExitsVolume함수를 추가합니다. 에이전트가 볼륨에 들어가면 상호작용 에이전트를 기록하고triggerable인터페이스를 구현하는 컴포넌트가 있는 자손 엔티티를 찾은 후 해당 엔티티에서 컴포넌트를 트리거합니다. 에이전트가 볼륨에서 나가면 이 볼륨과 상호작용하는 에이전트 목록에서 상호작용하는 해당 에이전트를 제거합니다.Verse# Event that is triggered when an Agent enters the InteractionVolume OnAgentEntersVolume(Agent:agent):void= for: ChosenEntity : Entity.FindDescendantEntities(entity) ChosenEntityComponent : ChosenEntity.GetComponents() CastToInterface := triggerable[ChosenEntityComponent] do: if (OnlyTriggerChildren?): if (Entity = ChosenEntity.GetParent[]): CastToInterface.Trigger()이 컴포넌트의
OnSimulate함수에AgentEntersEvent및AgentExitsEvent에 대한 콜백 함수로OnAgentEntersVolume및OnAgentExitsVolume함수를 등록하여 이 둘을volume_device에 연결합니다.Verse# Runs when the component should start simulating in a running game. OnBeginSimulation<override>():void = # Run OnBeginSimulation from the parent class before # running this component's OnBeginSimulation logic (super:)OnBeginSimulation() # Runs when the component should start simulating in a running game. # Can be suspended throughout the lifetime of the component. Suspensions # will be automatically cancelled when the component is disposed or the # game ends.
트리거 가능 컴포넌트 정의하기
다음으로, 이전에 생성된 triggerable 인터페이스를 구현하는 컴포넌트를 정의합니다. 이는 puzzle_component가 있는 엔티티의 후손인 씬 그래프 엔티티에 추가되어 trigger_component로 조상 엔티티에 의해 트리거되는 컴포넌트입니다. 이 튜토리얼에서는 다음과 같은 서로 다른 세 개의 triggerable 컴포넌트를 정의합니다.
triggerable_mesh_component: 엔티티의mesh_component비저빌리티를 토글합니다.triggerable_light_component: 엔티티의sphere_light_component를 켜고 끄며, 조명이 켜져 있거나 꺼져 있는지에 따라 이미시브 컬러가 있거나 없는 컴포넌트로 엔티티의mesh_component를 변경합니다.triggerable_movement_component:keyframed_movement_component를 사용하여 엔티티의mesh_component트랜스폼을 트리거합니다.
세 컴포넌트는 많은 기능을 공유하는데, 기능 중 많은 수가 동일한 방식으로 정의되어 있습니다. 시작하려면 TriggerableComponents.verse로 명명한 새로운 빈 Verse 파일을 생성합니다.
다음 모듈을 포함시킵니다.
이 파일에서는 씬 그래프 컴포넌트와 씬 이벤트를 사용하므로 /Verse.org/SceneGraph를 포함시킵니다.
이 파일에서는 keyframed_movement_delta를 사용하므로 /Verse.org/SceneGraph/KeyframedMovement를 포함시킵니다.
이 파일에서는 @editable 어트리뷰트를 사용하므로 /Verse.org/Simulation을 포함시킵니다.
이 파일에서는 Verse 트랜스폼을 사용하므로 /Verse.org/SpatialMath를 포함시킵니다.
Verseusing { /Verse.org/SceneGraph } using { /Verse.org/SceneGraph/KeyframedMovement } using { /Verse.org/Simulation } using { /Verse.org/SpatialMath }triggerable_mesh_component,triggerable_light_component,triggerable_movement_component컴포넌트를 정의합니다. 이 컴포넌트는 모두 베이스component클래스에서 상속하고 트리거 가능한 인터페이스를 구현합니다.세 컴포넌트 모두 다음과 같은 동일한 세 가지 이벤트를 구현합니다.
OnReceive: 이 컴포넌트가 씬 이벤트를 수신하는 경우 호출됩니다.OnBeginSimulation: 컴포넌트가 시뮬레이션을 시작해야 하는 경우 호출됩니다.OnSimulate: 컴포넌트가 시뮬레이션을 시작하는 경우 호출됩니다.
다음과 같은 이벤트 구현을 세 컴포넌트 모두에 추가합니다.
Verse# Runs when this component receives a scene event OnReceive<override>(SceneEvent:scene_event):logic= if (PuzzleSolvedEvent := puzzle_solved_event[SceneEvent], not PuzzlePiece?): Trigger() true false # Runs when the component should start simulating in a running game. OnBeginSimulation<override>():void = # Run OnBeginSimulation from the parent class before또한 세 컴포넌트 모두
triggerable컴포넌트가 퍼즐 조각인 경우triggered_event를 상위로 전달하여triggerable인터페이스PostTrigger오버라이드를 동일한 방식으로 구현합니다.triggerable인터페이스 자체에서는 이를 구현할 수 없는데, Verse에서 어떤entity가 트리거되는지 알아야 하고,Entity필드는component클래스의 일부이며component클래스에 대한 추가 종속성 없이는triggerable인터페이스에서 사용할 수 없기 때문입니다.VersePostTrigger<override>():void = if (PuzzlePiece?): Event := triggered_event: Triggered := Triggered TriggeredEntity := option{Entity} Entity.SendUp(Event)
트리거 가능 메시 컴포넌트
triggerable_mesh_component는 엔티티에서 mesh_component의 비저빌리티를 토글합니다. 이를 위해서는 다음 triggerable 인터페이스 함수에 대한 오버라이드를 제공해야 합니다.
SetInitialTriggeredStatePerformActionPerformReverseAction
메시 컴포넌트의 디테일 패널에서 설정된 대로 메시가 게임 월드에서 표시 상태로 시작되는지를 확인하여 트리거된 초기 상태가 결정됩니다.
triggerable_mesh_component의 액션은mesh_component메시가 표시되도록 만듭니다. 이를 위해서는 해당하는 엔티티의mesh_component를 가져오고 메시가 표시되도록 설정한 후triggerable_mesh_component를 트리거된 상태로 설정합니다.Verse# Determine the initial triggered state of this component SetInitialTriggeredState<override>():void= if (MeshComponent := Entity.GetComponent[mesh_component], MeshComponent.Visible?): set Triggered = truetriggerable_mesh_component의 반전 액션은mesh_component메시가 표시되지 않도록 만듭니다. 이를 위해서는 해당하는 엔티티의mesh_component를 가져오고 메시가 표시되지 않도록 설정한 후triggerable_mesh_component를 트리거되지 않은 상태로 설정합니다.VersePerformReverseAction<override>():void = if (MeshComponent := Entity.GetComponent[mesh_component]): if (MeshComponent.Visible?): set MeshComponent.Visible = false set Triggered = false
트리거 가능 이동 컴포넌트
triggerable_movement_component는 keyframed_movement_component를 사용하여 엔티티가 한 위치에서 일련의 다른 위치로 순서대로 이동하도록 트리거합니다. 이를 위해서는 다음 triggerable 인터페이스 함수에 대한 오버라이드를 제공해야 합니다.
SetInitialTriggeredStatePerformActionPerformReverseAction
또한 이 컴포넌트는 다음과 같은 몇 가지 헬퍼 함수를 사용합니다.
GetMovementStates: 이 엔티티를 위한 일련의 트랜스폼을 정의하는 자손 엔티티의 트랜스폼을 가져옵니다.MakeDeltaTransform: 두 입력 트랜스폼 간의 델타 트랜스폼을 생성합니다.ConstructKeyframeDeltas:keyframed_movement_component의 입력에 대한keyframed_movement_delta오브젝트 배열을 생성합니다.SetAndPlayAnimation: 키프레임을 지정하고 이동 애니메이션을 재생합니다.
키프레임이 지정된 애니메이션이 얼마나 지속되는지를 결정하는 이 클래스에 편집 가능 float를 추가합니다.
Verse@editable Duration:float = 2.0이 클래스는
triggerable인터페이스를 구현합니다.PerformAction및PerformReverseAction함수에 기본 구현이 없으므로 이 두 함수에 대한 오버라이드를 제공해야 합니다. 트리거된 초기 상태가 없고 액션이 위치 간 이동이므로 트리거된 기본 초기 상태는 false입니다. 기본 구현에 따라triggerable인터페이스에서 이렇게 지정되므로, 이 함수에서는 이 컴포넌트 클래스에 오버라이드가 필요하지 않습니다.triggerable_movement_component의 액션은 자손 엔티티에서 정의된 일련의 트랜스폼을 따라 엔티티를 이동합니다.VersePerformAction<override>():void = MovementStates := GetMovementStates() AnimationFrames:[]keyframed_movement_delta = ConstructKeyframeDeltas(MovementStates) SetAndPlayAnimation(AnimationFrames)triggerable_movement_component의 반전 액션은 자손 엔티티에서 정의된 일련의 트랜스폼을 따라 엔티티를 반대로 이동합니다. 이 함수는 트랜스폼 배열이 역순인 것을 제외하고PerformAction과 유사합니다.VersePerformReverseAction<override>():void = MovementStates := GetMovementStates() ReversedMovementStates := for: Index := 0..MovementStates.Length - 1 ReversedElement := MovementStates[MovementStates.Length - 1 - Index] do: ReversedElement AnimationFrames := ConstructKeyframeDeltas(ReversedMovementStates) SetAndPlayAnimation(AnimationFrames)이 컴포넌트가 사용하는 헬퍼 함수를 정의합니다. 먼저
GetMovementStates함수를 생성하여 이 이동에서 움직여야 하는 트랜스폼을 가져옵니다.VerseGetMovementStates():[]transform= for (ChildEntity : Entity.FindDescendantEntities(entity), Entity = ChildEntity.GetParent[]): ChildEntity.GetGlobalTransform()두 트랜스폼 간의 델타 트랜스폼을 생성하는
MakeDeltaTransform헬퍼 함수를 정의합니다. 트랜스폼 A에서 트랜스폼 B까지의 델타 트랜스폼 D는 트랜스폼 D가 트랜스폼 A에 적용되었을 때 그 결과가 트랜스폼 B가 되는 트랜스폼입니다.자세히 설명하기 위해, 현재 트랜스폼 A가 있는 씬 그래프 엔티티가 있지만, 트랜스폼 B가 되도록 위해 엔티티를 스케일 조절, 회전, 이동하려고 한다고 가정해 보겠습니다. 델타 트랜스폼 D는 트랜스폼 A에서 트랜스폼 B로 엔티티의 이동에 애니메이션을 적용하기 위해
keyframed_movement_delta에 필요한 트랜스폼인 것입니다.VerseMakeDeltaTransform(InTransformOne:transform, InTransformTwo:transform):transform = transform: Translation := InTransformTwo.Translation - InTransformOne.Translation Rotation := MakeComponentWiseDeltaRotation(InTransformOne.Rotation, InTransformTwo.Rotation) Scale := vector3: Forward := InTransformTwo.Scale.Forward / InTransformOne.Scale.Forward Right := InTransformTwo.Scale.Right / InTransformOne.Scale.Right Up := InTransformTwo.Scale.Up / InTransformTwo.Scale.Upkeyframed_movement_component애니메이션의 입력에 대한keyframed_movement_delta오브젝트 배열을 생성하는ConstructKeyframeDelta헬퍼 함수를 정의합니다.VerseConstructKeyframeDeltas<public>(MovementStates:[]transform):[]keyframed_movement_delta = var LastTransform:transform = Entity.GetGlobalTransform() for (EntityIndex -> CurrentTransform : MovementStates): DeltaTransform := MakeDeltaTransform(LastTransform, CurrentTransform) set LastTransform = CurrentTransform keyframed_movement_delta: Transform := DeltaTransform Duration := Duration Easing := if (MovementStates.Length > 1): if (EntityIndex = 0):keyframed_movement_delta배열을keyframed_movement_component애니메이션에 할당하고 이를 재생하는SetAndPlayAnimation함수를 생성합니다.VerseSetAndPlayAnimation<private>(Frames:[]keyframed_movement_delta):void = if (KeyframedMovementComponent := Entity.GetComponent[keyframed_movement_component]): KeyframedMovementComponent.SetKeyframes(Frames, oneshot_keyframed_movement_playback_mode{}) KeyframedMovementComponent.Play()
트리거 가능 조명 컴포넌트
triggerable_light_component는 조명을 켜고 끕니다. 이를 위해서는 다음 triggerable 인터페이스 함수에 대한 오버라이드를 제공해야 합니다.
SetInitialTriggeredStatePerformActionPerformReverseAction
조명의 트리거된 초기 상태는 조명이 켜져 있는지 여부에 따라 결정되므로
SetInitialTriggeredState가 조명이 이미 켜져 있는지를 확인하고 켜져 있는 경우 이를 초기에 트리거된 상태로 설정합니다.Verse# Determine the initial triggered state of this light SetInitialTriggeredState<override>():void= for: DescendantEntity : Entity.FindDescendantEntities(entity) Entity = DescendantEntity.GetParent[] MeshComponent := DescendantEntity.GetComponent[mesh_component] LightComponent := DescendantEntity.GetComponent[light_component] MeshComponent.IsEnabled[] LightComponent.IsEnabled[] do:triggerable_light_component에서 수행하는 액션은 조명을 켜고 조명이 켜져 있음을 나타내는 메시로 전환하는 것입니다.VersePerformAction<override>():void= if (not IsLightOn?): for (DescendantEntity:Entity.FindDescendantEntities(entity), Entity = DescendantEntity.GetParent[]): if (MeshComponent := DescendantEntity.GetComponent[mesh_component]): if (LightComponent := DescendantEntity.GetComponent[light_component]): MeshComponent.Enable() LightComponent.Enable() else: MeshComponent.Disable() set IsLightOn = truetriggerable_light_component에서 수행하는 액션은 조명을 끄고 이미시브가 아닌mesh_component가 표시되도록 설정하는 것입니다.VersePerformReverseAction<override>():void= if (IsLightOn?): for (DescendantEntity:Entity.FindDescendantEntities(entity), Entity = DescendantEntity.GetParent[]): if (MeshComponent := DescendantEntity.GetComponent[mesh_component]): if (LightComponent := DescendantEntity.GetComponent[light_component]): MeshComponent.Disable() LightComponent.Disable() else: MeshComponent.Enable() set IsLightOn = false
다음 단계
모든 Verse 코드를 작성해 봤으므로 이제 자신만의 Verse 코드를 만들어 보세요. 다음으로, 베이스 엔티티 및 컴포넌트로 프리팹을 생성해 보겠습니다. 이를 UEFN에서 재사용하여 퍼즐 게임을 만들 수 있습니다.
최종 코드
TriggerComponent.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/SceneGraph }
using { Logging }
trigger_component<public> := class<final_super>(component):
@editable
InteractionVolume<private>:volume_device = volume_device{}
TriggerableComponents.verse
using { /Verse.org/SceneGraph }
using { /Verse.org/SceneGraph/KeyframedMovement }
using { /Verse.org/Simulation }
using { /Verse.org/SpatialMath }
EnabledToolTip<public><localizes>:message = "Whether or not this component is enabled."
triggerable_mesh_component<public> := class<final_super>(component, triggerable):
<# Triggerable interface #>