Para obter o efeito visual cintilante nos infiltradores, você ocultará e mostrará repetidamente o personagem de cada jogador. Isso deve ocorrer em uma função sempre que um infiltrador sofrer dano, mas você também precisa garantir que o restante do código continue em execução quando essa função for chamada. Esse processo fica ainda mais complicado em situações em que há vários infiltradores. Como pode haver vários infiltradores com danos sofridos ao mesmo tempo durante um jogo, você precisará de um código que possa lidar com cada um deles individualmente.
Para conseguir isso, você fará uso intenso da expressão spawn (gerar). Gerar uma função permite executá-la de maneira assíncrona, sem colocar o restante do código em espera. Ao gerar uma função para cada infiltrador, você pode garantir que a cintilação de cada um deles aconteça independentemente do restante.
Siga estas etapas para aprender a fazer o personagem de cada infiltrador piscar quando sofrer dano.
Como criar o loop de cintilação
- Adicione uma nova função FlickerCharacter() à definição de classe invisibility_manager. Essa função usa um fort_character e faz o personagem alternar entre os estados visível e invisível. Adicione o especificador
a esta função para permitir que ela seja executada de maneira assíncrona. ~~~(verse) # Cintila para avisar a visibilidade de um agente, ocultando e mostrando repetidamente seu fort_character FlickerCharacter(InCharacter:fort_character) :void= Logger.Print("FlickerCharacter() invocado") ~~~ - Dentro de FlickerCharacter(), faça loop de Hide() em InCharacter, Sleep() por determinado período (o FlickerRateSeconds que você definiu anteriormente), mostre o personagem com Show() e volte a sleep. Assim, é criado um efeito de cintilação no personagem que permite aos jogadores inimigos rastreá-lo, mas ainda apresenta uma certa dificuldade para mirar devido aos breves períodos de invisibilidade.
# Cintila para avisar a visibilidade de um agente, ocultando e mostrando repetidamente seu fort_character FlickerCharacter(InCharacter:fort_character)<suspends>:void= Logger.Print("FlickerCharacter() invocado") # Loop escondendo e mostrando o personagem para criar um efeito de cintilação. loop: InCharacter.Hide() Sleep(FlickerRateSeconds) InCharacter.Show() Sleep(FlickerRateSeconds)Como interromper o loop
Você precisa de uma maneira de sair dessa função de loop quando for o momento de o personagem parar de piscar. O mapa
PlayerVisibilitySecondsconfigurado anteriormente rastreia por quanto tempo restante um jogador deve piscar. Portanto, você precisará diminuir essa quantidade de tempo a cada loop. Quando o tempo restante atingir 0, o jogador deverá parar de piscar e o loop poderá ser interrompido. - Obtenha a quantidade de tempo que um jogador ainda tem para piscar, acessando o mapa PlayerVisibilitySeconds com o uso de InCharacter.GetAgent[] como chave e armazenando-a em uma variável TimeRemaining. Na verdade, você pode definir o tempo restante no mapa na mesma expressão, diminuindo o valor em FlickerRateSeconds 2. Dessa forma, TimeRemaining se tornará o valor em PlayerVisibilitySeconds depois que a expressão set for resolvida. Observe que FlickerRateSeconds deve ser multiplicado por 2, pois Sleep() é chamada duas vezes por loop.
~~~(verse)
# Cintila para avisar a visibilidade de um agente, ocultando e mostrando repetidamente seu fort_character
FlickerCharacter(InCharacter:fort_character)
:void= Logger.Print("FlickerCharacter() invocado") # Loop escondendo e mostrando o personagem para criar um efeito de cintilação. loop: InCharacter.Hide() Sleep(FlickerRateSeconds) InCharacter.Show() Sleep(FlickerRateSeconds) # A cada loop, diminuor a quantidade de tempo que o personagem pisca em FlickerRateSeconds. # Se o tempo restante atingir 0, sair do loop if: TimeRemaining := set PlayerVisibilitySeconds[InCharacter.GetAgent[]] -= FlickerRateSeconds 2 ~~~ - Verifique se TimeRemaining é menor que ou igual a 0, o que indica que o personagem deve parar de piscar. Para fazer isso, chame Hide() no personagem para torná-lo invisível novamente e use break para sair do loop. A função FlickerCharacter() deve ter a seguinte aparência:
~~~(verse)
# Cintila para avisar a visibilidade de um agente, ocultando e mostrando repetidamente seu fort_character
FlickerCharacter(InCharacter:fort_character)
:void= Logger.Print("FlickerCharacter() invocado") # Loop escondendo e mostrando o personagem para criar um efeito de cintilação. loop: InCharacter.Hide() Sleep(FlickerRateSeconds) InCharacter.Show() Sleep(FlickerRateSeconds) # A cada loop, diminuor a quantidade de tempo que o personagem pisca em FlickerRateSeconds. # Se o tempo restante atingir 0, sair do loop if: TimeRemaining := set PlayerVisibilitySeconds[InCharacter.GetAgent[]] -= FlickerRateSeconds * 2 TimeRemaining <= 0.0 then: InCharacter.Hide() break ~~~
Como reiniciar e redefinir a cintilação
Considere o que acontece quando um infiltrador sofre dano enquanto já está piscando. Nesse caso, gerar outra função FlickerCharacter() pode sair rapidamente do controle, já que cada instância subsequente de dano gera outra função, e pode-se acabar com dezenas de funções atuando no mesmo personagem. Em vez disso, o valor do jogador em PlayerVisibilitySeconds precisa ser redefinido sempre que ele sofrer dano. Para fazer isso, você definirá uma função para verificar se um jogador está piscando. Se estiver, redefina a quantidade de tempo que ele deve piscar. Caso contrário, gere um novo evento de cintilação para esse personagem.
- Adicione uma nova função auxiliar IsFlickering() à definição de classe invisibility_manager, que você usará para determinar se um jogador está piscando. Essa função recebe um agente como argumento e retorna true quando seu valor em PlayerVisibilitySeconds é maior que 0.0. Adicione os especificadores decides e transacts a essa função para torná-la falível e para permitir que seja revertida caso falhe.
~~~(verse)
# Retorna se o jogador ainda tem tempo restante para cintilar
IsFlickering(InAgent:agent)
:void= PlayerVisibilitySeconds[InAgent] > 0.0 ~~~ - Adicione uma nova função StartOrResetFlickering() à definição de classe invisibility_manager. Essa função usa um agente como argumento e determina se um jogador deve iniciar ou redefinir a cintilação.
# Inicia um novo evento cintilador se o agente estiver invisível, caso contrário, # redefine a cintilação contínua do agente. StartOrResetFlickering(InAgent:agent):void= - Em
StartOrResetFlickering(), verifique se o agente em questão não está piscando. Se não estiver, você precisará iniciar um novo evento de cintilação para esse agente. Recupere ofort_characterdesse agente e salve-o em uma variávelFortCharacter. ~~~(verse) # Inicia um novo evento cintilador se o agente estiver invisível, caso contrário, # redefine a cintilação contínua do agente. StartOrResetFlickering(InAgent:agent):void= if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]): Logger.Print("Tentativa de iniciar NOVO FlickerEvent para este personagem") ~~~ - Defina o valor do agente em PlayerVisibilitySeconds como VulnerableSeconds, e use spawn em uma nova função FlickerCharacter() para esse agente, transmitindo seu FortCharacter.
if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]): Logger.Print("Tentativa de iniciar NOVO FlickerEvent para este personagem") # Nova cintilação iniciada if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds): spawn{FlickerCharacter(FortCharacter)} Logger.Print("FlickerEvent gerado para este personagem") - Se o agente já estava piscando, só é preciso redefinir seu valor em PlayerVisibilitySeconds como VulnerableSeconds. Lembre-se de que a função FlickerCharacter() anterior lerá esse valor de maneira assíncrona. Portanto, se o valor for redefinido enquanto FlickerCharacter() estiver em loop, continuará em loop sem break. A função StartOrResetFlickering() deve ter a seguinte aparência: ~~~(verse) # Inicia um novo evento cintilador se o agente estiver invisível, caso contrário, # redefine a cintilação contínua do agente. StartOrResetFlickering(InAgent:agent):void= if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]): Logger.Print("Tentativa de iniciar NOVO FlickerEvent para este personagem") # Nova cintilação iniciada if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds): spawn{FlickerCharacter(FortCharacter)} Logger.Print("FlickerEvent gerado para este personagem") else: # Redefinir a cintilação contínua if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds): Logger.Print("Redefinir o FlickerTimer do personagem para VulnerableSeconds") ~~~
Como fazer os infiltradores piscarem ao sofrerem dano
Para unir todas essas funções, você definirá uma função que lida com o que acontece quando um infiltrador sofre dano. Assim como com FlickerCharacter(), é preciso rastrear cada infiltrador individualmente para determinar se sofreu dano. A função para isso precisa ser assíncrona para que você possa gerar uma para cada infiltrador.
- Adicione uma nova função OnInfiltratorDamaged() à definição de classe invisibility_manager. Essa função usa um agente e manipula a chamada StartOrResetFlickering() quando o agente sofre dano. Adicione o especificador
a esta função para permitir que ela seja executada de maneira assíncrona. ~~~(verse) # Cintila para avisar a visibilidade de um agente sempre que ele recebe dano OnInfiltratorDamaged(InAgent:agent) :void= Logger.Print("Tentativa de iniciar cintilação desse personagem") ~~~ - Obtenha fort_team_collection do espaço de jogo atual e salve-o em uma variável TeamCollection. Em seguida, obtenha fort_character do agente transmitido para essa função.
~~~(verse)
# Cintila para avisar a visibilidade de um agente sempre que ele recebe dano
OnInfiltratorDamaged(InAgent:agent)
:void= Logger.Print("Tentativa de iniciar cintilação desse personagem") TeamCollection := GetPlayspace().GetTeamCollection() if (FortCharacter := InAgent.GetFortCharacter[]): ~~~ - Como essa função precisa monitorar continuamente o agente transmitido para ela, ela precisa entrar em loop. Esse loop deve ser executado sempre que o personagem em questão sofrer dano e chama StartOrResetFlickering no agente que a função monitora. Adicione um loop a OnInfiltratorDamaged.
~~~(verse)
# Cintila para avisar a visibilidade de um agente sempre que ele recebe dano
OnInfiltratorDamaged(InAgent:agent)
:void= Logger.Print("Tentativa de iniciar cintilação desse personagem") TeamCollection := GetPlayspace().GetTeamCollection() if (FortCharacter := InAgent.GetFortCharacter[]): loop: ~~~ - Dentro do loop, verifique se IsVisibilityShared é verdadeiro. Se sim, isso quer dizer que quando um infiltrador sofre dano, todos os infiltradores da equipe devem começar a piscar. Se essa configuração estiver habilitada, obtenha a equipe desse agente e os jogadores dessa equipe por meio de chamadas para GetTeam[] e GetAgents[], respectivamente. ~~~(verse) if (FortCharacter := InAgent.GetFortCharacter[]): loop: if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]): ~~~
- Agora, em um loop for, chame StartOrResetFlickering em todos os companheiros de equipe. ~~~(verse) if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]): # Para cada companheiro de equipe, defina-os em segundos de PlayerVisibility e gere um FlickerEvent for(Teammate:TeamAgents): Logger.Print("Chamando StartOrResetFlickering em um colega de equipe") StartOrResetFlickering(Teammate) ~~~
- Se a visibilidade não for compartilhada, chame
StartOrResetFlickeringno agente que essa função monitora. ~~~(verse) loop: if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]): # Para cada companheiro de equipe, defina-os em segundos de PlayerVisibility e gere um FlickerEvent for(Teammate:TeamAgents): Logger.Print("Chamando StartOrResetFlickering em um colega de equipe") StartOrResetFlickering(Teammate) else: # Fazer apenas o personagem que sofreu dano piscar Logger.Print("Calling StartOrResetFlickering on InAgent") StartOrResetFlickering(InAgent) ~~~ - Finalmente, no final do loop, use Await() para o DamagedEvent() do personagem em questão. Dessa forma, o loop será iterado apenas quando um personagem tiver sofrido dano. Observe que esse loop será executado pelo menos uma vez quando a função iniciar, o que significa pelo menos uma chamada para StartOrResetFlickering(). É por esse motivo que os infiltradores iniciam o jogo piscando e depois ficam invisíveis. Isso ajuda a lembrar aos infiltradores que eles estão invisíveis, mas também que a invisibilidade não é permanente. A função OnInfiltratorDamaged() deve ter a seguinte aparência:
~~~(verse)
# Cintila para avisar a visibilidade de um agente sempre que ele recebe dano
OnInfiltratorDamaged(InAgent:agent)
:void= Logger.Print("Tentativa de iniciar cintilação desse personagem") TeamCollection := GetPlayspace().GetTeamCollection() if (FortCharacter := InAgent.GetFortCharacter[]): loop: if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]): # Para cada companheiro de equipe, defina-os em segundos de PlayerVisibility e gere um FlickerEvent for(Teammate:TeamAgents): Logger.Print("Chamando StartOrResetFlickering em um colega de equipe") StartOrResetFlickering(Teammate) else: # Fazer apenas o personagem que sofreu dano piscar Logger.Print("Calling StartOrResetFlickering on InAgent") StartOrResetFlickering(InAgent) FortCharacter.DamagedEvent().Await() ~~~
Como gerar funções para personagens no início do jogo
Voltando a StartInvisibilityManager(), antes de chamar Hide() no personagem de um jogador, gere uma função OnInfiltratorDamaged() para esse personagem. Dessa forma, cada infiltrador possui uma função que o monitora de forma assíncrona e lida com toda a lógica relacionada à sua cintilação. StartInvisibilityManager() deve ficar assim:
~~~(verse)
StartInvisibilityManager
# Se os jogadores surgirem na equipe de infiltradores, gerar uma função OnInfiltratorDamaged para cada
# um. Em seguida, tornar seu personagem invisível.
for (TeamPlayer:AllPlayers):
if:
FortCharacter:fort_character = TeamPlayer.GetFortCharacter[]
CurrentTeam := GetPlayspace().GetTeamCollection().GetTeam[TeamPlayer]
Logger.Print("Obteve a equipe atual deste jogador")
Teams[0] = CurrentTeam
set PlayerVisibilitySeconds[TeamPlayer] = 0.0
Logger.Print("Jogador adicionado a PlayerVisibilitySeconds")
then:
spawn{OnInfiltratorDamaged(TeamPlayer)}
Logger.Print("Jogador surgiu como um infiltrador, deixando-o invisível")
FortCharacter.Hide()
else:
Logger.Print("Este jogador não é um infiltrador")
~~~
Salve o script, compile-o e clique em Iniciar Sessão na barra de ferramentas do UEFN para testar o nível. Quando você fizer isso, os infiltradores deverão piscar assim que o script começar e, em seguida, ficar invisíveis. Ao sofrerem dano, eles deverão piscar, individualmente ou em equipe, de acordo com o que tiver sido definido em IsVisibilityShared.

Próxima etapa
Na próxima etapa deste tutorial, você aprenderá a lidar com jogadores que ingressam em um jogo em andamento.