Эта функция Scene Graph пока находится в экспериментальной стадии. В настройках проекта установите флажок, чтобы включить параметр «Экспериментальные функции Scene Graph» для возможности доступа к этим экспериментальным функциям в Scene Graph.
Если ваш проект содержит экспериментальные функции Scene Graph, они будут обнаружены в процессе проверки в творческом портале. Вы получите уведомление о ресурсах, ограничивающих возможность публикации острова. Чтобы опубликовать остров, отключите экспериментальные функции в настройках проекта.
Во все модули и компоненты Scene Graph встроена функция, которая создаёт одиночные события или цепочку событий в Scene Graph, называемые Событиями сцены. События сцены — это протоколы связи, которые позволяют различным частям Scene Graph взаимодействовать друг с другом, переопределяя и определяя новое поведение модулей и компонентов.
События сцены можно использовать повторно в ваших проектах, вы можете расширять события сцены, добавляя дополнительные события или изменяя поведение модулей и компонентов в цепочке событий, чтобы делать что-то немного по-другому
На одно событие сцены могут реагировать несколько компонентов, а сами события могут отправляться вверх или вниз по иерархии в Scene Graph. Их можно представить как сообщения, передаваемые по Scene Graph, где каждый компонент может отреагировать на сообщение.
Например, если вы создаёте кладбище с помощью Scene Graph, вы можете использовать события сцены, чтобы определить цепочку событий, которые запускаются в момент, когда распахиваются ворота кладбища со статичной сеткой. Открытие ворот может стать отправной точкой для цепочки событий, в результате которых из-за надгробий будет появляться эффект частиц призраков и начнёт воспроизводиться мрачная музыка.
Это может быть событие сцены, которое вы используете для кладбища несколько раз, или вы можете настроить событие так, чтобы можно было использовать базовые функции события сцены: допустим, какая-то дверь со статичной сеткой открывается и вызывает серию последующих событий.
События сцены нужны для отделения частей Scene Graph друг от друга, чтобы они могли взаимодействовать через сообщения, а не напрямую привязываться друг к другу.
Как работают события сцены
Для передачи событий в определённые модули и компоненты Scene Graph используется Verse. Чтобы обработать событие сцены в компоненте, переопределите существующую функцию OnReceive(scene_event):logic в компоненте. Каждое событие сцены, передаваемое в ваш компонент, будет вызывать эту функцию, что позволит среагировать на событие.
Событие сцены — это любой класс, в котором реализован интерфейс scene_event. События сцены можно отправлять одним из трёх способов: SendUp, SendDown или SendDirect.
SendUp
SendUp отправляет событие в целевой модуль и его родительский элемент. Родительский модуль будет передавать это событие в свои родительские элементы и далее, пока не достигнет модуля симуляции, родительскими элементами которого являются модули в вашем мире.
Отправитель точно не знает, какой приёмник будет обрабатывать событие и будет ли вообще какой-нибудь элемент реагировать на событие. Такой вид отправки отлично подходит для отправки телеметрических данных, передачи сигналов системам более высокого уровня, а также различных видов запросов, которые могут или не могут быть обработаны.
В качестве примера представьте, что вы наносите удар мечом по ногам персонажа, причём нога находится в нижней части иерархии. Нога не может обработать событие урона, поэтому событие отправляется в базу персонажа, где вы разместили damage_receiver_component, который может получить событие урона и обработать удар.
На диаграмме ниже родительский модуль — это событие с меткой 1, которое активируется в первом зелёном модуле. Вызов SendUp начинается в иерархии с родительского модуля и передаёт событие вверх в модуль симуляции.
SendDown
Вы также можете использовать модуль симуляции в качестве катализатора и отправить серию изменений вниз по дереву модулей. SendDown отправляет событие в целевой модуль и во все его дочерние элементы. Затем каждый дочерний элемент передаёт это событие в свои дочерние элементы.
Эту функцию можно использовать в ситуации, когда произошло глобальное событие и все элементы сцены должны иметь возможность среагировать на него.
SendDirect
SendDirect отправляет событие напрямую в целевой модуль, но не передаёт его в родительские или дочерние модули. SendDirect используется для отправки событий в конкретный модуль или компонент, как показано на диаграмме ниже.
Поглощение событий
Когда событие отправляется вверх или вниз в иерархии модулей, отдельные компоненты могут не реагировать на него. Компонент помечает событие как завершённое и не передаёт его в свои дочерние или родительские элементы, по сути действуя в качестве выключенного переключателя.
Поглощение событий позволяет контролировать область действия эффекта события. Оно работает двумя способами:
Создание события, относящегося только к одному модулю, но не к его дочерним или родительским элементам.
Недопущение отправки соответствующего события напрямую в дочерние или родительские элементы.
Избегая отправки события напрямую в дочерние или родительские элементы, вы можете определить, для чего это событие нужно этим элементам модуля, и сделать так, чтобы можно было вручную отправить в них другое событие в ответ для большего контроля реакции этих модулей на исходное событие.
К примеру, модуль получает событие урона через SendDown. Компонент одного модуля может решить ответить на это событие, уменьшив очки здоровья, и не будет передавать это событие дальше по иерархии модулей, поскольку дочерним модулям нет необходимости получать урон.
Однако в случае, когда система начисления очков задана выше принимающего модуля, цепочка родительских элементов модуля должна будет отправить информацию об уроне, чтобы наградить игрока медалью за нанесение урона врагам. В этой ситуации в принимающем модуле можно вызвать SendUp со специальным событием, которое отправляет оценку очки за урон в родительский компонент, реагирующий на информацию о начислении очков.
Поглощение событий во время SendUp и SendDown
События могут поглощаться во время распространения события, инициируемого либо функцией SendUp, либо функцией SendDown, путём возврата значения true из реализации OnReceive(SceneEvent:scene_event):logic компонента.
Если какой-либо компонент модуля решит поглотить событие во время SendUp, все компоненты этого модуля всё равно будут активировать свои соответствующие обратные вызовы OnRecieve. После этого передача события прекращается и оно не передаётся в родительский элементы модуля.
Если какой-либо компонент модуля решит поглотить событие во время SendDown, все компоненты этого модуля всё равно будут активировать свои соответствующие обратные вызовы OnRecieve. Событие не передаётся в дочерние элементы модуля. При этом событие всё равно будет передаваться в оставшиеся модули на том же уровне.
На диаграмме ниже показано, что событие сцены 4 продолжает цепочку событий, а событие сцены 3 — нет.
Создание события сцены
Перед созданием событий сцены необходимо выполнить небольшую исходную настройку и определить последовательность действий. Создайте ресурсы, перечисленные ниже, чтобы заполнить компоненты сетки деревьями и эффекты частиц соответствующими эффектами. При сохранении ресурсов эффектов частиц они автоматически сохраняются в файл Verse с именем Assets.digest.verse в качестве объектов Verse, на которые можно ссылаться в коде.
Визуальный эффект разряда молнии
Чтобы молния начиналась в случайной точке в небе над модулем и заканчивалась в модуле Scene Graph на земле, измените визуальный эффект удара молнии следующим образом:
Задайте для параметра Настройка генератора луча > Начало луча значение Добавить вектор в местоположение, для параметра Местоположение значение SimulationPosition, для параметра Вектор значение Вектор со случайным диапазоном с Минимальным (–200.0, –200,0, 1000) и Максимальным значением (200,0, 200,0, 2000,0).
Установите для параметра Настройка генератора луча > Конец луча значение SimulationPosition.
Теперь удар молнии будет начинаться где-то в небе над модулем и заканчиваться в модуле, к которому прикреплён
particle_system_component.Деревья и трава со статичной сеткой
Смоделируйте собственные деревья в режиме моделирования.
Добавьте эти ресурсы в модули, а затем разместите несколько модулей «Цель молнии» в Scene Graph, в которые может ударить молния. В каждом из этих модулей есть компонент удара молнии particle_system_component, готовый получить инструкции, определённые вами в событии сцены. Дополнительная настройка не требуется.
Модули могут быть вложены в готовый элемент или полностью отделены друг от друга и распределены по сцене.
Перенесите ресурсы из базового набора UE, чтобы создать визуальный эффект огня; также вы можете создать визуальные эффекты молнии и огня в UE и перенести эти ресурсы в свой проект UEFN.
Создайте все свои ресурсы перед добавлением модулей в сцену. Так все созданные вами ресурсы со статичной сеткой появятся в меню компонентов сетки.
Разместив модули, определите последовательность событий, вызвавших лесной пожар, а затем определите названия событий, описывающие, что делает каждое событие. После этого следуйте указаниям в уроке по Verse в разделе Кодирование поведения.
Соглашения об именах событий сцены
В названии события сцены вы можете использовать любое количество символов. Название должно описывать событие и чётко демонстрировать его цель, например damage_taken_event, health_power_up_event и т. д.
В последовательности событий, связанных с лесным пожаром, есть два основных события:
Удар молнии
Распространение огня
Чтобы описать, что делают эти события, мы назовём событие удара молнии struck_by_lightning_event, а событие распространения огня — fire_propagation_event.
Последовательность событий
При создании события сцены представьте, что это интерфейс, в котором эффекты оказывают влияние на всю иерархию модулей и компонентов. Кроме того, поскольку события сцены могут использоваться другими людьми, подумайте, как другой разработчик мог бы развить ваше событие или использовать его в своём собственном Scene Graph.
Создаваемое вами событие сцены будет сообщать об основных событиях, которые произойдут перед активацией другого события, связанного с событием родительского элемента. Таким образом, последовательность событий выглядит следующим образом:
В мире происходит событие сцены
struck_by_lightning_event.Модули сообщают о том, получили ли они удар
struck_by_lightning_eventили нет. Модули, в которые попала молния, активируют событиеfire_propagation_event.Событие
fire_propagation_eventпозволяет другим модулям решить, находятся ли они достаточно близко к событиюfire_propagation_event, чтобы огонь распространился и на них.Событие
fire_propagation_eventраспространяется на другие компоненты сетки
Событие fire_propagation_event продолжает выполняться до тех пор, пока не запустится каждый компонент сетки, который решит, что он находится достаточно близко, чтобы fire_propagation_event распространило огонь и на него. Во время воспроизведения в сцене он должен напоминать реальный лесной пожар.
Развёртывание последовательности
Несмотря на то что struck_by_lightning_event вызывает событие fire_propagation_event, вы можете разнообразить игру, добавив новые элементы, из-за которых огонь также будет распространяться. К примеру, можно добавить explosive_event, которое при взрыве отправляет событие fire_propagation_event.
Ещё один интересный момент, связанный с событиями сцены, заключается в том, что модулям, обрабатывающим fire_propagation_event, не нужно знать, что вызвало пожар, — достаточно того, что что-то заставляет их принять решение о том, что им тоже нужно гореть. Так легче управлять кодом по двум причинам:
В мире происходит событие сцены
struck_by_lightning_event.Так проще изменить поведение
struck_by_lightning_event, не нарушив работу модулей, для которых важно только событиеfire_propagation_event.Так проще написать
explosive_event, потому что для объектов, которые могут загореться, требуется только событиеfire_propagation_event.
Кодирование поведения
Когда вы добавите в Scene Graph все необходимые модули, используйте приведённый ниже код для создания события сцены, которое начинается с удара молнии и приводит к возгоранию деревьев и нанесению урона от пожара.
Файл Assets.digest.verse автоматически находит объекты Verse, чтобы ссылаться на них в сцене. Ссылки на созданные вами эффекты частиц находятся в модуле визуальных эффектов.
VFX := module:
Fire_NS<scoped {/InvalidDomain/Scene_Events_Test}> := class<final><public>(particle_system_component):
Lightning_NS<scoped {/InvalidDomain/Scene_Events_Test}> := class<final><public>(particle_system_component):
Шаг 1: создание компонента Scene Graph
Чтобы создать событие сцены в UEFN, откройте меню Verse в строке меню и выберите Проводник Verse из выпадающего меню. В редакторе откроется панель проводника Verse со списком файлов Verse, связанных с вашим проектом.
Создайте новый файл Verse, выполнив следующие действия:
Нажмите правой кнопкой мыши на имени проекта в начале списка.
Выберите Добавить новый файл Verse в проект из выпадающего меню. Откроется окно «Создать сценарий Verse».
Выберите компонент Scene Graph из списка файлов Verse и назовите файл fire_event_component.
Нажмите Создать. Файл автоматически откроется в Visual Studio Code (VS Code) и будет содержать базовые API, необходимые для создания поведения нового компонента.
Когда вы будете готовы протестировать сценарий, откройте меню Verse и выберите Создать код Verse из выпадающего меню. После этого нажмите на кнопку «Verse», чтобы запустить код.
Шаг 2: добавление библиотек Verse
Начните разработку события сцены с создания последовательности событий и условий, которые приводят к удару молнии. После определения события удара молнии создайте событие лесного пожара, определив свойства урона от огня, протоколы распространения огня и протоколы тушения.
Добавьте следующие библиотеки в свой компонент, чтобы можно было использовать пространственную математику для определения местоположения молнии и огня, одновременное выполнение и прочие функции Verse.
Verseusing { /Verse.org/SpatialMath } using { /Verse.org/Random } using { /Verse.org/Concurrency } using { /Verse.org/Simulation } using { /Verse.org/SceneGraph }
Шаг 3: определение класса события удара молнии
Этот класс события определяет местоположение источника молнии, место попадания молнии и DamageRadius попадания.
Создайте класс события
struck_by_lightning_event := class(scene_event):. Класс определяет свойства события сцены в постоянных выражениях, которые описывают, где в сцене происходит событие удара молнии, используя вектор для информации о местоположении, а радиус урона от удара молнии задаётся значением типаfloat.Verse# Event to indicate an entity is struck by lightning struck_by_lightning_event<public> := class(scene_event): # Lightning hit location HitLocation:vector3 = vector3{} # Lightning damage radius DamageRadiusCentimeters:float = 100.0
Шаг 4: создание компонента погоды и добавление редактируемых свойств с переменными
Используйте редактируемые свойства и переменные, чтобы настроить появление молнии в сцене, случайные удары молнии, время между ударами и радиус урона от удара молнии.
Определите поведение молнии, добавив
редактируемыесвойства в класс компонентаlightning_weather_component := component ():. В редактируемых свойствах задайте минимальное и максимальное значение для времени между ударами молнии и радиуса урона от этих ударов в сантиметрах.Verse# Component that lives on an entity, and randomly creates lightning strikes lightning_weather_component<public> := class<final_super>(component): # Minimum random time between lightning strikes @editable MinRandomLightningDelaySeconds:float = 10.0 # Maximum random time between lightning strikes @editable MaxRandomLightningDelaySeconds:float = 60.0
Шаг 5: определение события удара молнии
Используйте метод для проверки визуального эффекта молнии и используйте его свойства начального и конечного луча, чтобы определить, насколько близко компоненты сетки находятся к визуальному эффекту, и передать эту информацию по всему событию
Создайте условный
методOnSimulate.VerseOnSimulate<override>()<suspends>:void=Создайте и добавьте
цикл, который случайным образом воспроизводит и останавливает компонент молнии, используя константу, в которой заданы минимальное и максимальное значения задержки времени воспроизведения.Verseloop: # Sleep for a random delay before next lightning strike RandomDelay := GetRandomFloat(MinRandomLightningDelaySeconds, MaxRandomLightningDelaySeconds) Sleep(MaxRandomLightningDelaySecondsДобавьте выражение
if, которое использует модуль симуляции для поиска других модулей в симуляции, поскольку все остальные модули являются дочерними по отношению к модулю симуляции. Так вы сможете выбрать случайный модуль, который будет поражён молнией, а не использовать коллизию для поиска модулей в сцене.Verse# Randomly hit an entity in the world with lightning if (SimEntity := Entity.GetSimulationEntity[]):Далее в коде нужно указать цель, куда будет попадать молния.
Добавьте выражение
then, которое передаёт запрос в качестве цели молнии из SimEntity. Затем добавьте выражение с условным выражениемif, которое будет реагировать на эту передачу и делать так, чтобы все модули, которые могут отреагировать, будут отправлять событие обратно в SimEntity. Далее нужно найти источник молнии, поскольку генератор луча использует две точки для начала и окончания луча.VerseLightningTargets := for (EntityWithLightning : SimEntity.FindDescendantEntitiesWithComponent(particle_system_component)): EntityWithLightning if: LightningTargets.Length > 0 RandomIndex := GetRandomInt(0, LightningTargets.Length - 1) RandomEntity := LightningTargets[RandomIndex]Вызовите
LightningVFXComponentв случайном месте с помощью оператора if. Затем добавьте выражение then, которое воспроизводит эффект частиц луча в исходном и целевом местоположениях.lightning_entityиспользует параметры Настройка генератора лучей > Начало луча для воспроизведения события удара молнии в случайных точках в небе. ЗатемLightningVFXComponentиспользует параметр частиц луча «Настройка генератора луча» > «Конец луча», чтобы определить, в каком месте сцены будет отображаться конец частицы луча. Для настройки задано значение «Местоположение симуляции», при котором дляlightning_entityиспользуются конечные координаты эффекта частиц.После этого создайте и определите урон от
struck_by_lightning_event, используя данные источника и цели, чтобы определить место нанесения урона целевому компоненту сетки с помощьюDamageRadiusдля описания области попадания удара молнии. Это событие будет передано в модуль симуляции для добавления случайной длительности удара молнии, поэтому завершите цепочку событий с помощьюSimulationEntity.SendDown(Event).Verseif (VFX := RandomEntity.GetComponent[particle_system_component]): RandomDurationOfStrike := GetRandomFloat(MinRandomLightningDurationSeconds, MaxRandomLightningDurationSeconds) VFX.Play() Sleep(RandomDurationOfStrike) VFX.Stop() else: Print("Could not find particle_system_component on this entity") Event := struck_by_lightning_event: HitLocation := Entity.GetGlobalTransform().Translation
Шаг 6: определение события пожара
Создайте классы событий, которые будут определять величину урона от огня и возможность его распространения с учётом урона, наносимого в сцене.
Создайте два класса. Класс
fire_damage_eventи классfire_propagation_event, которые проверяют достижение значенияDamageAmountи воспроизводят частицу огня, когдаDamageAmountдостигает порога значения с плавающей запятой.Verse# Event indicating an entity was damaged by fire fire_damage_event<public> := class(scene_event): BurningEntity:entity = entity{} DamageAmount:float = 100.0 # Event indicating an entity propagates fire fire_propagation_event<public> := class(fire_damage_event): FireRadiusCentimeters:float = 100.0Создайте класс
flammable_component, который будет определять, распространится ли огонь на сетку, а также редактируемые свойства для распространения огня на сетки в сцене.Verse# Component that makes something flammable flammable_component<public> := class<final_super>(component): # Fire damage amount applied every second @editable FireDamageAmount:float = 10.0 # Fire tries to propagate on this interval @editable FirePropagationIntervalSeconds:float = 10.0К
flammable_eventдобавляется условная переменная, которая определяет, воспроизводятся ли или должны ли воспроизводиться визуальные эффекты огня.Verse# Is it on fire? var IsOnFire:logic = falseФункция
IsCloseEnoughToBurningEntityToIgniteопределяет, достаточно ли близок огонь, чтобы активировать новые события визуального эффекта огня.Verse# Is this component close enough to the source of a fire propagation event to burst into flames? IsCloseEnoughToBurningEntityToIgnite(FirePropogationEvent:fire_propagation_event)<decides><transacts>:void = EntityLocation := Entity.GetGlobalTransform().Translation FirePropogationLocation := FirePropogationEvent.BurningEntity.GetGlobalTransform().Translation DistanceToFire := Distance(EntityLocation, FirePropogationLocation) DistanceToFire <= FirePropagationEvent.FireRadiusCentimetersФункция
IsCloseEnoughToLightningToIgniteопределяет, произошёл ли удар молнии достаточно близко к сетке, чтобы она загорелась.Verse# Is this component close enough to a lightning strike to burst into flames? IsCloseEnoughToLightningToIgnite(LightningEvent:struck_by_lightning_event)<decides><transacts>:void = EntityLocation := Entity.GetGlobalTransform().Translation LightningStrikeLocation := LightningEvent.HitLocation DistanceToFire := Distance(EntityLocation, LightningStrikeLocation) DistanceToFire <= LightningEvent.DamageRadiusCentimetersФункция OnRecieve определяет, какие сетки загораются, после того как функция IsCloseEnoughToBurningEntityToIgnite определяет вероятность распространения огня, а переменная IsCloseEnoughToLightningToIgnite определяет вероятность попадания удара молнии в сетку.
Verse# Receive scene events OnReceive<override>(SceneEvent:scene_event):logic = # Burst into flames if lightning hit close enough if (LightningEvent := struck_by_lightning_event[SceneEvent], IsCloseEnoughToLightningToIgnite[LightningEvent]): Ignite() # Burst into flames if something close enough is burning if (FireEvent := fire_propagation_event[SceneEvent], IsCloseEnoughToBurningEntityToIgnite[FireEvent]): Ignite() falseМетод
igniteиспользуется для автоматического воспроизведения эффекта с помощью условного оператора if, который выполняет поиск системы частиц огня. Операторthenопределяет, действительно ли компонент воспроизводит визуальный эффект огня.Добавьте асинхронные задачи в метод для проверки системы частиц огня Fire_NS, чтобы при вызове огонь появлялся и распространялся.
Verse# Burst into flames Ignite():void = if (not IsOnFire?): set IsOnFire = true # Add a new fire VFX component FireVFX := VFX.Fire_NS{Entity := Entity} Entity.AddComponents(array{FireVFX}) # Spawn async tasks to implement the state of being on fire
Шаг 7: определение события тушения огня
Далее создадим ряд методов, которые будут определять, когда компоненты сетки будут загораться, когда огонь будет потушен, какой урон он нанесёт и когда он будет распространяться.
Создайте
метод Extinguish,который останавливает эффект огня, выполняя поиск воспроизводящих его компонентов, с помощью условных операторов, чтобы найти систему частиц огня и удалить эффект.Verse# Put out the flames Extinguish():void= if (IsOnFire?): set IsOnFire = false # Remove the fire VFX component if (FireVFX := Entity.GetComponent[particle_system_component]): FireVFX.RemoveFromEntity()Создайте
метод OnFire,который использует цикл, отслеживающийFireDamage, передающий событие вниз по цепочке модулей до окончания события и останавливающий воспроизведение события.Verse# Suspends function called when we're on fire OnFire()<suspends>:void= # Damage self every second loop: # Fill out a fire damage event - replace this with whatever properties should go here FireDamage := fire_damage_event: DamageAmount := FireDamageAmount Entity.SendDown(FireDamage) Sleep(1.0)Наконец, создайте
метод FirePropagation, который используетциклдля распространения огня по цепочке модулей и тушит их по достижении модуля симуляции.Verse# Propagate fire to other entities FirePropagation()<suspends>:void= loop: Sleep(FirePropagationIntervalSeconds) if: SimulationEntity := Entity.GetSimulationEntity[] FirePropagationEvent := fire_propagation_event{ BurningEntity := Entity } then: # Broadcast fire propagation event down from simulation entity SimulationEntity.SendDown(FirePropagationEvent)
Когда код будет завершён, скомпилируйте его и добавьте соответствующие модули в Scene Graph для реализации эффектов ударов молнии и лесного пожара. После этого добавьте компонент flammable_component к модулям в сцене, которые должны загореться. Под модулем симуляции должен быть модуль верхнего уровня с компонентом lightning_weather_component, управляющий погодой.
Как только во все модули будут добавлены соответствующие события сцены, запустите сеанс редактирования по сети, чтобы проверить работу кода в сцене.
Результат
Скопируйте и вставьте код в проект, чтобы проверить реализацию события сцены.
lightning.verse
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/Random }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Verse.org/SpatialMath }
# Event to indicate an entity is struck by lightning
struck_by_lightning_event<public> := class(scene_event):
fire.verse
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Verse.org/SpatialMath }
# Event indicating an entity was damage by fire
fire_damage_event<public> := class(scene_event):
BurningEntity:entity = entity{}
DamageAmount:float = 100.0