Bot Pathing
Les bots d'Unreal utilisent un système de jalons, afin de naviguer dans l'espace selon des chemins prédéfinis dans le but de se procurer des objets de plus en plus puissants, le tout en venant à bout des obstacles (objectifs, joueurs) qu'ils rencontrent sur leur chemin. La quasi-totalité des actors nécessaires à la mise en place de ces chemins se trouvent dans l'actor browser en tant que dérivés de la classe NavigationPoint :
Notez d'abord que les PlayerStarts et items (adrénalines, armes, santé, objectifs du jeu etc...) sont d'office considérés comme des jalons. Il n'est donc pas nécessaire d'ajouter un pathnode sur un Playerstart ou sur chaque Weaponbase pour que les bots sachent les utiliser: le jeu ajoute par défaut un InventorySpot invisible à ces endroits, qui permet aux bots de savoir utiliser ces chemins.
PathNode : La pathnode est le premier élément que vous devrez ajouter à votre map. Il s'agit du jalon de base. Réparti de manière homogène
sur toute les surfaces navigables de votre map (tous les sols et tous les endroits où le bot est censé se déplacer), il permet au bot de se tracer
des chemins vers un item. Ainsi, si votre bot se trouve d'un coté de votre map et qu'il désire atteindre un objet de l'autre coté, il va suivre les
pathnode jusqu'à l'item, en empruntant le chemin le plus court, suivant diverses priorités. Le pathnode apparaît sous la forme d'une petite pomme marron. Afin que le moteur puisse relier plusieurs pathnodes, il ne doit pas y avoir plus de 1000 unités unreal entre deux pathnodes. En règle générale, un pathnode tout les 256 ou 512 unités est un bon choix car il permet de donner de nombreuses possibilités au bot sans recouvrir la map et ralentir excessivement le rebuild. La distance doit être plus courte dans les escaliers car les bots ont plus de difficultés à y naviguer.
Il n'est pas nécessaire d'aller chercher le pathnode dans l'actor browser, il se trouve, ainsi que le PlayerStart, dans le menu contextuel de base qui apparaît lors du clic droit:
La plupart du temps, il n'est pas utile d'aller tripoter les options du PathNode. Malgré tout, il existe plusieurs options qui peuvent être utiles dans des cas précis:
- bBlocked: Permet de bloquer le passage. Utilisable à des fins de test.
- bNoSuperSize: Il s'agit en réalité d'une correction de bug. Lorsqu'un bot pilote un Leviathan, le véhicule à tendance à s'identifier comme moins large qu'il ne l'est en réalité. Il risque donc d'essayer d'emprunter un passage trop étroit pour lui et de se retrouver coincé. Si vous constatez ce problème, mettez VRAI dans cette propriété, sur le pathnode ou RoadPathNode se trouvant dans le passage. Les autres véhicules ne seront pas affectés.
- bVehicleDestination: Il s'agit pratiquement de l'inverse du précédent. Si mise à vrai, cette propriété simule un passage très large permettant le passage des véhicules les plus importants, et les attirant vers lui. Utile si vos véhicules refusent de se rendre à un point précis de la map.
- ExtraCost: un "coût" additionnel pour atteindre ce NavigationPoint. Considerez ce champ comme une distance en unités Unreal. Augmenter la valeur d'ExtraCost rend les bots moins susceptibles d'utiliser un chemin. Cela peut permettre d'écarter relativement les bots d'une zone où vous ne voulez pas qu'ils s'aventurent trop fréquemment, mais généralement, cela permet de rendre moins attractif le chemin le plus utilisé. Par exemple, si vous avez deux routes menant à un même point mais que les bots n'en utilisent qu'une, ajouter un ExtraCost à un PathNode stratégique (un que les bots doivent obligatoirement utiliser, par exemple dans un goulet d'étranglement), permet de rééquilibrer l'usage des deux routes en rendant la route la plus utilisée moins attractive. Si les deux chemins sont relativement aussi longs, une valeur de 100 à 200 peut faire l'affaire, mais si l'écart est plus important, des valeurs de 500 voir plus peuvent être nécessaires.
- ForcedPaths et ProscribedPaths: Propriétés très importantes. Si vous entrez dans un des champs "ForcedPaths", le nom d'un pathnode (le nom se situe dans les propriétés, onglet Object => Name), le moteur ajoutera automatiquement un chemin entre les deux points, même si il n'en trouve pas lui-même à la compilation. ProscribedPaths fonctionne de la même façon, mais permet au contraire d'interdire des chemins entre deux points (par exemple si le chemin passe par une zone mortelle).
Il existe deux dérivées du PathNode: RoadPathNode, qui permet de définir des chemins pour les véhicules terrestres, et FlyingPathNode qui permet de définir des routes aux véhicules aériens (et doivent être placés dans le ciel).
Les portes : Par défaut, lorsqu'une porte est placée dans votre niveau, le bot n'a aucun moyen de savoir s'il s'agit d'une porte, d'un pilier qui bouge, d'un piston, d'un objectif utilisé lors d'un évènement scripté etc... En conséquence, les portes bloquent les pathnodes et sont considérées par les bots comme des obstacles infranchissables. Même une fois ouvertes, les chemins ne passent pas par l'ouverture dégagée, car la position fermée bloque les chemins.
Dans les NavigationPoint, il existe un actor dédié à ce problème. Pour en avoir un aperçu, ouvrez la map DM-Flux2, et regardez une des portes.
Les portes de la map sont activées par des triggers et les bots savent les utiliser grâce à l'actor "DOOR" placé au niveau de la porte. Dans cette map, à cause de l'épaisseur du mover, les deux actors se trouvent DANS la porte :
Voila le fonctionnement théorique: Grâce à l'actor Door, le bot considère le mover comme ne gênant pas son passage et emprunte le chemin. Au passage, il pénètre dans le rayon du trigger qui soulève la porte et lui libère physiquement le passage. Notez d'ailleurs qu'un bot ignorera un Mover dans son chemin si bNoAIRelevance est paramétré sur "Vrai" dans les propriétés du Mover.
Si vous ouvrez les propriétés des trois objets (le mover, le door et le trigger), voici ce que vous verrez: Le trigger a pour event "DoorE", qui est aussi le tag du mover. Il a aussi pour tag TrigE. Dans le Door, onglet Door, vous noterez DoorTag: DoorE et DoorTrigger: TrigE. Il suffit en effet de mettre dans DoorTrigger le tag du trigger et dans DoorTag, le tag du mover, pour que les bots sachent utiliser la porte.
Il existe d'autres propriétés dans le Door qui permettent de modifier le fonctionnement:
- bBlockedWhenClosed: Si la porte est fermée, le bot n'a aucun moyen de l'ouvrir et évite le chemin. Dans la majorité des cas, vous ne voudrez pas que cela soit le cas, mais si la porte n'est pas activée par le bot mais par un évènement exterieur (par exemple lors de l'accomplissement d'un objectif du mode Assault, comme dans AS-Glacier), vous devrez mettre ceci à Vrai, sinon les bots tenteront de passer à travers une porte qu'ils n'ont aucun moyen d'ouvrir.
- bInitiallyClosed: Par défaut, la porte est fermée. Cette propriété est par défaut sur Vrai. Le bot considérera la porte comme "Fermée" lorsqu'elle est à sa key 0. Si la porte au contraire est censée se fermer (par exemple dans le cas d'un piège, qui a dit DM-Pressure?), mettez cette propriété à Vrai.
- DoorTag: Mettez ici le tag du mover.
- DoorTrigger: Mettez ici le tag du trigger, si il y en a un. Sinon, laissez vide.
Les Jumpers : Tout droit venus de Quake, les jumpers sont un des éléments les plus ludiques et les plus stratégiques du jeu. Le fonctionnement est simple: Vous, ou le bot, marchez dessus et hop, vous êtes expédié en express sur le point d'arrivée choisi.
Les exemples les plus spectaculaires se trouvent dans des maps comme DM-Plunge ou la (très très bonne) map BR-Skyline.
Contrairement à ce que je pensais quand j'ai commencé à mapper, et à ce que risquent de penser pas mal de gens, le jumper est un point immatériel de l'espace. L'effet de particules, le petit cercle de lumière et le socle spécial sont ajoutés par le mapper pour signaler le jumper, mais ils n'ont aucun lien avec.
Le jumper se trouve dans l'actor browser sous JumpPad. Il s'agit de l'actor nommé "UTJumpPad":
Pour utiliser un jumper, vous avez besoin de deux objets: le UTJumpPad, et un pathnode au point d'arrivée voulu:
Si vous essayez de jouer la map et que vous marchez sur le jumper, vous serez envoyé à la vertical: le Jumper sait qu'il doit vous propulser mais il ne sait pas où. Pour lui donner un point d'arrivée, il suffit de mettre le nom du PathNode dans les ForcedPaths du Jumper (dans mon cas, PathNode0):
Notez que si vous mettez plusieurs ForcedPaths à votre jumper, vous serez envoyé aléatoirement vers n'importe quel point d'arrivée. Le Jumper dosera lui-même la force et la vitesse à laquelle il doit envoyer un joueur pour atteindre la destination. Si pour une raison ou une autre, cela ne marche pas, vous pouvez influer légèrement sur la trajectoire. Par exemple, dans DM-Premaka, j'ai eu un problème, car une grosse décoration au plafond bloquait les joueurs qui se prenaient la tête dessus, et retombaient sans atteindre la destination du jumper.
Dans les propriétés, onglet JumpPad, vous pouvez définir un JumpZModifier. Par défaut, la valeur est "1", et c'est un multiplicateur. Si la trajectoire par défaut de votre JumpPad est deux fois trop basse, vous pouvez la doubler en mettant 2. Si elle est deux fois trop haute, vous pouvez la diminuer en entrant 0.5. Si vous voulez la diviser par 4, entrez 0.25, etc...
L'autre propriété est JumpSound: c'est le son qui sera joué lorsque quelqu'un passera sur le jumper. On met souvent un "BOING" quelconque, cela permet à l'adversaire de repérer le joueur qui passe par le jumper à l'oreille.
AssaultPath : Sans doute l'un des modificateurs d'AI les plus méconnus et pourtant le plus puissant, l'AssaultPath détermine des trajectoires d'assaut pour les équipes, dans les modes à objectifs (par exemple CTF, BR, AS, ONS, etc). À ce sujet, voir ce tutorial-ci.
ShootSpots : Les ShootSpots sont des points qui servent en mode Bombe de balle. Ils permettent de spécifier des points à partir desquels les bots tireront la balle vers le but, lorsqu'ils n'arrivent pas à passer au travers. Vous pouvez en placer plusieurs. Ils n'ont pas de propriété. Placez un BlueShootSpot dans la base bleue pour marquer un endroit où un bot rouge tirera vers le but bleu et vice-versa.
BlockedPath : Permet de bloquer un chemin. Utile à des fins de tests.
Utilisation des ascenceurs : Pour utiliser un ascenceur, vous avez besoin de quatre actors au moins: Le mover, un LiftCenter que vous placerez dessus, un LiftExit à chaque sortie.
LiftCenter : Il fonctionne un peu comme l'actor DOOR, mais pour les ascenceurs. Dans le LiftTag, mettez le tag du mover, et dans le LiftTrigger, le tag de l'éventuel trigger (sinon, laissez vide).
LiftExit : Cet actor marque la sortie de l'ascenceur et fait la jointure entre le réseau de navigation normal et le LiftCenter. Placez-en un à chaque sortie que le bot doit pouvoir utiliser.
Dans l'onglet Lift-Exit, mettez le tag du mover dans Lift-Tag. Le champ SuggestedKeyFrame permet au bot de ne pas faire le sombre abruti en sautant d'en haut sur l'ascenseur, pour essayer d'arriver en bas: mettez y la keyframe à laquelle le mover doit être pour que le Lift-Exit soit utilisable. Pour le cas d'un simple ascenseur à deux Keys :
Il est aussi possible d'utiliser les Lift-Exit et leur onglet LiftJump pour donner aux bots la possibilité de faire un lift jump. Placez simplement un Lift-Exit supplémentaire à l'endroit où le bot doit atterrir, et mettez en Suggested Keyframe la keyframe à partir de laquelle le bot doit sauter (en général la keyframe finale, celle où l'ascenseur est au sommet, dans mon cas la keyframe 1). Dans l'onglet LiftJump, mettez bLiftJumpExit à VRAI. Vous pouvez aussi interdire au bot de faire un double saut au sommet du lift-jump en mettant bNoDoubleJump à Vrai.
Note: Dans DM-Reconstruct, AngelMapper utilise les lift-exit et lift-center d'une manière assez originale :
Ici, il n'y a pas d'ascenseur: Lorsque quelqu'un passe, un pont se construit au fur et à mesure. Le bot doit donc pouvoir savoir qu'il y a un passage, mais il n'y a pas de sens défini, alors on ne met pas de suggested keyframe. L'un des movers qui constituent le pont qui se construit au fur et à mesure a pour tag Bridge202, ce qui indique au bot qu'un mover va bien se mettre en place pour permettre le passage. L'intelligence artificielle ignore simplement les autres triggers et movers, la construction du pont se fait à son insu. Les bots utilisent ce système à la perfection.
JumpSpot: Actor assez polyvalent, il permet d'indiquer à l'intelligence artificielle où sauter et de quelle manière. Il se trouve sous JumpDest dans l'actor browser.
Ses dérivés incluent les GameObjectives qui sont en effet des destinations pour les bots.
Le JumpSpot doit être placé à l'endroit où le bot doit arriver en exécutant un saut, c'est-à-dire par exemple au sommet d'une haute marche que le bot ne peu pas franchir en marchant normalement. Il indique aussi aux bots où envoyer leur balise de téléporteur. Le JumpSpot pose quelques difficultés à l'IA et ne doit donc pas être utilisé sans retenue quand ce n'est pas nécessaire. Pour qu'un bot utilise le JumpSpot comme point d'arrivée d'un saut ou d'une téléportation, il faut mettre son nom dans le ForcedPath des pathnodes qui doivent y conduire.
Ses options permettent de déterminer quel type de saut doit être utilisé par l'IA pour accéder à ce point.
Onglet JumpDest:
- bForceDoubleJump: Force le bot à exécuter un double-saut pour atteindre ce point. Utile si le bot rencontre un problème et n'arrive pas à accéder à la destination avec un saut normal mais n'essaye pas de double-sauter. Attention toutefois: le bot commence son double-jump au niveau du pathnode, au lieu de calculer où le faire pour arriver au sommet. Faites donc attention à ne pas mettre le pathnode trop loin du point de destination, ou le saut échouera.
- bForceAllowDoubleJumping: Certaines situations peuvent présenter un bug qui empêche le bot de faire un double saut. Mettre cette propriété à Vrai peut régler le problème.
- bNeverImpactJump: Si mise à Vrai, le bot ne tentera pas de faire des "impactjumps" (hammerjump dans UT2), c'est à dire des sauts boostés par un coup de shield dans leurs pieds.
- bNoLowGrav: Si mis à Vrai, le JumpSpot sera ignoré en basse gravité.
- bOnlyTranslocator: Si vrai, le point ne sera accessible que par téléporteur.
- TranslocTargetTag: Permet de viser un point précis au téléporteur. Placez ici le Tag de l'actor qui doit être visé.
- TranslocZOffset: Censé permettre d'ajuster la hauteur du tir de la balise. Difficile à jauger. À utiliser si les bots n'arrivent pas à envoyer leur balise au bon endroit (si elle est bloquée par un obstacle comme un plafond bas ou un rebord un peu trop haut).
A noter que selon UnrealWiki, il y a un bug dans la propriété TranslocZOffest, elle fonctionne de manière assez étrange et risque même de ne pas marcher du tout.
Un Jumpspot ne doit être placé qu'au sommet des endroits à atteindre. Steve Polge précise que le spot ne doit être "en bas" que si la chute risque d'être mortelle au bot ou lui causerait trop de dommages, pour le forcer à utiliser un téléporteur.
Playerstart : Le Playerstart est le point de spawn des joueurs et des bots. On y touche rarement, mais voici tout de même ses options:
- bCoopStart: Résidu des actors de UT99 qui eux-même l'ont hérité d'Unreal 1: permet d'utiliser ce playerstart en jeu coopératif.
- TeamNumber Permet de choisir quelle couleur spawn à ce point dans les modes de jeu en équipe (0: rouge, 1: bleu).
- bEnabled Permet de définir si ce PlayerStart est utilisable. Utilisable à des fins de test.
- bPrimaryStart: Mettez sur FAUX si vous voulez utiliser ce point de départ uniquement si tous les autres playerstarts sont bloqués.
- bSinglePlayerStart: Permet d'utiliser ou non ce Playerstart en mode simple joueur.
Le playerstart a deux sous-classes: le triggeredPlayerstart a pour seul différence d'avoir par défaut bEnabled à Faux. Il peut être déclenché par trigger, ce qui mettra bEnabled sur Vrai et le rendra actif. Très utilisé dans les mods Assaut et Onslaught. L'autre sous-classe, xFieldPlayerStart, ne semble pas être utilisée.
Un dernier Cas: Les échelles: Pour créer une échelle, il faut placer un ladderVolume près du mur, qui touche le sol et dépasse de l'arète supérieure d'au moins la hauteur d'un joueur, et orienter le volume vers le mur (avec la propriété walldir). Les LadderVolumes créent automatiquement des points de navigation spéciaux: les actors "Ladder" (équivalent du DOOR actor pour les échelles en fait).
Si un problème survient, désactivez bAutoPath dans les options du ladder volume et placez-les à la main au sommet et au pied de l'échelle. Notons que les bots sont intelligents: ils éviteront d'utiliser une échelle si quelqu'un est déjà en train de l'utiliser (si le volume contient déjà quelqu'un).
Sniper : pour une fois, nous n'utiliserons pas un actor de la classe NavigationPoint, mais une UnrealScriptedSequence.
Placez-la à un endroit où vous voulez que votre bot campe. Elle contient par défaut deux actions: Move_to_point, vide, et une action_waitfortimer, qui est par défaut réglée sur trois secondes (vous pouvez changer), et qui fera attendre le bot en regardant dans la direction vers laquelle pointe l'UnrealScriptedSequence. Des options sont disponibles pour faire un réglage plus fin:
- bDontChangeScript: Si un bot snipe à cet endroit et se prend, par exemple, une roquette dans le dos, il va combattre l'attaquant en se rapellant de ce qu'il faisait, et revenir à ce point après avoir gagné (ou alors crever comme un sale abruti et tout oublier).
- bNotInVehicle: Le point est ignoré si le bot est en véhicule.
- bRoamingScript: Si oui, le bot ne va pas rester là et camper comme un joueur de CS en chaleur, mais va continuer de bouger en ne s'arrétant qu'un court instant pour regarder si quelqu'un passe par-là.
- bSniping: Si oui, le bot va rester là et camper comme un joueur de CS en chaleur.
- EnemyAcquisitionScriptProbability: Chances que le bot soit "happé" par le script, c'est à dire qu'il accomplisse le script en passant a proximité de la ScriptedSequence.
- EnnemyAcquisitionScriptTag: Tag de la scripted sequence à suivre une fois celle-ci accomplie.
- SnipingVolumetag: Vous n'avez jamais révé d'un bot qui ferait du spawnkill (viser les playerstarts de loin) sur Face ? C'est votre jour de chance: En entrant le tag d'un volume dans ce champ, le bot va sniper tout ce qui se trouvera dans ce volume.
- Priority: Si vous avez plusieurs scripts que le bot peut utiliser, vous pouvez leur donner des ordres de priorité.
- WeaponPreference: L'arme que le bot privilégiera en exécutant la séquence. Les trucs habituels s'appliquent: Du Flak dans les couloirs, du sniper sur les grandes distances, etc...
Débuggage
L'AI est un truc assez difficile à tester parce que vous ne pouvez pas vraiment savoir si votre bot a raté son mouvement ou si il a changé d'avis pendant qu'il s'y préparait. Unreal a plusieurs outils pour tester vos réseaux:
Le premier est Tool=>Review path. Cette commande va tester vos chemins pour:
- Tout les points de navigation peuvent être rejoints d'une manière ou d'une autre depuis n'importe quel autre point du niveau (si ce n'est pas le cas, vous avez une cassure dans vos réseaux et les bots ne pourront pas accéder à certaines zones).
- Tous les points de navigation ont des jalons appropriés (sauf si vous avez mis bNOAIRelevance à vrai)
- Aucun point de navigation n'a une valeur de ExtraCost inférieure à zéro.
Par contre, cet outil considère souvent comme des erreurs des trucs parfaitement normaux, alors utilisez-le avec précaution.
Lancez votre map depuis l'éditeur. Vous pouvez ajouter quelques bots avec la commande ADD BOTS X, où X est le nombre de bots à faire apparaitre. Vous pouvez alors utiliser certaines commandes:
- SoakBots : Si un bot a un problème, le jeu se mettra en pause, et vous aurez droit à un log d'erreur.
- ShowDebug : De nombreuses informations en temps réel sur l'état du bot regardé. À utiliser en regardant un bot en mode spectateur.
- ShowAI : Vous montre la "pensée" du bot à chaque instant, ainsi que le chemin qu'il suit.
- ReviewJumpSpots : Spawn un bot avec le skin par défaut (spawn donc un brave Jakob). Ce bot se téléporte automatiquement à chaque point de navigation relié à un JumpSpot, et tente de l'atteindre.
- Si vous tapez ReviewJumpSpots Jump, le bot va essayer d'atteindre les JumpSpots en sautant et en faisant des ImpactJump.
- Si vous tapez ReviewJumpSpots Transloc, le bot va essayer d'atteindre les JumpSpots avec le téléporteur.
- Si vous tapez ReviewJumpSpots LowGrav, le bot va essayerd'atteindre les JumpSpots avec les paraparamès de basse gravité.
- Si vous tapez ReviewJumpSpots Combo, le bot va exécuter le combo d'adrénaline Vitesse et essayer d'utiliser ces sauts décuplés pour atteindre les JumpSpots.
Si vous tapez seulement ReviewJumpSpots, le bot va exécuter les quatre modes de test les un à la suite des autres. D'abord, il tentera de les atteindre au téléporteur, puis une fois tous les JumpSpots testés, il recommencera avec des impact jumps, etc.
Ces tests sont intéressants car vous pouvez voir ce qui ne va pas : un test manqué vous montrera clairement, par exemple, que le bot n'arrive pas à lancer la balise de son téléporteur à cause d'une décoration trop proéminente, etc. De plus, le résultat de tous les essais sera marqué dans le log d'UT2004 (/System/UT2004.log). Pour chaque essai, une ligne apparaitra sous la forme "Test ModeDeSaut from NomDeLaMap.NavigationPointDeDépart to NomDeLaMap.NavigationPointd'Arrivée:
ScriptLog: Test translocation from CTF-Senate.JumpSpot55 to CTF-Senate.JumpSpot21
Ensuite il affichera dans l'idéal la ligne
ScriptLog: Success!
Sinon, il affichera quelque chose comme FAILED. Dans ce cas, regardez l'actor de départ et l'actor d'arrivée. Repérez ces actors dans l'éditeur, et essayer de voir pourquoi ça ne marche pas. Parfois, c'est simplement une erreur de paramétrage, parfois c'est plus complexe. La translocation semble particulièrement difficile parce que les bots gèrent mal la trajectoire de la balise par rapport aux humains.
Lien utile : La présentation des actors par Steve Polge.