Para conseguir el efecto visual de parpadeo en los infiltrados, deberás ocultar y mostrar repetidamente el personaje de cada jugador. Quieres que esto ocurra en una función cada vez que un infiltrado resulte dañado, pero también tienes que asegurarte de que el resto de tu código siga ejecutándose cuando se llame a esta función. Esto se complica aún más en situaciones en las que tienes varios infiltrados. Puede haber varios infiltrados dañados al mismo tiempo durante una partida, por lo que necesitarás un código que pueda manejar cada uno de ellos individualmente.
Para conseguirlo, vas a utilizar mucho la expresión spawn. Generar una función te permite ejecutarla de forma asíncrona, sin poner en espera el resto de tu código. Generando una función para cada infiltrado, puedes asegurarte de que el parpadeo de cada uno se produzca independientemente del resto.
Sigue estos pasos para aprender a hacer que el personaje de cada infiltrado parpadee en caso de daño.
Cómo crear el bucle de parpadeo
- Añade una nueva función
FlickerCharacter()a la definición de la claseinvisibility_manager. Esta función toma unfort_charactery hace parpadear su personaje cuando es visible y cuando no lo es. Añade el especificador<suspends>a esta función para permitir que se ejecute de forma asíncrona.# Parpadea la visibilidad de un agente ocultando y mostrando repetidamente su fort_character. FlickerCharacter(InCharacter:fort_character)<suspends>:void= Logger.Print("FlickerCharacter() invocado") - Dentro de
FlickerCharacter(), haz un bucleHide()sobre elInCharacter,Sleep()durante un cierto tiempo (elFlickerRateSecondsque definiste antes),Show()el personaje, y desactívalo de nuevo. Esto creará un efecto parpadeante en el personaje que permitirá a los jugadores enemigos seguirle la pista, pero seguirán teniendo algunas dificultades para apuntar debido a los breves periodos de invisibilidad.# Parpadea la visibilidad de un agente ocultando y mostrando repetidamente su fort_character. FlickerCharacter(InCharacter:fort_character)<suspends>:void= Logger.Print("FlickerCharacter() invocado") # Haz un bucle ocultando y mostrando el personaje para crear un efecto de parpadeo. loop: InCharacter.Hide() Sleep(FlickerRateSeconds) InCharacter.Show() Sleep(FlickerRateSeconds)Cómo romper el bucle
Necesitas una forma de salir de esta función de bucle cuando el personaje deje de parpadear. El mapa
PlayerVisibilitySecondsque configuraste antes registra la cantidad de tiempo que le queda a un jugador para parpadear, así que tendrás que disminuir esa cantidad de tiempo durante cada bucle. Cuando el tiempo restante llegue a 0, el reproductor debería dejar de parpadear y podrás salir del bucle. - Obtén la cantidad de tiempo que le queda a un jugador para parpadear accediendo al mapa
PlayerVisibilitySecondsutilizandoInCharacter.GetAgent[]como clave y almacenándolo en una variableTimeRemaining. En realidad, puedes establecer la cantidad de tiempo restante en el mapa en la misma expresión, disminuyendo el valor enFlickerRateSeconds * 2. De este modo,TimeRemainingse convertirá en el valor dePlayerVisibilitySecondsdespués de que se resuelva la expresiónset. Ten en cuenta queFlickerRateSecondsdebe multiplicarse por 2, ya que llamas aSleep()dos veces por bucle.# Parpadea la visibilidad de un agente ocultando y mostrando repetidamente su fort_character. FlickerCharacter(InCharacter:fort_character)<suspends>:void= Logger.Print("FlickerCharacter() invocado") # Haz un bucle ocultando y mostrando el personaje para crear un efecto de parpadeo. loop: InCharacter.Hide() Sleep(FlickerRateSeconds) InCharacter.Show() Sleep(FlickerRateSeconds) # En cada bucle, disminuye la cantidad de tiempo que el personaje parpadea en FlickerRateSeconds. # Si el tiempo restante llega a 0, sal del bucle. if: TimeRemaining := set PlayerVisibilitySeconds[InCharacter.GetAgent[]] -= FlickerRateSeconds * 2 - Comprueba si
TimeRemaininges menor o igual que 0, lo que indica qué personaje debe dejar de parpadear. Para ello, llama aHide()en el personaje para hacerlo invisible de nuevo, y sal del bucle (break). Tu funciónFlickerCharacter()debería tener el siguiente aspecto:# Parpadea la visibilidad de un agente ocultando y mostrando repetidamente su fort_character. FlickerCharacter(InCharacter:fort_character)<suspends>:void= Logger.Print("FlickerCharacter() invocado") # Haz un bucle ocultando y mostrando el personaje para crear un efecto de parpadeo. loop: InCharacter.Hide() Sleep(FlickerRateSeconds) InCharacter.Show() Sleep(FlickerRateSeconds) # En cada bucle, disminuye la cantidad de tiempo que el personaje parpadea en FlickerRateSeconds. # Si el tiempo restante llega a 0, sal del bucle. if: TimeRemaining := set PlayerVisibilitySeconds[InCharacter.GetAgent[]] -= FlickerRateSeconds * 2 TimeRemaining <= 0.0 then: InCharacter.Hide() break
Cómo iniciar y restablecer el parpadeo
Piensa en lo que ocurre cuando un infiltrado resulta dañado mientras ya parpadea. En este caso, generar otra función FlickerCharacter() podría descontrolarse rápidamente, ya que cada instancia posterior de daño generaría otra función, y podrías acabar con muchas de funciones actuando sobre el mismo personaje. En su lugar, el valor del jugador en PlayerVisibilitySeconds debe restablecerse cada vez que reciba daño. Para ello, definirás una función que compruebe si un jugador está parpadeando. Si lo están, restablece la cantidad de tiempo que deben parpadear. Si no, genera un nuevo evento de parpadeo para ese personaje.
- Añade una nueva función auxiliar
IsFlickering()a la definición de claseinvisibility_manager, que utilizarás para determinar si un jugador parpadea. Esta función toma un agente como argumento y devuelvetruesi su valor enPlayerVisibilitySecondses mayor que0,0. Añade los especificadoresdecidesytransactsa esta función, tanto para que pueda fallar como para que pueda revertirse en caso de fallo.# Devuelve si al jugador le queda tiempo de parpadeo. IsFlickering(InAgent:agent)<decides><transacts>:void= PlayerVisibilitySeconds[InAgent] > 0.0 - Añade una nueva función
StartOrResetFlickering()a la definición de claseinvisibility_manager. Esta función toma un agente como argumento y determina si un jugador debe iniciar o restablecer el parpadeo.# Inicia un nuevo evento de parpadeo si el agente era invisible, en caso contrario # restablece el parpadeo en curso. StartOrResetFlickering(InAgent:agent):void= - En
StartOrResetFlickering(), comprueba si el agente dado no parpadea. Si no lo hace, tienes que iniciar un nuevo evento de parpadeo para este agente. Recupera elfort_characterde ese agente y guárdalo en una variableFortCharacter.# Inicia un nuevo evento de parpadeo si el agente era invisible, en caso contrario # restablece el parpadeo en curso. StartOrResetFlickering(InAgent:agent):void= if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]): Logger.Print("Intentando iniciar un nuevo FlickerEvent para este personaje") - Establece el valor del agente en
PlayerVisibilitySecondsaVulnerableSeconds, y luegospawnuna nueva funciónFlickerCharacter()para este agente, pasándole suFortCharacter.if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]): Logger.Print("Intentando iniciar un nuevo FlickerEvent para este personaje") # Nuevo parpadeo iniciado if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds): spawn{FlickerCharacter(FortCharacter)} Logger.Print("Se ha generado un FlickerEvent para este personaje") - Si el agente ya parpadeaba, solo tienes que restablecer su valor en
PlayerVisibilitySecondsaVulnerableSeconds. Recuerda que la funciónFlickerCharacter()anterior leerá este valor de forma asíncrona, por lo que si se restablece el valor mientrasFlickerCharacter()está en bucle, continuará el bucle sinbreak. Tu funciónStartOrResetFlickering()debería tener el siguiente aspecto:# Inicia un nuevo evento de parpadeo si el agente era invisible, en caso contrario # restablece el parpadeo en curso. StartOrResetFlickering(InAgent:agent):void= if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]): Logger.Print("Intentando iniciar un nuevo FlickerEvent para este personaje") # Nuevo parpadeo iniciado if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds): spawn{FlickerCharacter(FortCharacter)} Logger.Print("Se ha generado un FlickerEvent para este personaje") else: # Restablece el parpadeo en curso if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds): Logger.Print("Restablece el FlickerTimer del personaje a VulnerableSeconds")
Cómo hacer que los infiltrados parpadeen cuando resulten dañados
Para unir todas estas funciones, vas a definir una función que se encargue de lo que ocurre cuando un infiltrado resulta dañado. Solo que, al igual que con FlickerCharacter(), tienes que rastrear a cada infiltrado individualmente para determinar si ha sido dañado. La función debe ser asíncrona para que puedas generar uno por cada infiltrado.
- Añade una nueva función
OnInfiltratorDamaged()a la definición de claseinvisibility_manager. Esta función toma un agente y se encarga de llamar aStartOrResetFlickering()cuando el agente está dañado. Añade el especificador<suspends>a esta función para permitir que se ejecute de forma asíncrona.# Parpadea la visibilidad de un agente cada vez que recibe daño. OnInfiltratorDamaged(InAgent:agent)<suspends>:void= Logger.Print("Intentando iniciar el parpadeo de este personaje") - Obtén la
fort_team_collectiondel espacio de juego actual y guárdala en una variableTeamCollection. A continuación, obtén elfort_characterpara el agente pasado a esta función.# Parpadea la visibilidad de un agente cada vez que recibe daño. OnInfiltratorDamaged(InAgent:agent)<suspends>:void= Logger.Print("Intentando iniciar el parpadeo de este personaje") TeamCollection := GetPlayspace().GetTeamCollection() if (FortCharacter := InAgent.GetFortCharacter[]): - Como esta función necesita controlar continuamente al agente que se le pasa, debe hacer un bucle. Este bucle debe ejecutarse cada vez que se daña al personaje indicado y llama a
StartOrResetFlickeringen el agente que supervisa la función. Añade un bucle aOnInfiltratorDamaged.# Parpadea la visibilidad de un agente cada vez que recibe daño. OnInfiltratorDamaged(InAgent:agent)<suspends>:void= Logger.Print("Intentando iniciar el parpadeo de este personaje") TeamCollection := GetPlayspace().GetTeamCollection() if (FortCharacter := InAgent.GetFortCharacter[]): loop: - Dentro del bucle, comprueba si
IsVisibilitySharedestrue. Si es así, eso significa que cuando se daña a un infiltrado, todos los infiltrados del equipo deberían empezar a parpadear. Si esta configuración está activada, obtén tanto el equipo de este agente como los jugadores de ese equipo mediante llamadas aGetTeam[]yGetAgents[]respectivamente.if (FortCharacter := InAgent.GetFortCharacter[]): loop: if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]): - Ahora, en un bucle
for, llama aStartOrResetFlickeringen cada Teammate.if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]): # Para cada compañero de equipo, configúralo en PlayerVisibility segundos y genera un FlickerEvent. for(Teammate:TeamAgents): Logger.Print("Llamando a StartOrResetFlickering en un Teammate") StartOrResetFlickering(Teammate) - Si la visibilidad no es compartida, entonces llama a
StartOrResetFlickeringen el agente que supervisa esta función.loop: if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]): # Para cada compañero de equipo, configúralo en PlayerVisibility segundos y genera un FlickerEvent. for(Teammate:TeamAgents): Logger.Print("Llamando a StartOrResetFlickering en un Teammate") StartOrResetFlickering(Teammate) else: # Solo parpadea el personaje dañado Logger.Print("Llamando a StartOrResetFlickering en InAgent") StartOrResetFlickering(InAgent) - Por último, al final del bucle, usa
Await()para esperar elDamagedEvent()del personaje indicado. De este modo, el bucle solo iterará cuando un personaje esté dañado. Ten en cuenta que este bucle se ejecutará al menos una vez cuando se inicie la función, lo que significa al menos una llamada aStartOrResetFlickering(). Por eso, los infiltrados empiezan el juego parpadeando y luego se vuelven invisibles. Esto ayuda a recordar a los infiltrados que son invisibles, pero también que la invisibilidad no es permanente. Tu funciónOnInfiltratorDamaged()debería tener el siguiente aspecto:# Parpadea la visibilidad de un agente cada vez que recibe daño. OnInfiltratorDamaged(InAgent:agent)<suspends>:void= Logger.Print("Intentando iniciar el parpadeo de este personaje") TeamCollection := GetPlayspace().GetTeamCollection() if (FortCharacter := InAgent.GetFortCharacter[]): loop: if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]): # Para cada compañero de equipo, configúralo en PlayerVisibility segundos y genera un FlickerEvent. for(Teammate:TeamAgents): Logger.Print("Llamando a StartOrResetFlickering en un Teammate") StartOrResetFlickering(Teammate) else: # Solo parpadea el personaje dañado Logger.Print("Llamando a StartOrResetFlickering en InAgent") StartOrResetFlickering(InAgent) FortCharacter.DamagedEvent().Await()
Funciones de generación de personajes al inicio de la partida
En StartInvisibilityManager(), antes de llamar a Hide() en el personaje de un jugador, crea una función OnInfiltratorDamaged() para ese personaje. De este modo, cada infiltrado tiene una función que lo supervisa de forma asíncrona y se encarga de toda la lógica relacionada con su parpadeo. StartInvisibilityManager() debería tener el siguiente aspecto:
StartInvisibilityManager<public>(AllTeams:[]team, AllPlayers:[]player, Infiltrators:team):void=
Logger.Print("¡Secuencia de comandos de invisibilidad iniciada!")
set Teams = GetPlayspace().GetTeamCollection().GetTeams()
for(PlayerSpawner:PlayersSpawners):
PlayerSpawner.SpawnedEvent.Subscribe(OnPlayerSpawn)
# Para cada jugador, si ha aparecido en el equipo de infiltrados, crea una función OnInfiltratorDamaged para dicho
# jugador. Luego haz que su personaje sea invisible.
for (TeamPlayer:AllPlayers):
if:
FortCharacter:fort_character = TeamPlayer.GetFortCharacter[]
CurrentTeam := GetPlayspace().GetTeamCollection().GetTeam[TeamPlayer]
Logger.Print("Tengo el equipo actual de este jugador")
Teams[0] = CurrentTeam
set PlayerVisibilitySeconds[TeamPlayer] = 0.0
Logger.Print("Jugador añadido a PlayerVisibilitySeconds")
then:
spawn{OnInfiltratorDamaged(TeamPlayer)}
Logger.Print("El jugador aparece como infiltrado, haciéndolo invisible")
FortCharacter.Hide()
else:
Logger.Print("Este jugador no es un infiltrado")
Guarda la secuencia de comandos, compílala y haz clic en Abrir sesión en la barra de herramientas de UEFN para poner a prueba el nivel. Cuando pruebes tu nivel, cada infiltrado debería parpadear cuando se inicie la secuencia de comandos, y luego volverse invisible. Al recibir daño, deberían parpadear, ya sea individualmente o en equipo, en función de lo que hayas configurado como IsVisibilityShared .

Siguiente paso
En el siguiente paso de este tutorial, aprenderás a manejar a los jugadores que se unen a la partida en curso.