Les médecins sont un archétype de personnage courant dans de nombreux jeux. Le rôle du médecin est de soigner les personnages proches et d'aider ses coéquipiers à récupérer après avoir subi des dégâts. Les médecins jouent différents rôles en fonction du jeu, par exemple, les médecins qui soignent les patients dans un hôpital, les médecins de combat qui aident leur équipe à se battre tout en la soignant ou les stations neutres qui soignent tout le monde.
Le personnage médecin que vous allez créer dans cet exemple suit un ensemble de règles logiques.
- Inactif :
- Commencer à soigner les agents
- Boucle de soins
- Atteindre l'agent
Le médecin commence en mode inactif et patrouille jusqu'à ce qu'un agent pénètre dans la zone de soins. Cet agent est ajouté à la file d'attente du médecin. Le médecin doit savoir quel est le prochain agent à soigner, et une file d'attente de type "premier entré, premier sorti" constitue une structure de données utile à cet effet. Le premier personnage à entrer dans la zone de soins sera donc le premier soigné.
Une fois que le médecin a trouvé l'agent qu'il doit soigner, il vérifie d'abord si les PV de l'agent sont inférieurs au seuil de guérison. Si c'est le cas, il commence à le soigner à un rythme spécifique jusqu'à ce que les PV de l'agent atteignent ce seuil ou que l'agent sorte de la zone de soins. Pendant qu'il soigne, le médecin tente de rester proche de l'agent en se dirigeant continuellement vers lui. Une fois le seuil de guérison atteint, le médecin soigne l'agent suivant et recommence le processus. En l'absence d'agent à soigner, le médecin redevient inactif.
Vous pouvez visualiser la logique du PNJ médecin à l'aide de la machine à états finis ci-dessous. Pour plus d'informations sur les machines à états finis, consultez le document Comprendre les comportements de PNJ.
Dans ce guide, vous apprendrez à créer un médecin personnalisé à l'aide du script de comportement de PNJ qui soigne les personnages à proximité lorsque leurs PV sont inférieurs à un certain seuil. Le script complet est inclus à la fin de ce guide à titre de référence.
Création d'un nouveau script de comportement de PNJ
Pour commencer à créer votre PNJ médecin, créez un nouveau script de comportement de PNJ nommé medic_example. Pour plus d'informations sur la création de votre propre script de comportement de PNJ, voir Créer votre propre comportement de PNJ. Ouvrez le fichier Verse dans Visual Studio Code.
Procédez comme suit pour créer un script de comportement de PNJ dans l'UEFN qui fait apparaître un médecin capable de soigner les joueurs à proximité.
Implémentation de la file d'attente de guérison
Le script de comportement de PNJ commence par plusieurs valeurs utilisées pour le déplacement du personnage et la visualisation de débogage. Vous n'aurez pas besoin de toutes ces valeurs dans ce script, c'est pourquoi vous allez maintenant supprimer le code inutile.
-
En haut de la définition de classe
medic_example, supprimez les valeurs avantOnBegin(). Votre médecin n'attendra pas pour se déplacer vers les personnages et les suivra plutôt lorsqu'il les soignera. Vous n'avez pas besoin des valeurs de débogage pour cet exemple, et vous utiliserez d'autres objets pour visualiser le moment où votre médecin soigne les personnages. -
Dans
OnBegin(), après avoir obtenu les interfaces du personnage dans la première instructionif, supprimez le code à l'intérieur de l'instructionthen. Votre médecin n'a pas besoin de faire des boucles entre les points après l'apparition et patrouillera plutôt autour de son point d'apparition en attendant que les personnages soient soignés. -
Supprimez les fonctions
DrawDebugLocation()etDrawDebugLookAt(). Vous n'utiliserez pas les valeurs de débogage dans cet exemple et vous n'avez donc pas besoin des fonctions associées qui les utilisent.
Après avoir supprimé le code inutile, vous pouvez commencer à créer votre médecin.
-
En haut de la définition de la classe
medic_example, ajoutez les valeurs suivantes :-
float modifiable
HealingThreshold. Il s'agit du seuil de PV en dessous duquel les PV des personnages doivent se trouver pour recevoir des soins.# Le seuil de PV qu'un personnage doit atteindre avant d'être soigné. @editable HealingThreshold:float = 50.0 -
Ajoutez un float modifiable
HealingDelay. Il s'agit du temps d'attente entre chaque instance de soins aux personnages. Modifiez cette valeur selon que vous souhaitez que votre médecin soigne lentement ou rapidement.# Le seuil de PV qu'un personnage doit atteindre avant d'être soigné. @editable HealingThreshold:float = 50.0 # Délai d'attente avant de soigner des personnages @editable HealingDelay:float = 1.5 -
Un float modifiable
HealingAmount. Il s'agit de la quantité de PV à restaurer aux personnages par instance de soin. Lorsque votre PNJ médecin soigne un personnage, il restaure une quantitéHealingAmountde PV toutes lesHealingDelaysecondes.# Délai d'attente avant de soigner des personnages @editable HealingDelay:float = 1.5 # Quantité de PV à restaurer aux personnages par instance de soin @editable HealingAmount:float = 5.0 -
Une zone d'altération modifiable
HealVolume. Il s'agit du volume dans lequel les personnages entrent pour recevoir des soins. Vous utiliserez une zone d'altération dans cet exemple, car la zone d'altération a unAgentEntersEventauquel votre médecin peut s'abonner pour vérifier si des personnages ont besoin d'être soignés.# Quantité de PV à restaurer aux personnages par instance de soin @editable HealingAmount:float = 5.0 # Le volume dans lequel les personnages entrent pour recevoir des soins. @editable HealVolume:mutator_zone_device = mutator_zone_device{} -
Un générateur d'effets visuels modifiable
VFXSpawner. Le retour visuel est important pour savoir si votre code fonctionne. C'est pourquoi vous utiliserez un générateur d'effets visuels pour produire des effets lorsqu'un personnage est en train d'être soigné.# Le volume dans lequel les personnages entrent pour recevoir des soins. @editable HealVolume:mutator_zone_device = mutator_zone_device{} # Le générateur qui joue des effets visuels pendant que les personnages sont soignés. @editable VFXSpawner:vfx_spawner_device = vfx_spawner_device {} -
Une variable facultative
agentnomméeAgentToFollow. Elle stocke une référence au personnage que le médecin doit suivre pendant qu'il le soigne# Le générateur qui joue des effets visuels pendant que les personnages sont soignés. @editable VFXSpawner:vfx_spawner_device = vfx_spawner_device {} # L'agent à suivre pendant les soins var AgentToFollow:?agent = false -
Une file d'attente variable d'agents nommée
AgentsToHeal. Si plusieurs personnages ont besoin d'être soignés, votre médecin les soignera en fonction de l'ordre dans lequel ils sont entrés dans leHealVolume. Vous configurerez le code de la file d'attente à l'étape suivante. Pour plus d'informations sur la structure de données de la file d'attente, voir les piles et les files d'attente dans verse.# L'agent à suivre pendant les soins var AgentToFollow:?agent = false # La file d'attente des agents à soigner au cas où plusieurs agents entrent dans le volume de soin. var AgentsToHeal<public>:queue(agent) = queue(agent){} -
Un float variable
UpdateRateSeconds. Il s'agit du temps d'attente entre la mise à jour de la position duHealVolumeet duVFXSpawner.# La file d'attente des agents à soigner au cas où plusieurs agents entrent dans le volume de soin. var AgentsToHeal<public>:queue(agent) = queue(agent){} # Permet de spécifier la fréquence de mise à jour de la position du HealVolume et du VFXSpawner UpdateRateSeconds<private>:float = 0.1
-
-
Pour implémenter la file d'attente
AgentsToHeal, vous utiliserez le code fourni à la fin de cette étape.- De retour dans l'explorateur Verse, faites un clic droit sur le nom de votre projet et choisissez Ajouter un nouveau fichier Verse au projet pour ouvrir la fenêtre Création de script Verse.
-
Dans la fenêtre Création de script verse, cliquez sur Classe Verse pour la sélectionner comme script.
-
Nommez votre classe Verse en remplaçant le texte du champ Nom de la classe par
queue. -
Cliquez sur Créer pour créer le fichier Verse.
-
Dans l'explorateur Verse, double-cliquez sur le nom de votre fichier Verse pour l'ouvrir dans Visual Studio Code.
-
Remplacez le code de votre fichier
queuepar le code suivant. Ce code implémente une file d'attente générique de typetypeen utilisant la structure de données d'une liste. Il s'agit d'un exemple de type paramétrique puisque l'implémentation de la file d'attente fonctionne quel que soit le type à partir duquel vous la créez. Dans votre exemple, vous utiliserez une file d'attente de personnages. La définition de votre file d'attente dansmedic_examplesera doncqueue(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 -
Ordonner vos scripts Verse dans des dossiers distincts peut faciliter l'organisation et permettre de référencer plus facilement les fichiers couramment utilisés. À titre d'exemple, vous allez créer un dossier pour stocker les comportements de vos PNJ dans ce projet. Dans Visual Studio Code, cliquez sur le bouton Ajouter un dossier à l'espace de travail pour créer un nouveau dossier dans votre projet UEFN. Nommez le dossier
npc_behaviors. Faites ensuite glisser votre fichier Versemedic_exampledans le nouveau dossier.
Votre code dans medic_example devrait maintenant compiler correctement.
Guérison des personnages à l'intérieur d'un volume
Lorsqu'un personnage blessé entre dans le HealVolume, votre médecin doit commencer à le soigner si ses PV sont inférieurs au HealingThreshold. Une fois les PV du personnage supérieurs au HealingThreshold, votre médecin doit arrêter de soigner ce personnage et passer au blessé suivant. Dans le cas de personnages multiples, votre médecin doit soigner les personnages dans l'ordre dans lequel ils sont entrés dans le HealVolume. Procédez comme suit pour soigner les personnages lorsqu'ils entrent dans le HealVolume.
-
De retour dans votre fichier
medic_example, dansOnBegin()après l'instructionthen, démarrez uneloop(boucle). Dans laloop, récupérez le résultat de la fonctionDequeue()de la file d'attenteAgentsToHealet enregistrez-le dans une variableDequeueResult.then: loop: # Récupérer le prochain agent de la file d'attente à soigner. En présence d'un agent à soigner, appeler AgentToHeal. # En l'absence d'agent à soigner, attendre qu'un agent entre dans le HealVolume. if: DequeueResult := AgentsToHeal.Dequeue[] -
La variable
DequeueResultest untuplequi renvoie à la fois une copie de la file d'attenteAgentsToHealavec le premier élément supprimé et l'agent en tête de la file d'attente. Mettre à jourAgentsToHealen lui attribuant la première valeur du tuple et enregistrer la seconde valeur commeAgentToHeal.if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1) -
Une fois que vous avez l'agent à soigner, vous devez commencer à le soigner pendant qu'il est dans le
HealVolume. Vous définirez une nouvelle fonction nomméeHealCharacter()pour gérer cela. Ajouter une nouvelle fonction nomméeHealCharacter()à la définition de classemedic_example. Cette fonction prend comme arguments de fonction les interfacesAgentToHeal,NavigableetFocusabledes médecins. Ajoutez le modificateur<suspends>à cette fonction, car elle doit effectuer plusieurs tâches asynchrones pour guérir un personnage.# Soigner le personnage, puis attendre la durée de HealingDelayAmount. # Prend fin lorsque les PV du personnage atteignent le HealingThreshold # ou lorsque le personnage quitte le HealVolume. HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void= -
Dans
HealCharacter, vérifiez queAgentToHealse trouve dans le volume en appelantIsInVolume[]et en transmettantAgentToHealcomme argument. Si l'agent est dans le volume, il peut commencer à recevoir des soins. Tous les agents guérissables implémentent l'interfacehealthful, qui fait partie dufort_characterde l'agent. Récupérez lefort_characterde l'agent et enregistrez-le dans une valeurCharacterToHeal.HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void= # Ne soigner le personnage que s'il se trouve à l'intérieur du HealVolume if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[] -
Lorsque le personnage est prêt à être soigné, vous devez vous assurer que votre médecin reste à proximité de celui-ci. Créez une
navigation_targetà partir d'AgentToHealen utilisantMakeNavigationTargetet enregistrez-la dans une variableNavigationTarget. Ensuite, dans une instructionbranch, appelez la fonctionNavigateTo()en utilisant l'interfacenavigatabledu PNJ pour que votre médecin se dirige vers l'agent à soigner (AgentToHeal). Toujours dans la fonctionbranch, appelez la fonctionMaintainFocus()pour vous assurer que votre médecin se focalise sur l'AgentToHeal. L'utilisation d'une instructionbranchdans ce contexte vous permet d'exécuterNavigateTo()etMaintainainFocus()de manière asynchrone en même temps, mais aussi d'exécuter tout code après votrebranchimmédiatement. Pour plus d'informations sur les expressions de branche, voir la page Les embranchements dans Verse# Ne soigner le personnage que s'il se trouve à l'intérieur du HealVolume if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[] then: Print("Le personnage est dans le volume, début des soins") NavigationTarget := MakeNavigationTarget(AgentToHeal) branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) -
Activez le
VFXSpawnerpour générer des effets visuels lorsque votre médecin soigne un personnage. Ensuite, dans une expressiondefer, désactivez leVFXSpawner. Étant donné que le code pour désactiver leVFXSpawnerse trouve dans une expressiondefer, il ne s'exécutera pas tant que l'étendue actuelle ne sera pas fermée. Dans cette situation, cela signifie que le code ne s'exécutera qu'une fois la fonction terminée. Il est donc garanti que c'est la dernière chose qui se produit dans la fonction. Pour plus d'informations sur les expressionsdefer, voir la page L'expression defer.branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) VFXSpawner.Enable() defer: VFXSpawner.Disable() -
La guérison du
CharacterialToHealdoit s'arrêter lorsqu'une de deux conditions se produit. Soit les PV du personnage sont restaurés au-delà duHealingThreshold, soit le personnage sort duHealVolume. Pour ce faire, vous utiliserez une expressionrace. Configurez une expressionraceentre uneloopet unAwait()sur l'événementHealVolume.AgentExitsEvent..branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) VFXSpawner.Enable() defer: VFXSpawner.Disable() race: loop: HealVolume.AgentExitsEvent.Await() -
À l'intérieur de la
loop, obtenez les PV actuels du personnage en utilisantGetHealth()et enregistrez-les dans une valeurCurrentHealth. Ensuite, dans une instructionif, vérifiez que la somme deCurrentHealthet deHealingAmountest supérieure auHealingThreshold. Si c'est le cas, votre médecin doit cesser de soigner et sortir (break) de la boucle. Cependant, si les PV actuels du personnage sont juste un peu inférieurs au seuil de guérison, il doit être soigné jusqu'au seuil de guérison. Ajoutez une seconde instructionifà l'intérieur de la première pour vérifier siCurrentHealthest inférieur auHealingThreshold. Si tel est le cas, définissez les PV du personnage à la valeur duHealingThreshold.race: loop: CurrentHealth := CharacterToHeal.GetHealth() if(CurrentHealth + HealingAmount > HealingThreshold): if (CurrentHealth < HealingThreshold): CharacterToHeal.SetHealth(HealingThreshold) PrintNPCB("Le personnage a atteint HealingThreshold, arrêt des soins") break HealVolume.AgentExitsEvent.Await() -
Sinon, si la somme de la
CurrentHealthet deHealingAmountn'est pas supérieure auHealingThreshold, les PV du personnage sont fixés à la somme deCurrentHealthet deHealingAmount.if(CurrentHealth + HealingAmount > HealingThreshold): if (CurrentHealth < HealingThreshold): CharacterToHeal.SetHealth(HealingThreshold) PrintNPCB("Le personnage a atteint HealingThreshold, arrêt des soins") break else: CharacterToHeal.SetHealth(CurrentHealth + HealingAmount) -
À la fin de la
loop, entrer en veille pendant la durée deHealingDelay. Sans cette veille, les personnages seront soignés à chaque mise à jour de la simulation. LeHealingDelayempêche de les soigner instantanément. Votre codeHealCharacter()devrait ressembler à ce qui suit.# Soigner le personnage, puis attendre la durée de HealingDelayAmount. # Prend fin lorsque les PV du personnage atteignent le HealingThreshold # ou lorsque le personnage quitte le HealVolume. HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void= # Ne soigner le personnage que s'il se trouve à l'intérieur du HealVolume if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[] then: Print("Le personnage est dans le volume, début des soins") 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("Le personnage a atteint HealingThreshold, arrêt des soins") break else: CharacterToHeal.SetHealth(CurrentHealth + HealingAmount) Sleep(HealingDelay) HealVolume.AgentExitsEvent.Await() -
De retour dans
OnBegin(), dans l'expressionthenà l'intérieur de votreloop, appelezHealCharacter()en transmettant les interfacesAgentToHeal,Navigable, etFocusable.if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1) then: Print("Retrait du prochain agent à soigner de la file d'attente") HealCharacter(AgentToHeal, Navigatable, Focusable) -
Votre médecin n'aura pas toujours un personnage à soigner près de lui, et la fonction
Dequeue[]échouera en l'absence d'agents dans la file d'attenteAgentsToHeal. Pour gérer cela, ajoutez une instructionelseà la fin de laloop. Dans cette instructionif, appelezSleep()pendant la durée deHealingDelay, puisAwait()(attendez) l'événementHealVolume.AgentEntersEvent. Ainsi, votre médecin n'appellera pas indéfinimentDequeue[]sur la file d'attenteAgentsToHealet attendra plutôt qu'un nouveau personnage entre dans leHealVolumeavant de recommencer la boucle. Votre boucle terminée devrait ressembler à ce qui suit.loop: # Récupérer le prochain agent de la file d'attente à soigner. En présence d'un agent à soigner, appeler AgentToHeal. # En l'absence d'agent à soigner, attendre qu'un agent entre dans le HealVolume. if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1) then: Print("Retrait du prochain agent à soigner de la file d'attente") HealCharacter(AgentToHeal, Navigatable, Focusable) else: Print("AgentsToHeal est vide !") Sleep(HealingDelay) HealVolume.AgentEntersEvent.Await()
Suivi des personnages dans le volume de soin
Pour savoir quand des personnages entrent ou sortent du HealVolume, vous allez abonner les fonctions AgentEntersEvent et AgentExitsEvent du HealVolume à de nouvelles fonctions.
-
Ajoutez une nouvelle fonction nommée
OnAgentEnters()à la définition de la classemedic_example. Cette fonction prend l'agent qui vient d'entrer dans leHealVolumeet le place dans la file d'attenteAgentsToHeal.OnAgentEnters(EnteredAgent:agent):void= Print("L'agent est entré dans le volume de soin") -
Dans
OnAgentEnters(), vérifiez que l'agent dans le volume n'est pas le médecin. Si c'est le cas, fixez la file d'attenteAgentsToHealau résultat de l'appel àEnqueue[]avec l'agentEnteredAgent. Votre fonctionOnAgentEnters()devrait ressembler à ce qui suit :OnAgentEnters(EnteredAgent:agent):void= Print("L'agent est entré dans le volume de soin") if (EnteredAgent <> GetAgent[]): set AgentsToHeal = AgentsToHeal.Enqueue(EnteredAgent) -
Lorsqu'un agent quitte le
HealVolume, il n'est pas nécessaire de le retirer de la file d'attenteAgentsToHeal. C'est parce que la boucle dansOnBegin()appelle déjàDequeue[]dans une boucle. Cependant, il se peut que vous souhaitiez exécuter un code lorsqu'un agent quitte le volume dans vos exemples, c'est pourquoi vous allez configurer une fonction à cet effet. Ajoutez une nouvelle fonction nomméeOnAgentExits()à la définition de la classemedic_example.OnAgentExits(ExitAgent:agent):void= Print("L'agent a quitté le volume de soin") -
Dans
OnBegin(), abonnez les événementsAgentEntersEventetAgentExitsEventdeHealVolumeàOnAgentEntersetOnAgentExitsrespectivement. Puisqu'il doit commencer à l'état désactivé, il est adéquat pour l'appel deDisable()sur le générateur de personnages.OnBegin<override>()<suspends>:void= Print("Salut, l'IA !") VFXSpawner.Disable() HealVolume.AgentEntersEvent.Subscribe(OnAgentEnters) HealVolume.AgentExitsEvent.Subscribe(OnAgentExits)
Déplacer le volume de soin avec le médecin
Lorsque le médecin se déplace, le HealVolume doit se déplacer avec lui pour correspondre à sa position actuelle. Il en va de même pour le VFXSpawner. Pour ce faire, vous utiliserez une nouvelle fonction DeviceFollowCharacter().
-
Ajoutez une nouvelle fonction nommée
DeviceFollowCharacter()à la définition de la classemedic_example. Cette fonction doit être exécutée de manière asynchrone pour mettre à jour en permanence les positions de l'appareil, c'est pourquoi il faut lui ajouter le modificateur<suspends>.DeviceFollowCharacter()<suspends>:void= -
Dans la fonction
DeviceFollowCharacter(), récupérez lefort_characterdu médecin en récupérant d'abord l'agent avecGetAgent[], puis en appelantGetFortCharater[].DeviceFollowCharacter()<suspends>:void= if: # Récupérer l'agent (personnage IA) auquel ce comportement est associé. Agent := GetAgent[] # Récupérer l'interface fort_character de l'agent pour accéder aux comportements, événements, fonctions et interfaces spécifiques aux personnages Fortnite. Character := Agent.GetFortCharacter[] -
Vous devez maintenant déplacer continuellement le
HealVolumeet leVFXSpawnerà la position duCharacter. Pour ce faire, vous devez effectuer unMoveTo()en boucle sur les deux appareils. Démarrez uneloop, récupérez la transformation duCharacteret enregistrez-la dans une variableCharacterTransform.if: # Récupérer l'agent (personnage IA) auquel ce comportement est associé. Agent := GetAgent[] # Récupérer l'interface fort_character de l'agent pour accéder aux comportements, événements, fonctions et interfaces spécifiques aux personnages Fortnite. Character := Agent.GetFortCharacter[] then: loop: CharacterTransform := Character.GetTransform() -
Appeler
MoveTo()sur leVFXSpawneret leHealVolume, en les déplaçant versCharacteracterTransform.TranslationetCharacterTransform.Rotation. Fixez la durée àUpdateRateSecondssecondes. Enfin, appelezSleep()pour la durée d'UpdateRateSecondsafin d'empêcher les appareils de mettre à jour leur position à chaque mise à jour de simulation. La mise à jour de la position de l'appareil à chaque mise à jour de simulation peut provoquer des mouvements saccadés sur vos appareils. Votre codeDeviceFollowCharacter()terminé devrait ressembler à ce qui suit.DeviceFollowCharacter()<suspends>:void= if: # Récupérer l'agent (personnage IA) auquel ce comportement est associé. Agent := GetAgent[] # Récupérer l'interface fort_character de l'agent pour accéder aux comportements, événements, fonctions et interfaces spécifiques aux personnages 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) -
Dans
OnBegin(), après l'instructionifoù vous enregistrez vos interfaces de personnages, mais avant la boucle, générez une instance deDeviceFollowCharacter().
Ajouter votre personnage au niveau
-
Créez une nouvelle définition de PNJ nommée Médecin. Cliquez sur votre nouvelle définition de PNJ pour ouvrir l'écran Définition de personnage PNJ.
-
Dans l'écran Définition de personnage PNJ, modifiez les propriétés suivantes :
-
Dans Type de personnage PNJ, définissez Type sur Garde. L'interface des gardes vous permet d'accéder à des fonctionnalités spécifiques aux gardes, telles que des événements lorsque le garde est alerté ou a des soupçons, et vous permet de recruter des gardes à utiliser comme alliés. Les gardes peuvent également s'équiper d'armes, tandis que les personnages de type personnalisé et faune sauvage ne le peuvent actuellement pas. Vous pouvez également modifier le nom de votre personnage dans l'onglet Nom.
-
Sous Comportement de personnage PNJ, définissez Comportement sur Comportement Verse. Ensuite, définissez le Script de comportement de PNJ sur
medic_example. Votre personnage aura toujours accès aux fonctionnalités de l'interface des gardes, mais utilisera votre script Verse pour décider quoi faire pendantOnBeginetOnEnd. -
Dans l'onglet Modificateurs, sous Modificateur d'apparition de garde, cliquez sur l'onglet Cosmétique pour modifier l'apparence cosmétique de votre personnage. Vous pouvez choisir un cosmétique préexistant ou activer Reciblage cosmétique des personnages pour utiliser un modèle personnalisé. Notez que seuls les gardes et les personnages de type personnalisé peuvent utiliser le reciblage cosmétique des personnages, ce qui n'est pas le cas des PNJ de type faune sauvage. Pour plus d'informations concernant les modificateurs de personnage et sur ceux qui s'appliquent aux différents types de personnages, consultez la page (Définitions de PNJ)[npc-character-definitions-in-unreal-editor-for-fortnite].
-
-
Enregistrez votre définition de PNJ. Dans le navigateur de contenu, faites glisser votre définition de PNJ dans le niveau. Cela créera automatiquement un nouveau générateur de personnage et lui attribuera votre définition de personnage.
-
Faites glisser une zone d'altération et un appareil générateur d'effets visuels dans le niveau.
-
Sélectionner votre générateur de personnage. Dans l'organiseur, sous Options utilisateur :
-
Définissez le remplacement de script AIBehavior sur votre script
medic_example. Le remplacement du scriptAIBehaviordans l'organiseur vous permet de référencer des appareils dans le niveau, et vous aurez besoin de cette fonctionnalité pour assigner vos HealVolume et VFXSpawner. -
Définissez HealVolume sur la zone d'altération, et VFXSpawner sur le générateur d'effets visuels que vous avez placé dans le niveau.
-
-
Sélectionnez votre zone d'altération. Dans l'Organiseur, sous Options utilisateur, définissez Zone visible pendant le jeu sur True. Cela vous aidera à visualiser l'emplacement du
HealVolumeet comment il se déplace avec le médecin. -
Sélectionnez votre générateur d'effets visuels. Dans l'Organiseur, sous Options utilisateur, définissez Effet visuel sur l'effet de votre choix. Cet exemple utilise l'effet Bulles pour illustrer les soins, mais vous pourriez utiliser quelque chose de différent, comme des feux d'artifice ou des étincelles. Modifiez l'effet visuel en fonction des besoins de votre personnage.
-
Cliquez sur Lancer la session dans la barre d'outils de l'UEFN pour tester votre niveau. Lors du test, votre personnage devrait soigner les personnages blessés qui entrent dans la zone d'altération. Lors de la guérison d'un personnage, les effets visuels doivent être joués et le médecin doit suivre et se focaliser sur le personnage qu'il soigne.
Script complet
Ce qui suit est un script complet pour un PNJ qui soigne les personnages dont les PV sont inférieurs à un certain seuil.
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 }
# Un comportement de PNJ créé par Verse qui peut être utilisé dans une définition de PNJ ou dans un remplacement de script de comportement de générateur de personnages.
medic_example<public> := class(npc_behavior):
# Le seuil de PV qu'un personnage doit atteindre avant d'être soigné.
@editable
HealingThreshold:float = 50.0
# Délai d'attente avant de soigner des personnages
@editable
HealingDelay:float = 1.5
# Quantité de PV à restaurer aux personnages par instance de soin
@editable
HealingAmount:float = 5.0
# Le volume dans lequel les personnages entrent pour recevoir des soins.
@editable
HealVolume:mutator_zone_device = mutator_zone_device{}
# Le générateur qui joue des effets visuels pendant que les personnages sont soignés.
@editable
VFXSpawner:vfx_spawner_device = vfx_spawner_device {}
# L'agent à suivre pendant les soins
var AgentToFollow:?agent = false
# La file d'attente des agents à soigner au cas où plusieurs agents entrent dans le volume de soin.
var AgentsToHeal<public>:queue(agent) = queue(agent){}
# Permet de spécifier la fréquence de mise à jour de la position du HealVolume et du VFXSpawner
UpdateRateSeconds<private>:float = 0.1
OnBegin<override>()<suspends>:void=
VFXSpawner.Disable()
HealVolume.AgentEntersEvent.Subscribe(OnAgentEnters)
HealVolume.AgentExitsEvent.Subscribe(OnAgentExits)
if:
# Récupérer l'agent (personnage IA) auquel ce comportement est associé.
Agent := GetAgent[]
# Récupérer l'interface fort_character de l'agent pour accéder aux comportements, événements, fonctions et interfaces spécifiques aux personnages Fortnite.
Character := Agent.GetFortCharacter[]
# Récupérer l'interface navigable du personnage pour définir des cibles spécifiques vers lesquelles il doit se rendre.
Navigatable := Character.GetNavigatable[]
# Récupérer l'interface focus_interface du personnage pour définir les cibles spécifiques sur lesquelles se focaliser après les avoir rejointes.
Focusable := Character.GetFocusInterface[]
then:
# Configurer le HealVolume et le VFXSpawner pour qu'ils suivent continuellement le PNJ.
spawn{DeviceFollowCharacter()}
loop:
# Récupérer le prochain agent de la file d'attente à soigner. En présence d'un agent à soigner, appeler AgentToHeal.
# En l'absence d'agent à soigner, attendre qu'un agent entre dans le HealVolume.
if:
DequeueResult := AgentsToHeal.Dequeue[]
set AgentsToHeal = DequeueResult(0)
AgentToHeal := DequeueResult(1)
then:
PrintNPCB("Retrait du prochain agent à soigner de la file d'attente")
HealCharacter(AgentToHeal, Navigatable, Focusable)
else:
PrintNPCB("AgentsToHeal est vide !")
Sleep(HealingDelay)
HealVolume.AgentEntersEvent.Await()
else:
# Si le code en arrive là, c'est que la récupération de l'agent et de ses interfaces a échoué.
PrintNPCB("Erreur dans le script de comportement de PNJ lors de la configuration du PNJ",
?Duration := 6.0,
?TextColor := NamedColors.Red)
# Soigner le personnage, puis attendre la durée de HealingDelayAmount.
# Prend fin lorsque les PV du personnage atteignent le HealingThreshold
# ou lorsque le personnage quitte le HealVolume.
HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void=
# Ne soigner le personnage que s'il se trouve à l'intérieur du HealVolume
if:
HealVolume.IsInVolume[AgentToHeal]
CharacterToHeal := AgentToHeal.GetFortCharacter[]
then:
PrintNPCB("Le personnage est dans le volume, début des soins")
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("Le personnage a atteint HealingThreshold, arrêt des soins")
break
else:
CharacterToHeal.SetHealth(CurrentHealth + HealingAmount)
Sleep(HealingDelay)
HealVolume.AgentExitsEvent.Await()
# Définit le HealVolume et le VFXSpawner pour qu'ils suivent continuellement le personnage grâce à une boucle
# MoveTo sur la position du personnage.
DeviceFollowCharacter()<suspends>:void=
if:
# Récupérer l'agent (personnage IA) auquel ce comportement est associé.
Agent := GetAgent[]
# Récupérer l'interface fort_character de l'agent pour accéder aux comportements, événements, fonctions et interfaces spécifiques aux personnages Fortnite.
Character := Agent.GetFortCharacter[]
then:
# Mettre MoveTo en boucle sur le HealVolume et le VFXSpawner pour faire correspondre leur position à celle du
# Personnage PNJ
loop:
CharacterTransform := Character.GetTransform()
VFXSpawner.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds)
HealVolume.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds)
Sleep(UpdateRateSeconds)
# Lorsqu'un agent entre dans le HealVolume, il est ajouté à la
# file d'attente AgentsToHeal s'il n'est pas le personnage PNJ.
OnAgentEnters(EnteredAgent:agent):void=
PrintNPCB("L'agent est entré dans le volume de soin")
if (EnteredAgent <> GetAgent[]):
set AgentsToHeal = AgentsToHeal.Enqueue(EnteredAgent)
# Lorsqu'un agent quitte le HealVolume, PrintNPCB dans le journal
OnAgentExits(ExitAgent:agent):void=
PrintNPCB("L'agent a quitté le volume de soin")
# Wrapper personnalisé qui fournit une durée et une couleur par défaut.
PrintNPCB(Msg:string,?Duration:float = 3.0, ?TextColor:color = NamedColors.Green):void =
Print("[new_npc_behavior] {Msg}", ?Color := TextColor, ?Duration := Duration)
# Cette fonction s'exécute lorsque le PNJ disparaît ou est éliminé du monde.
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
À vous de jouer
Dans ce guide, vous avez appris à créer un médecin qui soigne automatiquement les personnages dont les PV sont en dessous d'un certain seuil. À l'aide de ce que vous avez appris, essayez de créer votre propre médecin avec ses propres comportements.
-
Pouvez-vous créer un médecin qui alterne entre les volumes de dégât et de soin en fonction de la présence ou non d'un ennemi dans le volume ?
-
Que diriez-vous d'un médecin qui utilise une ressource épuisable pour soigner les personnages ? Comment le médecin pourrait-il restaurer cette ressource ? Pourrait-il la restaurer au fil du temps ou en attaquant des ennemis ?