На этой странице описан порядок создания нового компонента Scene Graph с названием trigger_component. Этот компонент запускает действия с модулями, в которых есть компонент Scene Graph, реализующий интерфейс активируемых компонентов. Компонент trigger_component предоставляет агентам способ взаимодействовать со Scene Graph через volume_device. Когда агент входит в компонент volume_device или выходит из него, подаётся сигнал о соответствующем событии AgentEnteredEvent и AgentExitedEvent и вызываются соответствующие функции обратного вызова для выполнения действий с компонентами и модулями Scene Graph.
На следующем изображении белый пьедестал на земле — это модуль Scene Graph с компонентом trigger_component и невидимым volume_device вокруг постамента.
Когда игрок встаёт на пьедестал, trigger_component включает два самых дальних источника света справа.
Когда игрок сходит с постамента и покидает volume_device, источники света продолжают гореть.
Дополнительная информация о функциях языка Verse, используемых на этой странице, приведена на следующих страницах:
Определение компонента «Триггер»
Перейдите в проводник Verse, правой кнопкой мыши выберите название проекта и далее — Добавить новый файл Verse в проект.
Выберите шаблон Компонент Scene Graph и измените название компонента на 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Добавьте функции
OnAgentEntersVolumeиOnAgentExitsVolume, которые определяют, что происходит, когда агент входит в связанный компонентvolume_deviceи покидает его. Когда агент войдёт в эту область, зарегистрируйте взаимодействующий агент и найдите дочерние модули, содержащие компонент, реализующий интерфейсактивируемых компонентов, а затем активируйте этот компонент в этих модулях. Когда агент покинет эту область, удалите взаимодействующий агент из списка агентов, взаимодействующих с этой областью.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()Подключите функции
OnAgentEntersVolumeиOnAgentExitsVolumeк компонентуvolume_device, регистрируя их в качестве функций обратного вызова дляAgentEntersEventиAgentExitsEventв функцииOnSimulateэтого компонента.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.
Определение активируемых компонентов
Далее определим компоненты, реализующие созданный ранее интерфейс активируемых компонентов. Это компоненты, которые добавляются в модули Scene Graph, которые являются потомками модуля с puzzle_component и активируются модулем-предком с trigger_component. В этом уроке определены три разных активируемых компонента:
triggerable_mesh_component: делает видимым/невидимымmesh_componentмодуля.triggerable_light_component: включает/выключаетsphere_light_componentмодуля и заменяетmesh_componentмодуля на компонент с испускаемым цветом или без него, в зависимости от того, включён источник света или нет.triggerable_movement_component: активирует преобразованиеmesh_componentмодуля с помощьюkeyframed_movement_component.
Эти три разных компонента обращаются к большому набору общих функций, многие из которых определены таким же образом. Для начала создайте новый пустой файл Verse с именем TriggerableComponents.verse.
Добавьте следующие модули:
/Verse.org/SceneGraph, так как в этом файле используются компоненты Scene Graph и события сцены.
/Verse.org/SceneGraph/KeyframedMovement, так как в этом файле используется keyframed_movement_delta.
/Verse.org/Simulation, так как в этом файле используется атрибут @editable.
/Verse.org/SpatialMath, так как в этом файле используются преобразования Verse.
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. Все эти компоненты наследуются от базового классакомпонентови реализуют интерфейс активируемых компонентов.Все три компонента реализуют три одинаковых события:
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Все три компонента также реализуют переопределение
PostTriggerинтерфейсаактивируемых компонентовтаким же образом, отправляяtriggered_eventеслиактивируемыйкомпонент является деталью головоломки. Вы не можете сделать это внутри самого интерфейсаактивируемых компонентов, потому что Verse нужно знать, какоймодульактивируется, а полемодуляявляется частью классакомпонентаи недоступно для интерфейсаактивируемых компонентовбез дополнительной зависимости от классакомпонента.VersePostTrigger<override>():void = if (PuzzlePiece?): Event := triggered_event: Triggered := Triggered TriggeredEntity := option{Entity} Entity.SendUp(Event)
Активируемый компонент сетки
Компонент triggerable_mesh_component делает видимым/невидимым компонент mesh_component в модуле. Для этого необходимо предоставить переопределения для следующих функций интерфейса активируемых компонентов:
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 = trueПротивоположное действие для
triggerable_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. Для этого необходимо предоставить переопределения для следующих функций интерфейса активируемых компонентов:
SetInitialTriggeredStatePerformActionPerformReverseAction
Кроме того, этот компонент использует несколько вспомогательных функций:
GetMovementStates: получение преобразований дочерних модулей, определяющие серию преобразований для данного модуля.MakeDeltaTransform: создание дельта-преобразования между двумя входными преобразованиями.ConstructKeyframeDeltas: создание массива объектовkeyframed_movement_deltaдля ввода вkeyframed_movement_component.SetAndPlayAnimation: назначение опорных кадров и воспроизведение анимации перемещения.
Добавьте в этот класс редактируемое число с плавающей запятой, которое определяет продолжительность анимации опорных кадров.
Verse@editable Duration:float = 2.0Этот класс реализует интерфейс
активируемых компонентов, поэтому необходимо обеспечить переопределения для функцийPerformActionиPerformReverseAction, поскольку они не имеют реализации по умолчанию. Поскольку начального состояния активации нет, а действие заключается в перемещении из одного места в другое, то начальное состояние активации по умолчанию имеет значение false. Это то, что делает базовая реализация в интерфейсеактивируемых компонентов, поэтому эта функция не требует переопределения в данном классе компонентов.Действие для
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, которая создаёт дельта-преобразование между двумя преобразованиями. Дельта-преобразование, или D, от преобразования A к преобразованию B — это такое преобразование, при котором в результате применения преобразования D к преобразованию A получается преобразование B.В дополнение к этому представим, что у вас есть модуль Scene Graph с текущим преобразованием A, но вы хотите масштабировать, повернуть и перенести свой модуль, чтобы получить преобразование B. Дельта-преобразование D — это преобразование, необходимое для
keyframed_movement_delta, чтобы анимировать перемещение вашего модуля от преобразования A к преобразованию B.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.UpОпределите вспомогательную функцию
ConstructKeyframeDelta, которая создаёт массив объектовkeyframed_movement_deltaдля ввода в анимациюkeyframed_movement_component.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):Создайте функцию с названием
SetAndPlayAnimation, которая присваивает массивkeyframed_movement_deltaанимацииkeyframed_movement_componentи воспроизводит её.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 включает и выключает источник света. Для этого необходимо предоставить переопределения для следующих функций интерфейса активируемых компонентов:
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 = trueДействие, выполняемое компонентом
triggerable_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 #>