Медики — это распространённый архетип персонажей во многих играх. Задача медика — лечить ближайших персонажей и помогать своим товарищам по команде восстанавливаться после получения урона. Медики выполняют разные роли в зависимости от игры — например, это могут быть врачи, лечащие пациентов в больнице, боевые медики, которые помогают своей команде сражаться и одновременно лечат, или нейтральные медпункты, которые лечат кого угодно.
Персонаж-медик, которого вы создадите в этом примере, следует набору логических правил.
- Бездействие
- Начать лечить агентов
- Цикл лечения
- Перейти к агенту
Медик начинает игру в состоянии бездействия и патрулирует, пока в область лечения не войдёт агент. Этот агент добавляется в очередь лечения медика. Медику необходимо отслеживать агента, которого нужно вылечить следующим, и очередь — это удобная структура данных для этой цели, поскольку очереди организованы по принципу «первый вошёл, последний вышел». Это означает, что персонаж, который первым войдёт в область лечения, будет вылечен первым.
Как только медик получает агента, которого необходимо вылечить, он сначала проверяет, находится ли здоровье агента ниже порога лечения. Если это так, он начинает лечить его с определённой скоростью до тех пор, пока здоровье агента не достигнет порогового значения или пока агент не выйдет из области лечения. Во время лечения медик старается оставаться рядом с агентом, постоянно следуя за ним. Как только здоровье агента вернётся к пороговому уровню, медик примется за лечение следующего агента и начнёт процесс заново. Если агентов для лечения больше нет, медик вернётся в состояние бездействия.
Логику работы неигрового персонажа-медика можно визуализировать с помощью конечного автомата, представленного ниже. Дополнительная информация о конечных автоматах приведена на странице Понятие поведения неигрового персонажа.
Пройдя это руководство, вы узнаете, как с помощью сценария поведения неигрового персонажа создать собственного персонажа-медика, который лечит других персонажей поблизости, когда их здоровье падает ниже определённого порога. Для справки в конце этого руководства приведён полный сценарий.
Создание нового сценария поведения неигрового персонажа
Чтобы начать создавать своего собственного неигрового персонажа-медика, создайте новый сценарий поведения неигрового персонажа с именем medic_example. Дополнительную информацию о создании пользовательского сценария поведения неигрового персонажа см. на странице Создание пользовательского поведения неигрового персонажа. Откройте файл Verse в Visual Studio Code.
Выполните следующие шаги, чтобы создать сценарий поведения неигрового персонажа в UEFN, обеспечивающий появление персонажа-медика, который лечит ближайших игроков.
Реализация очереди лечения
В начале сценария поведения неигрового персонажа задаётся несколько параметров, предназначенных для перемещения персонажа и визуализации отладки. В нашем сценарии все эти параметры не понадобятся, поэтому удалите ненужный код.
-
В начале определения класса
medic_exampleудалите параметры передOnBegin(). Ваш персонаж-медик не будет ждать, чтобы подойти к персонажу, а вместо этого будет следовать за ним во время лечения. В этом примере не нужны отладочные параметры, и вы будете использовать другие объекты для визуализации того, как ваш медик лечит персонажей. -
В
OnBegin(), после получения интерфейсов персонажа в первом оператореif, удалите код в блокеthen. Вашему персонажу-медику не нужно перемещаться между точками после появления. Вместо этого он будет патрулировать вокруг своей точки появления, ожидая персонажей для лечения. -
Удалите функции
DrawDebugLocation()иDrawDebugLookAt(). В этом примере вы не будете использовать отладочные параметры, поэтому вам не нужны и связанные с ними функции.
Удалив ненужный код, вы можете приступить к созданию собственного персонажа-медика.
-
В начале определения класса
medic_exampleдобавьте следующие параметры:-
Редактируемая переменная с плавающей запятой
HealingThreshold. Если здоровье персонажа падает ниже этого порога, он получает лечение.# Порог очков здоровья, которого должен достичь персонаж, чтобы получить лечение. @editable HealingThreshold:float = 50.0 -
Добавьте редактируемую переменную типа float
HealingDelay. Это время ожидания между сеансами лечения при лечении персонажей. Измените его, если ваш медик должен лечить медленнее или быстрее.# Порог очков здоровья, которого должен достичь персонаж, чтобы получить лечение. @editable HealingThreshold:float = 50.0 # Время ожидания перед началом лечения персонажей @editable HealingDelay:float = 1.5 -
Редактируемая переменная с плавающей запятой
HealingAmount. Это количество очков здоровья, восстанавливаемое у персонажа в каждом сеансе лечения. Когда ваш неигровой персонаж-медик лечит персонажа, он восстанавливает у негоHealingAmountочков здоровья каждыеHealingDelayсекунд.# Время ожидания перед началом лечения персонажей @editable HealingDelay:float = 1.5 # Сколько очков здоровья восстанавливается у персонажа в каждом сеансе лечения @editable HealingAmount:float = 5.0 -
Редактируемая зона модификатора
HealVolume. Это область, в которую персонажи должны войти, чтобы получить лечение. В данном примере вы будете использовать зону модификатора, потому что зона модификатора имеет событиеAgentEntersEvent, на которое медик может подписаться и проверять наличие персонажей, нуждающихся в лечении.# Сколько очков здоровья восстанавливается у персонажа в каждом сеансе лечения @editable HealingAmount:float = 5.0 # Область, в которую персонажи должны войти, чтобы получить лечение. @editable HealVolume:mutator_zone_device = mutator_zone_device{} -
Редактируемый генератор визуальных эффектов
VFXSpawner. Визуальная обратная связь позволяет проверить работу кода, поэтому нужно использовать генератор визуальных эффектов, отображаемых при лечении персонажа.# Область, в которую персонажи должны войти, чтобы получить лечение. @editable HealVolume:mutator_zone_device = mutator_zone_device{} # Генератор визуальных эффектов, воспроизводящий визуальные эффекты во время лечения персонажей. @editable VFXSpawner:vfx_spawner_device = vfx_spawner_device {} -
Переменная типа option
agentс именемAgentToFollow. Служит для хранения ссылки на персонажа, за которым должен следовать медик во время лечения.# Генератор визуальных эффектов, воспроизводящий визуальные эффекты во время лечения персонажей. @editable VFXSpawner:vfx_spawner_device = vfx_spawner_device {} # Агент, за которым нужно следовать во время его лечения var AgentToFollow:?agent = false -
Переменная очередь агентов с именем
AgentsToHeal. Если в лечении нуждается несколько персонажей, ваш медик будет лечить их в порядке, в котором они вошли вHealVolume. Код очереди вы напишете на следующем шаге. Дополнительную информацию о структуре данных типа очереди см. в разделе Стеки и очереди в Verse.# Агент, за которым нужно следовать во время его лечения var AgentToFollow:?agent = false # Очередь агентов для лечения в случае, если в область лечения входят несколько агентов. var AgentsToHeal<public>:queue(agent) = queue(agent){} -
Переменная с плавающей запятой
UpdateRateSeconds. Это время ожидания при обновлении положенийHealVolumeиVFXSpawner.# Очередь агентов для лечения в случае, если в область лечения входят несколько агентов. var AgentsToHeal<public>:queue(agent) = queue(agent){} # Определяет частоту обновления положений HealVolume и VFXSpawner UpdateRateSeconds<private>:float = 0.1
-
-
Чтобы реализовать очередь
AgentsToHeal, используйте код, представленный в конце этого шага.- В Проводнике Verse нажмите правой кнопкой мыши по названию своего проекта и выберите Добавить новый файл Verse в проект, чтобы открыть окно Создать сценарий Verse.
-
В окне «Создать сценарий Verse» нажмите Класс Verse, чтобы выбрать его в качестве сценария.
-
Присвойте имя своему классу Verse, изменив текст в поле Имя класса на
queue. -
Нажмите Создать, чтобы создать файл Verse.
-
В проводнике Verse дважды нажмите на файл Verse, чтобы открыть его в Visual Studio Code.
-
Замените код в файле
queueследующим кодом. Этот код реализует общую очередь типаtypeс использованием структуры данных типа списка. Это пример параметрического типа, поскольку реализация очереди будет работать независимо от типа, на основе которого она создана. В данном примере вы будете использовать очередь из персонажей, поэтому определением очереди вmedic_exampleбудетqueue(agent).list(t:type) := class: Data:t Next:?list(t) queue<public>(t:type) := class<internal>: Elements<internal>:?list(t) = false Size<public>:int = 0 Enqueue<public>(NewElement:t):queue(t) = queue(t): Elements := option: list(t): Data := NewElement Next := Elements Size := Size + 1 Dequeue<public>()<decides><transacts>:tuple(queue(t), t) = List := Elements? (queue(t){Elements := List.Next, Size := Size - 1}, List.Data) Front<public>()<decides><transacts>:t = Elements?.Data CreateQueue<public><constructor>(InData:t where t:type) := queue(t): Elements := option: list(t): Data := InData Next := false Size := 1 -
Хранение сценариев Verse в отдельных папках помогает в их организации и позволяет легко ссылаться на часто используемые файлы. В качестве примера создайте папку для хранения поведений ваших неигровых персонажей в этом проекте. В Visual Studio Code нажмите на кнопку Новая папка, чтобы создать новую папку в проекте UEFN. Назовите папку
npc_behaviors. Затем перетащите файл Versemedic_exampleв новую папку.
Теперь ваш код в medic_example должен скомпилироваться правильно.
Лечение персонажей внутри области
Когда раненый персонаж входит в HealVolume, ваш персонаж-медик должен начать лечить его, если здоровье персонажа меньше HealingThreshold. Как только здоровье персонажа превысит HealingThreshold, ваш медик должен прекратить лечение этого персонажа и перейти к следующему персонажу, нуждающемуся в лечении. Если персонажей несколько, ваш медик должен лечить их в том порядке, в каком они вошли в HealVolume. Выполните следующие действия, чтобы лечить персонажей, когда они входят в HealVolume.
-
Вернитесь к файлу
medic_exampleи вOnBegin()после оператораthenначните циклloop. Внутри циклаloopполучите результат функцииDequeue()для очередиAgentsToHealи сохраните его в переменнойDequeueResult.then: loop: # Получите следующего агента в очереди на лечение. Если агент для лечения есть, исцелите его, вызвав AgentToHeal. # Если агентов для лечения нет, подождите, когда агент войдёт в HealVolume if: DequeueResult := AgentsToHeal.Dequeue[] -
Переменная
DequeueResultпредставляет собой кортеж (tuple), который возвращает как копию очередиAgentsToHealс исключённым первым элементом, так и агента в начале очереди. Обновите очередьAgentsToHeal, присвоив ей первое значение из кортежа, и сохраните второе значение какAgentToHeal.if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1) -
Как только у вас появится агент для лечения, его нужно начать лечить, пока он находится в пределах
HealVolume. Для решения этой задачи определите новую функциюHealCharacter(). Добавьте новую функциюHealCharacter()в определение классаmedic_example. Эта функция принимает в качестве аргументовAgentToHealоба интерфейсаNavigatableиFocusableперсонажей-медиков. Добавьте к этой функции модификатор<suspends>, так как при лечении персонажа она должна выполнять несколько асинхронных задач.# Вылечите персонажа, а затем подождите в течение времени HealingDelayAmount. # Завершите лечение, когда здоровье персонажа достигнет HealingThreshold # или персонаж покинет HealVolume. HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void= -
В
HealCharacterпроверьте, находится лиAgentToHealв области, вызвавIsInVolume[]и передав в качестве аргументаAgentToHeal. Если агент находится в области, можно приступать к его лечению. Все пригодные для лечения агенты реализуют интерфейсhealthful, который является частьюfort_characterагента. Получитеfort_characterагента и сохраните его в переменнойCharacterToHeal.HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void= # Лечите персонажа только в том случае, если он находится внутри HealVolume if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[] -
Когда персонаж готов к лечению, нужно убедиться, что медик находится рядом с ним. Создайте цель
navigation_targetна основеAgentToHealс помощьюMakeNavigationTargetи сохраните её в переменнойNavigationTarget. Затем в оператореbranchвызовите функциюNavigateTo(), используя интерфейсnavigatableнеигрового персонажа, чтобы ваш медик переместился кAgentToHeal. Также в функцииbranchвызовите функциюMaintainFocus(), чтобы ваш медик сфокусировался наAgentToHeal. Использование оператораbranchв данном контексте позволяет асинхронно выполнятьNavigateTo()иMaintainFocus(), а также немедленно выполнять любой код послеbranch. Дополнительную информацию о выражениях branch см. на странице branch в Verse.# Лечите персонажа только в том случае, если он находится внутри HealVolume if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[] then: Print("Персонаж находится в области, начинаем лечение") NavigationTarget := MakeNavigationTarget(AgentToHeal) branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) -
Включите
VFXSpawner, чтобы воспроизвести визуальные эффекты во время лечения персонажа медиком. Затем в выраженииdeferотключитеVFXSpawner. Поскольку код отключенияVFXSpawnerнаходится в выраженииdefer, он не запустится до выхода из текущей области видимости. В данной ситуации это означает, что код будет выполняться только при завершении работы функции, поэтому он гарантированно будет последним, что выполнится в функции. Дополнительную информацию о выражениях defer см. на странице defer.branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) VFXSpawner.Enable() defer: VFXSpawner.Disable() -
Лечение
CharacterToHealдолжно прекращаться при выполнении одного из условий. Либо здоровье персонажа восстанавливается до уровня вышеHealingThreshold, либо персонаж выходит изHealVolume. Для реализации такого поведения воспользуйтесь выражениемrace. Запишите выражениеraceдля циклаloopи методаAwait()событияHealVolume.AgentExitsEvent.branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) VFXSpawner.Enable() defer: VFXSpawner.Disable() race: loop: HealVolume.AgentExitsEvent.Await() -
Внутри цикла
loopполучите текущее значение здоровья персонажа с помощьюGetHealth()и сохраните его в переменнойCurrentHealth. Затем в оператореifпроверьте, превышает ли суммаCurrentHealthиHealingAmountзначениеHealingThreshold. Если да, медик должен прекратить лечение и цикл завершается (break). Однако, если текущее здоровье персонажа лишь немного меньше порога лечения, его нужно вылечить до порога лечения. Добавьте второй операторifвнутри первого, который проверяет, меньше лиCurrentHealth, чемHealingThreshold. Если да, установите здоровье персонажа равнымHealingThreshold.race: loop: CurrentHealth := CharacterToHeal.GetHealth() if(CurrentHealth + HealingAmount > HealingThreshold): if (CurrentHealth < HealingThreshold): CharacterToHeal.SetHealth(HealingThreshold) PrintNPCB("Персонаж достиг порога лечения, лечение прекращается") break HealVolume.AgentExitsEvent.Await() -
В противном случае, если сумма
CurrentHealthиHealingAmountне превышаетHealingThreshold, установите в качестве значения здоровья персонажа суммуCurrent HealthиHealingAmount.if(CurrentHealth + HealingAmount > HealingThreshold): if (CurrentHealth < HealingThreshold): CharacterToHeal.SetHealth(HealingThreshold) PrintNPCB("Персонаж достиг порога лечения, лечение прекращается") break else: CharacterToHeal.SetHealth(CurrentHealth + HealingAmount) -
В конце цикла
loopподождите (Sleep) в течение времениHealingDelay. Без этого ожидания персонажи будут лечиться в каждом обновлении симуляции, так чтоHealingDelayне позволит им исцеляться мгновенно. Готовый кодHealCharacter()должен выглядеть следующим образом.# Вылечите персонажа, а затем подождите в течение времени HealingDelayAmount. # Завершите лечение, когда здоровье персонажа достигнет HealingThreshold # или персонаж покинет HealVolume. HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void= # Лечите персонажа только в том случае, если он находится внутри HealVolume if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[] then: Print("Персонаж находится в области, начинаем лечение") NavigationTarget := MakeNavigationTarget(AgentToHeal) branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) VFXSpawner.Enable() defer: VFXSpawner.Disable() race: loop: CurrentHealth := CharacterToHeal.GetHealth() if(CurrentHealth + HealingAmount > HealingThreshold): if (CurrentHealth < HealingThreshold): CharacterToHeal.SetHealth(HealingThreshold) PrintNPCB("Персонаж достиг порога лечения, лечение прекращается") break else: CharacterToHeal.SetHealth(CurrentHealth + HealingAmount) Sleep(HealingDelay) HealVolume.AgentExitsEvent.Await() -
Вернитесь в
OnBegin()и в выраженииthenвнутри циклаloopвызовите функциюHealCharacter(), передав в качестве аргументовAgentToHeal, интерфейсNavigableи интерфейсFocusable.if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1) then: Print("Из очереди получен следующий агент для лечения") HealCharacter(AgentToHeal, Navigatable, Focusable) -
Рядом с вашим медиком не всегда будет персонаж, которого можно вылечить, и функция
Dequeue[]не даст результата, если в очередиAgentsToHealнет агентов. Для обработки такой ситуации добавьте операторelseв конец цикла. Внутри этого оператораifвызовитеSleep()на времяHealingDelay, а затемAwait()для событияHealVolume.AgentEntersEvent. Таким образом, ваш персонаж-медик не будет бесконечно вызыватьDequeue[]для очередиAgentsToHeal, а вместо этого будет ждать, когда новый персонаж войдёт вHealVolume, прежде чем цикл начнётся с начала. Готовый цикл должен выглядеть следующим образом.loop: # Получите следующего агента в очереди на лечение. Если агент для лечения есть, исцелите его, вызвав AgentToHeal. # Если агентов для лечения нет, подождите, когда агент войдёт в HealVolume if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1) then: Print("Из очереди получен следующий агент для лечения") HealCharacter(AgentToHeal, Navigatable, Focusable) else: Print("Очередь AgentsToHeal пуста!") Sleep(HealingDelay) HealVolume.AgentEntersEvent.Await()
Отслеживание нахождения персонажей в области лечения
Чтобы знать, когда персонажи входят в HealVolume или выходят из этой области, подпишите события AgentEntersEvent и AgentExitsEvent области HealVolume на новые функции.
-
Добавьте новую функцию
OnAgentEnters()в определение классаmedic_example. Эта функция принимает в качестве аргумента агента, который только что вошёл вHealVolume, и ставит его в очередьAgentsToHeal.OnAgentEnters(EnteredAgent:agent):void= Print("Агент вошёл в область лечения") -
В
OnAgentEnters()проверьте, что агент в области не является персонажем-медиком. Если это условие выполняется, присвойте очередиAgentsToHealрезультат вызоваEnqueue[]с аргументомEnteredAgent. Готовая функцияOnAgentEnters()должна выглядеть следующим образом:OnAgentEnters(EnteredAgent:agent):void= Print("Агент вошёл в область лечения") if (EnteredAgent <> GetAgent[]): set AgentsToHeal = AgentsToHeal.Enqueue(EnteredAgent) -
Когда агент выходит из
HealVolume, его не нужно удалять из очередиAgentsToHeal. Это связано с тем, что внутри цикла вOnBegin()уже вызываетсяDequeue[]. Однако в иных случаях может потребоваться выполнять код, когда агент выходит из области, поэтому реализуйте соответствующую функцию и в данном примере. Добавьте новую функциюOnAgentExits()в определение классаmedic_example.OnAgentExits(ExitAgent:agent):void= Print("Агент вышел из области лечения") -
В
OnBegin()подпишите событияAgentEntersEventиAgentExitsEventобластиHealVolumeсоответственно наOnAgentEntersиOnAgentExits. Поскольку сначала эта область должна быть отключена, здесь можно вызватьDisable()для генератора персонажей.OnBegin<override>()<suspends>:void= Print("Привет, ИИ!") VFXSpawner.Disable() HealVolume.AgentEntersEvent.Subscribe(OnAgentEnters) HealVolume.AgentExitsEvent.Subscribe(OnAgentExits)
Перемещение области лечения вместе с медиком
Когда персонаж-медик перемещается, область HealVolume должна перемещаться вместе с ним, чтобы соответствовать его текущему местоположению. То же относится и к генератору VFXSpawner. Для этого используем новую функцию DeviceFollowCharacter().
-
Добавьте новую функцию
DeviceFollowCharacter()в определение классаmedic_example. Эта функция должна выполняться асинхронно, чтобы постоянно обновлять положение устройств, поэтому добавьте к ней модификатор<suspends>.DeviceFollowCharacter()<suspends>:void= -
Внутри функции
DeviceFollowCharacter()получитеfort_characterмедика, сначала получив агента с помощьюGetAgent[], а затем вызвавGetFortCharater[].DeviceFollowCharacter()<suspends>:void= if: # Получите агента (ИИ-персонажа), с которым связано это поведение. Agent := GetAgent[] # Получите интерфейс агента fort_character для обращения к поведениям, событиям, функциям и интерфейсам, специфичным для персонажей Fortnite. Character := Agent.GetFortCharacter[] -
Теперь вам нужно непрерывно перемещать
HealVolumeиVFXSpawnerв положениеCharacter. Для этого нужно выполнятьMoveTo()в цикле для обоих устройств. Запустите циклloop, получите преобразованиеCharacterи сохраните его в переменнойCharacterTransform.if: # Получите агента (ИИ-персонажа), с которым связано это поведение. Agent := GetAgent[] # Получите интерфейс агента fort_character для обращения к поведениям, событиям, функциям и интерфейсам, специфичным для персонажей Fortnite. Character := Agent.GetFortCharacter[] then: loop: CharacterTransform := Character.GetTransform() -
Вызовите
MoveTo()как дляVFXSpawner, так и дляHealVolume, переместив их вCharacterTransform.TranslationиCharacterTransform.Rotation. Установите длительностьUpdateRateSecondsсекунд. Наконец, вызовитеSleep()на времяUpdateRateSeconds, чтобы устройства не обновляли своё положение при каждом обновлении симуляции. Обновление положения устройств в каждом обновлении симуляции может вызвать их дрожание. Готовый кодDeviceFollowCharacter()должен выглядеть следующим образом.DeviceFollowCharacter()<suspends>:void= if: # Получите агента (ИИ-персонажа), с которым связано это поведение. Agent := GetAgent[] # Получите интерфейс агента fort_character для обращения к поведениям, событиям, функциям и интерфейсам, специфичным для персонажей Fortnite. Character := Agent.GetFortCharacter[] then: loop: CharacterTransform := Character.GetTransform() VFXSpawner.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds) HealVolume.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds) Sleep(UpdateRateSeconds) -
В
OnBegin(), после оператораif, в котором вы сохраняете интерфейсы персонажей, но перед циклом, создайте экземплярDeviceFollowCharacter().
Добавление персонажа на уровень
-
Создайте новое определение неигрового персонажа с именем Медик. Нажмите на новое определение неигрового персонажа, чтобы открыть экран Определение неигрового персонажа.
-
На экране Определение неигрового персонажа измените следующие свойства:
-
В разделе Тип неигрового персонажа установите для параметра Тип значение Охранник. Интерфейс охранника позволяет обращаться к функциям, специфичным для персонажа-охранника, например к событиям, когда охранник переходит в состояние тревоги или настороженности, а также даёт возможность нанимать охранников для использования в качестве союзников. Также охранники могут использовать оружие, в то время как персонажи типа «Пользовательский» и «Дикое животное» в настоящее время не могут этого делать. Вы также можете изменить имя своего персонажа на вкладке Имя.
-
В разделе Поведение неигрового персонажа установите для параметра Поведение значение Поведение Verse. Затем установите
medic_exampleв качестве Сценария поведения неигрового персонажа. Ваш персонаж по-прежнему сможет обращаться к функциям интерфейса охранника, но будет использовать ваш сценарий Verse, чтобы решать, что делать при возникновении событийOnBeginиOnEnd. -
На вкладке Модификаторы в разделе Модификатор появления охранника нажмите на вкладку Косметический предмет, чтобы изменить внешний вид персонажа. Вы можете выбрать один из существующих косметических предметов или включить Перенос косметических предметов персонажа, чтобы использовать собственную модель. Обратите внимание, использовать перенос косметических предметов персонажа могут только охранники и пользовательские персонажи, а дикие животные не могут. Дополнительную информацию о модификаторах персонажей и о том, какие из них применяются к различным типам персонажей, см. на странице Определение персонажа.
-
-
Сохраните определение своего NPC. В Каталоге ресурсов перетащите определение неигрового персонажа на уровень. При этом будет автоматически создан новый генератор персонажей и ему будет присвоено определение вашего неигрового персонажа.
-
Перетащите на уровень одну зону модификатора и одно устройство «Генератор визуальных эффектов».
-
Выберите свой генератор персонажей. На панели Структура в Пользовательских настройках:
-
В выпадающем меню Переопределить сценарий поведения ИИ выберите свой сценарий
medic_example. Переопределение сценария поведения ИИ на панели «Структура» позволяет обращаться к устройствам на уровне, и эта функция понадобится для назначения HealVolume и VFXSpawner. -
Установите в качестве HealVolume зону модификатора, а в качестве VFXSpawner — генератор визуальных эффектов, который вы разместили на уровне.
-
-
Выберите свою зону модификатора. На панели Структура в Пользовательских настройках установите для параметра Отображение области в игре значение True. Это позволяет визуализировать местоположение области
HealVolumeи её перемещение вместе с персонажем-медиком. -
Выберите свой генератор визуальных эффектов. На панели Структура в Пользовательских настройках выберите свой визуальный эффект с помощью настройки Визуальный эффект. В этом примере лечение сопровождается эффектом Пузыри, но вы можете использовать что-то другое, например фейерверк или искры. Измените визуальный эффект в соответствии с потребностями вашего персонажа.
-
Нажмите Запустить сеанс на панели инструментов UEFN, чтобы выполнить игровой тест уровня. Во время игрового теста ваш персонаж должен лечить раненых персонажей, входящих в зону модификатора. При лечении персонажа должны воспроизводиться визуальные эффекты, а медик должен следовать за исцеляемым персонажем и фокусироваться на нём.
Полный сценарий
Ниже приведён полный сценарий для неигрового персонажа, который лечит персонажей, чьё здоровье падает ниже определённого порога.
medic_example.verse
using { /Fortnite.com/AI }
using { /Fortnite.com/Characters }
using { /Fortnite.com/Devices }
using { /Verse.org/Colors }
using { /Verse.org/Random }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
# Прописанное в Verse поведение NPC, которое можно использовать в определении NPC или переопределении сценария поведения устройства «Генератор персонажей».
medic_example<public> := class(npc_behavior):
# Порог очков здоровья, которого должен достичь персонаж, чтобы получить лечение.
@editable
HealingThreshold:float = 50.0
# Время ожидания перед началом лечения персонажей
@editable
HealingDelay:float = 1.5
# Сколько очков здоровья восстанавливается у персонажа в каждом сеансе лечения
@editable
HealingAmount:float = 5.0
# Область, в которую персонажи должны войти, чтобы получить лечение.
@editable
HealVolume:mutator_zone_device = mutator_zone_device{}
# Генератор визуальных эффектов, воспроизводящий визуальные эффекты во время лечения персонажей.
@editable
VFXSpawner:vfx_spawner_device = vfx_spawner_device {}
# Агент, за которым нужно следовать во время его лечения
var AgentToFollow:?agent = false
# Очередь агентов для лечения в случае, если в область лечения входят несколько агентов.
var AgentsToHeal<public>:queue(agent) = queue(agent){}
# Определяет частоту обновления положений HealVolume и VFXSpawner
UpdateRateSeconds<private>:float = 0.1
OnBegin<override>()<suspends>:void=
VFXSpawner.Disable()
HealVolume.AgentEntersEvent.Subscribe(OnAgentEnters)
HealVolume.AgentExitsEvent.Subscribe(OnAgentExits)
if:
# Получите агента (ИИ-персонажа), с которым связано это поведение.
Agent := GetAgent[]
# Получите интерфейс агента fort_character для обращения к поведениям, событиям, функциям и интерфейсам, специфичным для персонажей Fortnite.
Character := Agent.GetFortCharacter[]
# Получите навигационный интерфейс персонажа, чтобы задать цели, к которым должен перемещаться персонаж.
Navigatable := Character.GetNavigatable[]
# Получите focus_interface персонажа, чтобы задать цели, на которых персонаж должен фокусироваться после перемещения к ним.
Focusable := Character.GetFocusInterface[]
then:
# Настройте HealVolume и VFXSpawner таким образом, чтобы они постоянно следовали за неигровым персонажем
spawn{DeviceFollowCharacter()}
loop:
# Получите следующего агента в очереди на лечение. Если агент для лечения есть, исцелите его, вызвав AgentToHeal.
# Если агентов для лечения нет, подождите, когда агент войдёт в HealVolume
if:
DequeueResult := AgentsToHeal.Dequeue[]
set AgentsToHeal = DequeueResult(0)
AgentToHeal := DequeueResult(1)
then:
PrintNPCB("Из очереди получен следующий агент для лечения")
HealCharacter(AgentToHeal, Navigatable, Focusable)
else:
PrintNPCB("Очередь AgentsToHeal пуста!")
Sleep(HealingDelay)
HealVolume.AgentEntersEvent.Await()
else:
# Если этот код не даёт результата, значит, что-то пошло не так при получении агента и его интерфейсов
PrintNPCB( "Ошибка в сценарии поведения неигрового персонажа при настройке неигрового персонажа",
?Duration := 6.0,
?TextColor := NamedColors.Red )
# Вылечите персонажа, а затем подождите в течение времени HealingDelayAmount.
# Завершите лечение, когда здоровье персонажа достигнет HealingThreshold
# или персонаж покинет HealVolume.
HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void=
# Лечите персонажа только в том случае, если он находится внутри HealVolume
if:
HealVolume.IsInVolume[AgentToHeal]
CharacterToHeal := AgentToHeal.GetFortCharacter[]
then:
PrintNPCB("Персонаж находится в области, начинаем лечение")
NavigationTarget := MakeNavigationTarget(AgentToHeal)
branch:
Navigatable.NavigateTo(NavigationTarget)
Focusable.MaintainFocus(AgentToHeal)
VFXSpawner.Enable()
defer:
VFXSpawner.Disable()
race:
loop:
CurrentHealth := CharacterToHeal.GetHealth()
if(CurrentHealth + HealingAmount > HealingThreshold):
if (CurrentHealth < HealingThreshold):
CharacterToHeal.SetHealth(HealingThreshold)
PrintNPCB("Персонаж достиг порога лечения, лечение прекращается")
break
else:
CharacterToHeal.SetHealth(CurrentHealth + HealingAmount)
Sleep(HealingDelay)
HealVolume.AgentExitsEvent.Await()
# Настройте HealVolume и VFXSpawner таким образом, чтобы они постоянно следовали за персонажем, выполняя в цикле
# функцию MoveTo для положения персонажа.
DeviceFollowCharacter()<suspends>:void=
if:
# Получите агента (ИИ-персонажа), с которым связано это поведение.
Agent := GetAgent[]
# Получите интерфейс агента fort_character для обращения к поведениям, событиям, функциям и интерфейсам, специфичным для персонажей Fortnite.
Character := Agent.GetFortCharacter[]
then:
# Выполните в цикле MoveTo для HealVolume и VFXSpawner, чтобы их положение соответствовало положению
# неигрового персонажа
loop:
CharacterTransform := Character.GetTransform()
VFXSpawner.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds)
HealVolume.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds)
Sleep(UpdateRateSeconds)
# Когда агент входит в HealVolume, добавьте его в
# очередь AgentsToHeal, если он не является неигровым персонажем.
OnAgentEnters(EnteredAgent:agent):void=
PrintNPCB("Агент вошёл в область лечения")
if (EnteredAgent <> GetAgent[]):
set AgentsToHeal = AgentsToHeal.Enqueue(EnteredAgent)
# Когда агент выходит из HealVolume, сохраните данные в журнале с помощью функции PrintNPCB
OnAgentExits(ExitAgent:agent):void=
PrintNPCB("Агент вышел из области лечения")
# Пользовательский адаптер, обеспечивающий продолжительность и цвет по умолчанию.
PrintNPCB(Msg:string,?Duration:float = 3.0, ?TextColor:color = NamedColors.Green):void =
Print("[new_npc_behavior] {Msg}", ?Color := TextColor, ?Duration := Duration)
# Эта функция выполняется, когда неигровой персонаж исчезает или устраняется из мира.
OnEnd<override>():void =
if(Agent := GetAgent[]):
Print(medic_example_message_module.OnEndMessage(Agent))
else:
PrintNPCB("OnEnd")
queue.verse
list(t:type) := class:
Data:t
Next:?list(t)
queue<public>(t:type) := class<internal>:
Elements<internal>:?list(t) = false
Size<public>:int = 0
Enqueue<public>(NewElement:t):queue(t) =
queue(t):
Elements := option:
list(t):
Data := NewElement
Next := Elements
Size := Size + 1
Dequeue<public>()<decides><transacts>:tuple(queue(t), t) =
List := Elements?
(queue(t){Elements := List.Next, Size := Size - 1}, List.Data)
Front<public>()<decides><transacts>:t = Elements?.Data
CreateQueue<public><constructor>(InData:t where t:type) := queue(t):
Elements := option:
list(t):
Data := InData
Next := false
Size := 1
Самостоятельная работа
Изучив это руководство, вы сможете создать персонажа-медика, который автоматически лечит персонажей, здоровье которых падает ниже определённого порога. Используя полученные знания, попробуйте создать собственного персонажа-медика с особым поведением.
-
Сможете ли вы создать медика, который переключается между областями урона и лечения в зависимости от того, находится ли в области враг?
-
А как насчёт медика, который использует для лечения персонажей истощаемый ресурс? Как медик может восстанавливать этот ресурс? Можно ли восстанавливать ресурс просто с течением времени или атакуя врагов?