Медики — это распространённый архетип персонажей во многих играх. Задача медика — лечить ближайших персонажей и помогать своим товарищам по команде восстанавливаться после получения урона. Медики выполняют разные роли в зависимости от игры — например, это могут быть врачи, лечащие пациентов в больнице, боевые медики, которые помогают своей команде сражаться и одновременно лечат, или нейтральные медпункты, которые лечат кого угодно.
Персонаж-медик, которого вы создадите в этом примере, следует набору логических правил.
- Бездействие
- Начать лечить агентов
- Цикл лечения
- Перейти к агенту
Медик начинает игру в состоянии бездействия и патрулирует, пока в область лечения не войдёт агент. Этот агент добавляется в очередь лечения медика. Медику необходимо отслеживать агента, которого нужно вылечить следующим, и очередь — это удобная структура данных для этой цели, поскольку очереди организованы по принципу «первый вошёл, последний вышел». Это означает, что персонаж, который первым войдёт в область лечения, будет вылечен первым.
Как только медик получает агента, которого необходимо вылечить, он сначала проверяет, находится ли здоровье агента ниже порога лечения. Если это так, он начинает лечить его с определённой скоростью до тех пор, пока здоровье агента не достигнет порогового значения или пока агент не выйдет из области лечения. Во время лечения медик старается оставаться рядом с агентом, постоянно следуя за ним. Как только здоровье агента вернётся к пороговому уровню, медик примется за лечение следующего агента и начнёт процесс заново. Если агентов для лечения больше нет, медик вернётся в состояние бездействия.
Логику работы неигрового персонажа-медика можно визуализировать с помощью конечного автомата, представленного ниже. Дополнительная информация о конечных автоматах приведена на странице Понятие поведения неигрового персонажа.

Пройдя это руководство, вы узнаете, как с помощью сценария поведения неигрового персонажа создать собственного персонажа-медика, который лечит других персонажей поблизости, когда их здоровье падает ниже определённого порога. Для справки в конце этого руководства приведён полный сценарий.
Создание нового сценария поведения неигрового персонажа
Чтобы начать создавать своего собственного неигрового персонажа-медика, создайте новый сценарий поведения неигрового персонажа с именем 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
Самостоятельная работа
Изучив это руководство, вы сможете создать персонажа-медика, который автоматически лечит персонажей, здоровье которых падает ниже определённого порога. Используя полученные знания, попробуйте создать собственного персонажа-медика с особым поведением.
-
Сможете ли вы создать медика, который переключается между областями урона и лечения в зависимости от того, находится ли в области враг?
-
А как насчёт медика, который использует для лечения персонажей истощаемый ресурс? Как медик может восстанавливать этот ресурс? Можно ли восстанавливать ресурс просто с течением времени или атакуя врагов?