SEO-описание: Сделайте так, чтобы персонаж игрока мерцал при получении урона, используя эффект мерцания. Parent: triad-infiltration-in-verse Order: 6 Type: tutorial Tags: Verse Tags: Многопользовательская игра Tags: Командная игра Tags: Map Tags: Option Tags: Игровое пространство Hero-image: BannerImage.png Topic-image: TopicImage.png Social-image: SocialMedia.png Prereq: triad-infiltration-5-making-players-invisible-in-verse
Чтобы создать визуальный эффект мерцания захватчиков, необходимо сделать так, чтобы персонаж каждого захватчика постоянно то исчезал, то появлялся. Нужно прописать это в функции, отвечающей за получение урона захватчиком, а при вызове этой функции остальная часть кода должна продолжать выполняться. Эта задача ещё сложнее, когда в игре несколько захватчиков. Во время игры урон могут получить сразу несколько захватчиков, поэтому нужен код, способный обрабатывать ситуацию для каждого из них по отдельности.
Для этого нужно активно использовать выражение spawn
. Порождение экземпляра функции с использованием выражения spawn обеспечивает её асинхронное выполнение, при этом выполнение последующего кода не задерживается. Порождение отдельного экземпляра функции для каждого захватчика с помощью spawn обеспечивает мерцание каждого захватчика независимо от других игроков.
Ниже рассмотрен порядок настройки режима мерцания захватчиков при получении урона.
Создание цикла мерцания
- Добавьте новую функцию
FlickerCharacter()
в определении классаinvisibility_manager
. Эта функция принимает аргумент типаfort_character
и обеспечивает мерцание персонажа за счёт его попеременного исчезновения и появления. Добавьте спецификатор<suspends>
к этой функции, чтобы она выполнялась асинхронно.# Обеспечивает мерцание агента, периодически скрывая и отображая его fort_character FlickerCharacter(InCharacter:fort_character)<suspends>:void= Logger.Print("Вызвана функция FlickerCharacter()")
- В функции
FlickerCharacter()
создайте цикл, в котором будет вызыватьсяHide()
дляInCharacter
, затемSleep()
с определённым временем (значение в секундахFlickerRateSeconds
, заданное ранее), затемShow()
и сноваSleep()
. Это создаст эффект мерцания персонажа, который позволит врагам отслеживать его, но с определёнными сложностями из-за коротких периодов невидимости.# Обеспечивает мерцание агента, периодически скрывая и отображая его fort_character FlickerCharacter(InCharacter:fort_character)<suspends>:void= Logger.Print("Вызвана функция FlickerCharacter()") # Попеременное скрытие и отображение персонажа для создания эффекта мерцания. loop: InCharacter.Hide() Sleep(FlickerRateSeconds) InCharacter.Show() Sleep(FlickerRateSeconds)
Выход из цикла
Когда персонаж должен перестать мерцать, нужно выйти из цикла loop в функции. Ассоциативный массив
PlayerVisibilitySeconds
, который вы настроили ранее, отслеживает оставшееся время мерцания игроков, и нужно уменьшать это время на каждой итерации цикла. Когда оставшееся время станет равно нулю, игрок должен перестать мерцать и можно выйти из цикла. - Получите оставшееся время мерцания игрока, обратившись к ассоциативному массиву
PlayerVisibilitySeconds
и указав в качестве ключаInCharacter.GetAgent[]
, и сохраните время в переменнойTimeRemaining
. Фактически вы можете задать оставшееся время в ассоциативном массиве в том же выражении, уменьшив значение наFlickerRateSeconds * 2
. Таким образом, после выполнения выраженияset
переменнаяTimeRemaining
примет значение изPlayerVisibilitySeconds
. Обратите внимание, что нужно умножатьFlickerRateSeconds
на 2, поскольку вы будете вызыватьSleep()
дважды на каждой итерации цикла.# Обеспечивает мерцание агента, периодически скрывая и отображая его fort_character FlickerCharacter(InCharacter:fort_character)<suspends>:void= Logger.Print("Вызвана функция FlickerCharacter()") # Попеременное скрытие и отображение персонажа для создания эффекта мерцания. loop: InCharacter.Hide() Sleep(FlickerRateSeconds) InCharacter.Show() Sleep(FlickerRateSeconds) # В каждом цикле уменьшать время мерцания персонажа на FlickerRateSeconds. # Если оставшееся время достигло 0, выйти из цикла. if: TimeRemaining := set PlayerVisibilitySeconds[InCharacter.GetAgent[]] -= FlickerRateSeconds * 2
- Если значение
TimeRemaining
меньше или равно 0, персонаж должен перестать мерцать. Для этого вызовитеHide()
для персонажа, чтобы вновь сделать его невидимым, и выйдите из цикла с помощью выраженияbreak
. Ваша функцияFlickerCharacter()
должна выглядеть следующим образом:# Обеспечивает мерцание агента, периодически скрывая и отображая его fort_character FlickerCharacter(InCharacter:fort_character)<suspends>:void= Logger.Print("Вызвана функция FlickerCharacter()") # Попеременное скрытие и отображение персонажа для создания эффекта мерцания. loop: InCharacter.Hide() Sleep(FlickerRateSeconds) InCharacter.Show() Sleep(FlickerRateSeconds) # В каждом цикле уменьшать время мерцания персонажа на FlickerRateSeconds. # Если оставшееся время достигло 0, выйти из цикла. if: TimeRemaining := set PlayerVisibilitySeconds[InCharacter.GetAgent[]] -= FlickerRateSeconds * 2 TimeRemaining <= 0.0 then: InCharacter.Hide() break
Запуск и сброс мерцания
Рассмотрим, что произойдёт, если захватчик получит урон, когда он уже мерцает. В этом случае возможен неконтролируемый многократный вызов функции FlickerCharacter()
, так как каждый последующий случай урона будет порождать очередной экземпляр такой функции и в итоге у вас будет множество выполняющихся функций для одного персонажа. Вместо этого нужно сбрасывать значение времени игрока в PlayerVisibilitySeconds
всякий раз, когда игрок получает урон. Для этого определите функцию проверки статуса мерцания игрока. Если он мерцает, нужно сбросить время его мерцания. Если нет, нужно создать новое событие мерцания для этого персонажа.
- Добавьте новую вспомогательную функцию
IsFlickering()
в определении классаinvisibility_manager
для проверки статуса мерцания игрока. Эта функция принимает агент в качестве аргумента и возвращаетtrue
, если значениеPlayerVisibilitySeconds
больше0,0
. Добавьте спецификаторыdecides
иtransacts
к этой функции, чтобы сделать из неё функцию с возможным отсутствием результата и она могла откатываться при отсутствии результата.# Возвращает информацию о том, осталось ли у игрока время мерцания IsFlickering(InAgent:agent)<decides><transacts>:void= PlayerVisibilitySeconds[InAgent] > 0.0
- Добавьте новую функцию
StartOrResetFlickering()
в определении классаinvisibility_manager
. Эта функция принимает агент в качестве аргумента и определяет, нужно ли запустить или сбросить мерцание игрока.# Создаёт новое событие мерцания, если агент был невидим, в противном случае # сбрасывает текущее мерцание агента. StartOrResetFlickering(InAgent:agent):void=
- В
StartOrResetFlickering()
проверьте, не мерцает ли указанный агент. Если нет, нужно инициировать для него новое событие мерцания. Получите значениеfort_character
для этого агента и сохраните его в переменнойFortCharacter
.# Создаёт новое событие мерцания, если агент был невидим, в противном случае # сбрасывает текущее мерцание агента. StartOrResetFlickering(InAgent:agent):void= if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]): Logger.Print("Пытаемся инициировать НОВОЕ событие FlickerEvent для этого персонажа")
- Задайте для агента значение
VulnerableSeconds
вPlayerVisibilitySeconds
, а затем породите (spawn
) для него новый экземпляр функцииFlickerCharacter()
, передав в неёFortCharacter
.if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]): Logger.Print("Пытаемся инициировать НОВОЕ событие FlickerEvent для этого персонажа") # Запущено новое мерцание if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds): spawn{FlickerCharacter(FortCharacter)} Logger.Print("Сгенирировано событие FlickerEvent для этого персонажа")
- Если агент уже и так мерцал, вам нужно просто сбросить его время в
PlayerVisibilitySeconds
наVulnerableSeconds
. Обратите внимание, что функцияFlickerCharacter()
, порождённая ранее, будет асинхронно считывать это значение, поэтому если оно будет сброшено во время выполненияFlickerCharacter()
в цикле, цикл продолжится без прерывания (break
). Ваша функцияStartOrResetFlickering()
должна выглядеть следующим образом:# Создаёт новое событие мерцания, если агент был невидим, в противном случае # сбрасывает текущее мерцание агента. StartOrResetFlickering(InAgent:agent):void= if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]): Logger.Print("Пытаемся инициировать НОВОЕ событие FlickerEvent для этого персонажа") # Запущено новое мерцание if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds): spawn{FlickerCharacter(FortCharacter)} Logger.Print("Сгенирировано событие FlickerEvent для этого персонажа") else: # Сбросить текущее мерцание if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds): Logger.Print("Сбросим FlickerTimer персонажа на VulnerableSeconds")
Мерцание захватчиков при получении урона
Чтобы связать все эти функции воедино, нужно написать функцию, которая будет обрабатывать нанесение урона захватчику. Точно так же как в случае с FlickerCharacter()
, нужно отслеживать каждого захватчика по отдельности, чтобы определить, был ли ему нанесён урон. Для этого нужна асинхронная функция, чтобы можно было порождать отдельный экземпляр функции для каждого захватчика.
- Добавьте новую функцию
OnInfiltratorDamaged()
в определении классаinvisibility_manager
. Эта функция принимает агент и вызываетStartOrResetFlickering()
при нанесении урона этому агенту. Добавьте спецификатор<suspends>
к этой функции, чтобы она выполнялась асинхронно.# Обеспечивает мерцание агента всякий раз, когда он получает урон OnInfiltratorDamaged(InAgent:agent)<suspends>:void= Logger.Print("Пытаемся инициировать мерцание этого персонажа")
- Получите набор команд
fort_team_collection
в текущем игровом пространстве и сохраните его в переменнойTeamCollection
. Затем получите значениеfort_character
для агента, переданного в эту функцию.# Обеспечивает мерцание агента всякий раз, когда он получает урон OnInfiltratorDamaged(InAgent:agent)<suspends>:void= Logger.Print("Пытаемся инициировать мерцание этого персонажа") TeamCollection := GetPlayspace().GetTeamCollection() if (FortCharacter := InAgent.GetFortCharacter[]):
- Поскольку эта функция должна непрерывно отслеживать переданный в неё агент, она должна выполняться в цикле. Этот цикл должен выполняться всякий раз, когда указанный персонаж получает урон, и в нём должен производиться вызов
StartOrResetFlickering
для агента, отслеживаемого функцией. Добавьте цикл вOnInfiltratorDamaged
.# Обеспечивает мерцание агента всякий раз, когда он получает урон OnInfiltratorDamaged(InAgent:agent)<suspends>:void= Logger.Print("Пытаемся инициировать мерцание этого персонажа") TeamCollection := GetPlayspace().GetTeamCollection() if (FortCharacter := InAgent.GetFortCharacter[]): loop:
- Внутри цикла проверьте, задано ли для
IsVisibilityShared
значение true. Если да, то при нанесении урона захватчику должны начать мерцать все захватчики в этой команде. Если эта настройка включена, получите команду данного агента и игроков в этой команде, вызвавGetTeam[]
иGetAgents[]
.if (FortCharacter := InAgent.GetFortCharacter[]): loop: if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]):
- Затем в цикле
for
вызовитеStartOrResetFlickering
для каждого участника команды (Teammate).if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]): # Для каждого участника команды задать PlayerVisibilitySeconds и создать FlickerEvent for(Teammate:TeamAgents): Logger.Print("Вызываем StartOrResetFlickering для Teammate") StartOrResetFlickering(Teammate)
- Если мерцание не распространяется на всех захватчиков, вызовите
StartOrResetFlickering
для агента, отслеживаемого этой функцией.loop: if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]): # Для каждого участника команды задать PlayerVisibilitySeconds и создать FlickerEvent for(Teammate:TeamAgents): Logger.Print("Вызываем StartOrResetFlickering для Teammate") StartOrResetFlickering(Teammate) else: # Сделать мерцающим только персонаж, получивший урон Logger.Print("Вызываем StartOrResetFlickering для InAgent") StartOrResetFlickering(InAgent)
- Наконец, в конце цикла нужно с помощью метода
Await()
дождаться событияDamagedEvent()
для указанного персонажа. Таким образом цикл повторяется только в случае нанесения урона персонажу. Обратите внимание, что этот цикл выполнится хотя бы раз в момент запуска функции, т. е. функцияStartOrResetFlickering()
тоже будет вызвана как минимум один раз. По этой причине захватчики начинают игру в режиме мерцания, а затем становятся невидимыми. Это напоминает им не только о том, что они невидимы, но и о том, что эта невидимость не постоянна. Ваша функцияOnInfiltratorDamaged()
должна выглядеть следующим образом:# Обеспечивает мерцание агента всякий раз, когда он получает урон OnInfiltratorDamaged(InAgent:agent)<suspends>:void= Logger.Print("Пытаемся инициировать мерцание этого персонажа") TeamCollection := GetPlayspace().GetTeamCollection() if (FortCharacter := InAgent.GetFortCharacter[]): loop: if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]): # Для каждого участника команды задать PlayerVisibilitySeconds и создать FlickerEvent for(Teammate:TeamAgents): Logger.Print("Вызываем StartOrResetFlickering для Teammate") StartOrResetFlickering(Teammate) else: # Сделать мерцающим только персонаж, получивший урон Logger.Print("Вызываем StartOrResetFlickering для InAgent") StartOrResetFlickering(InAgent) FortCharacter.DamagedEvent().Await()
Порождение экземпляров функций для персонажей в начале игры
Также в StartInvisibilityManager()
перед вызовом Hide()
для персонажа игрока необходимо породить для него экземпляр функции OnInfiltratorDamaged()
. Так у каждого захватчика будет функция, отслеживающая его асинхронно и отвечающая за все логические операции, связанные с его мерцанием. Функция StartInvisibilityManager()
должна выглядеть следующим образом:
StartInvisibilityManager<public>(AllTeams:[]team, AllPlayers:[]player, Infiltrators:team):void=
Logger.Print("Сценарий установки невидимости запущен!")
set Teams = GetPlayspace().GetTeamCollection().GetTeams()
for(PlayerSpawner:PlayersSpawners):
PlayerSpawner.SpawnedEvent.Subscribe(OnPlayerSpawn)
# Для каждого игрока, появившегося в команде захватчиков, создать отдельный экземпляр функции
# OnInfiltratorDamaged. Затем сделать его персонаж невидимым.
for (TeamPlayer:AllPlayers):
if:
FortCharacter:fort_character = TeamPlayer.GetFortCharacter[]
CurrentTeam := GetPlayspace().GetTeamCollection().GetTeam[TeamPlayer]
Logger.Print("Текущая команда этого игрока определена")
Teams[0] = CurrentTeam
set PlayerVisibilitySeconds[TeamPlayer] = 0.0
Logger.Print("Игрок добавлен в PlayerVisibilitySeconds")
then:
spawn{OnInfiltratorDamaged(TeamPlayer)}
Logger.Print("Игрок появился в качестве захватчика, делаем его невидимым")
FortCharacter.Hide()
else:
Logger.Print("Этот игрок не захватчик")
Сохраните сценарий, скомпилируйте его и нажмите Запуск сеанса на панели инструментов UEFN, чтобы выполнить игровой тест уровня. Во время игрового теста уровня каждый захватчик должен мерцать при запуске сценария, а затем становиться невидимым. После получения урона он (или вся команда, в зависимости от значения IsVisibilityShared
) должен начать мерцать.

Что дальше
Дальше вы узнаете, что делать с игроками, присоединяющимися по ходу игры.