Les gardes portant des torches
Patrouilles et Navmesh
Comment montrer si un AI est inconscient
ou mort
Personnalisation d'AI – Exemple d'un Elément
de feu (Fire Elemental)
Ce tutorial a pour but de vous montrer comment ajouter un nouveau type d'IA au bestiaire de Deadly Shadows. On va prendre l'exemple de création d'un élément de feu, les méthodes sont les mêmes pour n'importe quel créature (du moment que les caractéristiques voulues ne dépassent pas les limites du moteur du jeu). Tous les sujets ne sont pas couverts ici. On ne va pas non plus ajouter de nouveaux meshes, des textures ni des animations. Au lieu de cela, on va plutôt se concentrer sur ce qui est déjà disponible ; ce qui est déjà pas mal.
Ce tutorial est pour les débutants. Il présente la recette complète pour faire un élément de feu qui marche, son but principal est de montrer de quoi est fait un AI.
Le design
Si vous avez déjà quelque expérience avec le design de vos propres modifications, ce conseil va vous paraître très évident mais il est si important qu'il devrait être dit à chaque fois : ne commencez jamais en ouvrant en premier l'éditeur. Prenez plutôt un crayon et une feuille de papier, asseyez-vous et répondez à quelques questions (L'ordre n'est pas obligatoire) :
1. A quoi devrait ressembler
votre créature ? Essayez de le dessiner ou d'écrire une petite description
(pas la peine de faire un chef d'œuvre, c'est seulement pour vous).
2. Quel est son rôle dans votre mission ou
votre campagne ? Est-ce un ennemi, un commerçant ou un piéton ? ou alors
peut-être le boss super méchant de fin de niveau ?
3. Décrivez-le en quelques mots ? Est-il dangereux
? Mystérieux ? Fait-il peur ? Amusant ? Surnaturel ? Intelligent ou abruti
? Est-il intellectuel ou instinctif ? Agressif ou lâche ? Bon ou méchant
? (ou peut-être comme Garrett, quelque part entre les deux)
4. Si c'est un ennemi, quelles sont ses qualités
? En d'autres termes, quels dangers représente-t-il pour le personnage-joueur
?
5. Et quels sont ses points faibles ? Comment
le joueur devra-t-il le vaincre ? Ou alors on ne peut le vaincre de manière
normale ? Est-il facile ou difficile de s'en défaire ?
6. Qu'est-ce qui rend cette créature différente
des autres ? Rappelez-vous, vous avez l'entier réglage à votre disposition.
Si vous avez besoin d'une bande de fanatiques religieux incorruptibles alors
vous n'aurez pas besoin de créer un nouveau groupe. Les Marteleurs feront
l'affaire.
7. Est-ce que cela correspond au contexte
? Par exemple, les références directes à des lieux, personnages, événements
réels sont déplacées. Alors que des extrapolations sur un ordre religieux
sont habituellement plus logiques.
Voilà des questions importantes qui vous forcent à prendre des décisions, mais ne vous y habituez pas trop. Pendant la réalisation, il se peut que quelque chose ne marche pas comme prévu ou même pas du tout. Le moteur ne supporte pas beaucoup de choses et d'autres requièrent trop d'efforts. En général, on doit séparer les conditions (les questions posées au dessus) à la réalisation (Sujet de ce tutoriel). Il peut être aussi bien de prendre en compte les limites techniques pendant la phase de réalisation. Par exemple, si vous n'avez pas les moyens de créer un nouveau mesh, alors votre créature est sûrement de type humanoïde.
Nous allons mettre en pratique ce que nous avons écrit pour notre IA personnalisé. Nous avons choisi un élément de Feu, remplissons les autres détails.
1. En tant qu'élément de
feu, il est fait exclusivement de… feu. Comme nous n'utilisons pas de mesh
personnalisé, nous savons qu'il a une vague apparence humanoïde. Il diffuse
de la lumière orangée, il émet des sons de craquements et de feu. Des sons
additionnels sont émis quand il est en colère ou lorsqu'il remarque quelque
chose d'inhabituel. Quand il entre ou se prépare au combat, un son plus
fort encore se fait entendre. Pendant le combat, il diffuse plus de lumière
que d'habitude ; c'est un signe d'agitation.
2. Il n'y a pas beaucoup d'éléments de feu
dans la Cité. Ils sont violents, détestent négocier et il est déconseillé
de s'allier avec eux. Ils ne sont pas doués de conscience, ni sociaux, ils
sont plus puissants que les autres créatures, il ne faut donc pas en mettre
beaucoup. On veut aussi que le joueur les combatte un par un. L'élément
de feu peut être un bon gardien de trésor ou surveiller un point de passage
important (comme un pont unique au dessus d'une rivière de lave)
3. Ils sont agressifs et sans conscience.
Ils sont effrayants car ils peuvent brûler un humain se tenant à côté d'eux.
Ce sont des créatures mystérieuses. Elles sont surnaturelles et de ce fait,
liées à des affaires surnaturelles. Elles sont le témoin de quelque chose
de magique aux alentours et aussi d'un potentiel de richesses (au moins
pour quelqu'un).
4. Les éléments de feu attaquent avec leurs
"membres" mais ce qui les rend dangereux est leur température. Ils n'ont
pas besoin de toucher Garrett pour lui faire du mal, le simple fait d'être
près d'eux est fatal. Ils ont leur propre source de lumière, il est difficile
de se cacher.
5. Les éléments de feu sont invulnérables
à la plupart des armes, ils ne peuvent pas être assommés mais on peut les
blesser avec de l'eau ou des flèches à eau. Le joueur devra donc combattre
ce genre de créatures à distance à cause de leur capacité à projeter de
la lumière et à brûler. On rendra l'élément de feu plus lent que les autres
créatures ainsi le joueur aura la possibilité de le contourner.
6. Notre nouveau IA a un panel de vulnérabilités
différent des humains et demande une autre méthode de furtivité de la part
du joueur (du genre "Je vais me placer ici et le sniper avec une de mes
flèches" ou alors "Je vais trouver un petit chemin et le contourner"). Il
faut aussi le placer dans un contexte spécifique : personne ne pourrait
voir un garde ordinaire en train de surveiller une cité antique en ruines
complètement oubliée.
7. Les éléments de feu ont fait leur première
apparition dans Dark Project. Ils font partie de la mythologie du jeu.
Bien, maintenant on peut poser notre crayon, ranger la feuille et ouvrir l'éditeur. Vous pouvez arrêter de lire pour le moment et construire une petite mission-test. Il devrait y avoir une pièce avec un Player Start, une chambre partiellement éclairée, deux couloirs les reliant : un petit couloir bien éclairé et un grand sombre, bien large.
Structure des données
Cela ne sert à rien d'avoir beaucoup d'IA si on ne peut pas les contrôler.
Nous aurons besoin d'une structure de données, à la manière d'une table
des matières ou un index.
On ne pense jamais à la table des matières quand on lit un roman, mais imaginez
seulement comment on pourrait trouver la description de la réunion du conseil
de Rivendell si "Le Seigneur des Anneaux" n'était pas divisé en chapitres
!
Ouvrez Actor Class Browser (Dans le menu View). Sur la gauche, il y a une arborescence avec les noms de toutes les classes disponibles. Chaque classe définit exactement une sorte d'objet et chaque objet que vous créez appartient à une classe. Cliquez sur un nom. Sur la droite, vous verrez les propriétés de la classe choisie (Sinon vous devriez voir un menu Properties dans la rangée la plus basse avec un [+], cliquez sur ce [+]). Tous les objets de cette classe, quand vous les créez, auront les mêmes propriétés que la classe à laquelle ils appartiennent. Vous pouvez changer les propriétés plus tard et même en rajouter de nouvelles, mais vous ne pouvez pas effacer celles qui existent déjà (Si vous les effacez quand même, on aura l'impression que l'objet aura perdu des propriétés, mais en réalité, elles sont toujours présentes, cachées à moins que vous mettiez une coche dans la fenêtre des propriétés des objets Show non-Local Props.)
Vous vous demandez peut-être pourquoi il y a une arborescence de classes
plutôt qu'une simple liste. Car il y a des classes qui sont basées sur d'autres.
Par ex, la classe Pawn est basée sur celle de
Actor. On dit que Actor
est la classe de base (ou le "parent") de la classe Pawn.
Pawn est la classe "enfant" de Actor
(on dit aussi que Pawn "hérite" d'Actor).
La relation entre la classe de base et la classe "enfant" est similaire
entre la classe et l'objet. A chaque fois que vous créez une nouvelle classe,
elle est une copie exacte de sa classe "parent".
Puis vous pouvez (devriez même !) changer quelques-unes de ses propriétés
ou en ajouter d'autres. Vous devriez toujours choisir une classe de base
qui sera la même et qui aura les mêmes choix que celle créée. Par exemple,
vous voulez faire une torche (hand torch) qui donne une lumière violette,
il est bon de chercher d'abord une torche normale, créez une classe "enfant"
à partir de là et changez seulement la couleur de la lumière (light color)
en violet (purple). Ceci vous fera économiser du temps et votre structure
de données sera bien ordonnée (ce qui est très important à la longue).
Vous n'êtes pas obligés de créer une nouvelle classe à chaque fois que vous avez besoin d'une nouvelle sorte d'objet. Si vous n'avez besoin que d'une seule et unique torche violette, créez simplement une torche normale puis changez ses propriétés. Aussi, souvenez-vous que les changements faits aux objets ne s'appliquent qu'à eux alors que faire des changements sur une classe change également tous les objets de cette classe. Si vous voulez beaucoup de torches violettes, il est mieux de créer une nouvelle classe ce qui vous évite de modifier chaque torche manuellement.
Créons une classe pour notre élément de feu ! Toutes les classes d'IA de
Deadly Shadows sont basées sur le T3AIPawn > AIPawn
> Pawn. Regardez un peu à tous ses "enfants" (et leurs "enfants",
ainsi de suite…). Leurs noms sont plus ou moins assez évidents. C'est un
bon entraînement car si vous cherchez, par exemple, la classe torche (torch
class), vous n'êtes pas obligés de scanner toutes les propriétés des classes
; il vous suffit simplement de chercher la classe nommée torch.
Nous sommes plus intéressés par T3AIPawnCreature
qui est la classe-parent de tous les IA non-humains. C'est là que notre
élément de feu sera le mieux classé.
Faites un clic droit sur T3AIPawnCreature et choisissez
New. Tapez FireElemental
au lieu de MyT3AIPawnCreature. Vous pouvez bien
sûr choisir un nom différent, mais gardez en tête que certains caractères
sont interdits (les espaces, en particulier). Il est mieux aussi de mettre
un nom en anglais, qui est accepté par tous comme étant la langue informatique.
Sans contexte, vous ne devineriez jamais ce qu'est un "ZywiolakOgnia", n'est-ce
pas ?
N'oublions pas de faire un clic droit sur la nouvelle classe et de choisir Placeable/UnPlaceable. Un astérisque apparaîtra à côté du nom de la classe qui indique qu'à partir de maintenant, l'éditeur vous laisse placer les objets de FireElemental dans vos maps. Ne le faites pas pour l'instant. Votre classe n'a pas encore toutes les propriétés requises. On y vient.
Les propriétés
Les propriétés stockent une très grande partie des informations des classes et des objets. Certaines montrent l'état actuel de l'objet et ses modifications durant le jeu. D'autres décrivent l'état original (initial) de l'objet. Regardez les propriétés de FireElemental. Il y a une propriété avec une catégorie appelée Health (Santé). Il y a aussi 2 sous-catégories : HealthState et MainHealth. Leurs valeurs sont déjà réglées. MainHealth est en bleu pâle ce qui signifie que FireElemental a la valeur de son "parent". Si vous changez un des champs de MainHealth, sa couleur changera ce qui indique que sa valeur a été modifiée localement.
Ne vous laissez pas intimider par le nombre des propriétés. C'est très
logique et sensé. Les meilleurs moyens d'en apprendre plus sur les propriétés
sont :
- De lire leur nom (Sérieusement, beaucoup oublient de le faire !),
- De lire les notes des développeurs,
- De parcourir les classes existantes,
- De regarder d'un peu plus près les missions originales et de voir comment
elles fonctionnent,
- De tester vos projets (Ne vous inquiétez pas, tant que vous travaillez
sur les objets et non sur les classes et que vous n'effacez rien, vous ne
risquez pas de détruire quelque chose).
Dans ce tutorial, nous allons modifier et ajouter quelques propriétés. Commençons avec Actor > VoiceTag
Cette propriété définit les voix de réponses que les IA vont utiliser. La valeur de FireElemental hérite de T3AIPawnCreature qui est T3CityGuard. Ce n'est pas approprié car nous ne voulons pas que notre créature parle comme un humain et on ne veut sûrement pas qu'il se plaigne de ses chaussures ou du capitaine. Aucune des voix de DS ne correspond car aucune des créatures des missions originales de DS n'a de voix "de feu". On ne va pas non plus créer notre propre son. On verra ça un peu plus tard. Pour l'instant, mettez Actor > Voice Tag à null (en fait, laissez le champ vide sans aucun caractère). Ceci indique au jeu que nous ne voulons pas donner de voix à notre AI.
Astuce : Si vous aimez une voix d'une des créatures des missions originales et souhaitez la donner à votre Ai, vous pouvez trouver la classe de la créature dans l'arborescence T3AIPawn, copiez la valeur Actor > VoiceTag et collez-la à la valeur Actor > VoiceTag de votre Ai. La même méthode peut s'appliquer dans quasiment tous les cas, tant que vous savez à quoi correspond la propriété que vous cherchez. Certains comportements sont définis par plus d'une propriété.
Sound > CharacterType
Sound > SoundAmbient
La classe FireElemental n'a pas ces propriétés, nous devons les ajouter manuellement. Faites un clic droit sur n'importe quelle catégorie de propriété et choisissez Add Property. Vous devriez voir une fenêtre avec une liste de catégories sur la gauche. Choisissez Sound de la liste. Une autre liste apparaît sur la droite, ce sont les propriétés assignées à la catégorie Sound. Choisissez CharacterType et SoundAmbient (Vous pouvez faire une sélection multiple en faisant un clic gauche et en maintenant la touche CTRL). Cliquez sur Add Property. Les deux devraient maintenant apparaître sur la liste des propriétés de FireElemental dans la catégorie Sound.
CharacterType définit le son des pas. On ne veut pas que l'élément de feu fasse des bruits de pas car il n'a pas de pieds, mettez cette propriété à null. Ce que nous voulons entendre, c'est le bruit du feu quand il est à proximité. Choisissez SoundAmbient et cliquez sur le bouton avec 3 points. Allez vous préparer un petit café, car les Schemas sont une panoplie de plus de 15000 petits fichiers textes et leur chargement peut prendre plusieurs minutes. Vous devriez maintenant voir une nouvelle fenêtre. C'est le browser des schemas sons. Ca n'a l'air de rien à la première impression mais les schemas sons sont une bonne invention. Chacun d'entre eux définit comment le son (ou le groupe de sons) devraient être joués. Donc "jouer un schema" est presque la même chose que "jouer un son", même si les schemas sont de simples scripts (texte) (Tellement simple, que l'on va en écrire un à la fin du tutoriel).
Allez dans le Schema Browser, ouvrez le répertoire de sons puis dans schemas_sfx. Trouvez fireplace_lp et sélectionnez-le. Réduisez le browser et retournez dans la fenêtre des propriétés de votre FireElemental. Allez dans Sound > SoundAmbient encore une fois mais là cliquez sur Use. La valeur de la propriété devrait changer en fireplace_lp.
AI > AnimationSpeedMultiplier
Comme on l'a dit avant, nous voulons ralentir le FireElemental. Mettons
la propriété à la même valeur que celle des zombies : 0.65.
Ce qui signifie le FireElemental sera 35 % plus lent que les autres créatures
qui ont un AI > AnimationSpeedMultiplier réglé
à 1 (dont les gardes de la ville). Garrett devrait
être capable de leur échapper sans le moindre souci.
AIPawn > CombatModelClass
AIPawn > MovementModelClass
AIPawn > BehaviorModelClass
AIPawn > FactionModelClass
Toutes les propriétés de la catégorie AIPawn
(avec l'exception de DiagnosticsOn) pointent vers
d'autres classes. On peut les trouver dans l'arborescence Actor
> MetaProperty > AIModel.
Jetons un coup d'oeil sur une des classes de modèle de combat, appelée ZombieCombatModel.
Il y a 3 catégories de propriétés intéressantes : AICombat,
AIMeleeCombat et AIRangedCombat. Tout ceci
paramètre le comportement du IA au combat. Vous pouvez toujours créer une
nouvelle classe et régler les valeurs des propriétés à celles que vous voulez.
Toutes les classes de modèles peuvent être modifiées de la même façon. Nous
allons en faire une pour notre FireElemental maintenant
car nous avons oublié un léger détail : les FireElemental
ne doivent pas glisser sur les flaques d'huile. On ne peut donc pas
utiliser un modèle de comportement existant, car de manière générale, toutes
les créatures ayant des jambes sont sensibles à ces saletés visqueuses.
Trouvez T3HumanBehaviorModel et créez son "enfant".
Appelez-le FireElementalBehaviorModel. Regardez
ses propriétés. Une des catégories est appelée AIStateFlags
et contient beaucoup de propriétés dont le nom commence par StateEnabled.
Le nombre d'états est limité et l'IA est toujours dans l'un d'eux. Son comportement
général est défini par les passages d'un état à un autre, constamment. En
règle générale, il y a un état pour chaque action possible (et sinon, cela
signifie que l'IA ne peut faire pas d'action ou qu'il doit faire quelque
chose avant).
Malheureusement, les Ai ne connaissent rien de l'effet glissant des huiles,
il faut donc mettre StateEnabled_Slipping à 0.
L'Ai ne pourra plus entrer dans l'état "Slipping" (glissant) et sera invulnérable
à l'huile. Appliquez ce changement maintenant.
Revenons à la classe FireElemental et à ses propriétés AIPawn. Réglez BehaviorModelClass à FireElementalBehaviorModel. Pour le combat, nous voulons qu'il soit agressif et sans conscience comme les zombies. Réglez CombatModelClass à ZombieCombatModel, FactionModelClass à UndeadFactionModel et enfin SensoryModelClass à UndeadSensoryModel.
Il y a une propriété de classe de modèle en plus dans cette catégorie : MovementModelClass. Sa valeur ne dépend que de vos goûts car elle définit les paramètres d'animations que l'AI va utiliser. Par exemple, si vous utilisez ZombieMovementModel, votre FireElemental aura des bras raides mais si vous choisissez StatueMovementModel, il aura l'air plus musclé et plus effrayant. Parfois, il peut être difficile de voir la différence car…
Render > SkeletalTag
Render > MaterialSkin
Render > CastShadows
Render > Transparency
…nous allons rendre notre Ai invisible. Plus précisément, son mesh sera
invisible. Les éléments de feu ont besoin d'avoir un mesh car sans cela,
il leur serait impossible de bouger dans votre niveau. D'un autre côté,
pas de mesh (la géométrie) ni de skin (les textures) ressemble à un élément
de feu. La solution est de les avoir mais de convaincre l'engin de ne pas
les dessiner. Pour ce faire, il faut ajouter la propriété Render
> Transparency et mettre une valeur à 1.
Ajoutez aussi Render > CastShadows et mettez la
valeur ShadowCastingFalse. Le FireElemental n'aura
pas d'ombre.
Ajoutez Render > SkeletalTag et mettez la valeur
GuardSkinny.
La dernière propriété à régler est Render > MaterialSkin.
Sa valeur est sans importance car le corps du FireElemental est invisible,
mais nous avons déjà pris pas mal de choses des morts-vivants, on peut donc
utiliser un de leurs skins (comme Zombie_01)
Les links (ou liens)
Nous allons faire une simple astuce : le corps du FireElemental est invisible mais nous allons lui attacher des flammes qui sont visibles. Quand le FireElemental bouge, les flammes suivent son mouvement. Le joueur aura l'impression que le FireElemental est fait exclusivement de feu.
Nous avons besoin de links. Le link est le lien entre 2 objets. Quand un
de ces objets change, l'autre peut changer aussi, d'une façon qui dépend
du type du link (qu'on appelle aussi flavor).
Par exemple, le link RigidAttachment permet à
un objet d'en suivre un autre dans l'espace.
Quand on fait un link entre un humain et un objet avec RigidAttachment,
l'objet est porté par l'humain. L'exemple le plus clair de cette technique
est celui du garde qui porte une torche mais quand on y regarde de plus
près, beaucoup d'IA ont les links RigidAttachment
pour les vêtements, les cheveux, les dents et les yeux.
Les links font référence aux objets mais vous pouvez les assigner à des
classes aussi. Quand un objet d'une classe linked (liée) est créé, tous
les objets nécessaires pour remplir les links sont créés automatiquement.
Par exemple, la classe GenWallTorch a des liens
(links) vers 2 autres classes. A chaque fois que vous placez une torche
murale sur votre map, l'éditeur ajoute automatiquement une flamme et de
la fumée, bien que ce soit 2 objets séparés.
Remarque : les classes "enfant" héritent des links de leurs parents. Si vous faites une classe "enfant" de GenWallTorch, elle aura les 2 mêmes links, même s'ils n'apparaissent pas sur la liste.
Trouvez la classe FireElemental dans l'Actor
Browser, faites un clic droit dessus et choisissez Show
Links. Sans surprise, notre Ai n'a pas de link. Cliquez sur Add
Link. Vous devriez voir une longue liste avec tous les types de links.
Choisissez RigidAttachment. Il y a 2 autres champs
dont un déjà rempli avec le nom de la classe que vous avez choisi. Il faut
remplir le deuxième également, mais nous ne pouvons choisir le nom à partir
d'une liste malheureusement. Cliquez dans le champ vide et tapez TorchFlame.
C'est le nom de classe de l'emitter qui produit
l'effet visuel d'une flamme de torche.
Remarque : les emitters sont des objets relativement complexes avec un simple but : ils créent constamment des sprites. Les sprites sont des petites images plates. Les emitters contrôlent leurs tailles, couleurs, textures, durées de vie, mouvements. On peut les utiliser pour produire des flammes, de la fumée, des étincelles, de la pluie, de la neige, etc. Tous ces effets ajoutent du spectaculaire mais ils peuvent consommer beaucoup de ressources. Utilisez-les avec modération.
Cliquez sur OK. Nous avons un link unique sur
notre liste mais il n'est pas encore pleinement configuré. Sélectionnez-le
et cliquez sur Edit Selected Link. Comme vous
pouvez le constater, les links ont des propriétés aussi.
Choisissez Attachment > m_parentBone et cliquez
sur le bouton avec les 3 points. Une liste de chaînes étranges apparaît,
nous en avons besoin pour le mesh. Chaque mesh d'IA a une série de hardpoints
prédéfinis auxquels les objets peuvent être attachés. Les points qui nous
intéressent commencent par HP et M
1. Ils concernent les parties du corps.
Choisissez HP_LeftHand et cliquez sur OK.
A partir de maintenant, chacun de vos FireElementals aura une flamme de
torche attachée à sa main gauche invisible. Ne fermez pas encore la fenêtre
de données link (link data).
Allez dans la propriété Link > m_name et réglez
sur Flames. C'est un nom personnalisé, vous pouvez
choisir n'importe quelle chaîne de caractères. Ecrivez-la quelque part car
nous en aurons besoin pour écrire les scripts. Vous pouvez cliquer sur Done.
Félicitations, vous avez paramétré un link !
Et pour les autres alors ? Répétez les mêmes actions mais choisissez à chaque fois un autre "hardpoint" :
- HP_RightHand
- HP_LeftHip
- HP_RightHip
- HP_RightKnee
- HP_LeftKnee
- M 1 L_Toe0
- M 1 R_Toe0
Et ce n'est pas tout. Créez 2 autres links mais utilisez cette fois FireplaceFlame
au lieu de TorchFlame. Mettez m_parentBone
à :
- HP_FrontWaist
- HP_Head
Il y a encore deux autres choses à faire avec ces links. Cliquez Add link encore une fois. Choisissez Vulnerability et tapez FireVulnerability dans le champ TO. Cliquez sur OK puis sur Done. FireVulnerability est un "enfant" de MetaProperty > VulnerabilityObject et il définit les sortes de dommages qu'un objet peut subir. Si on ne fait pas de link à un VulnerabilityObject pour notre FireElemental, il deviendra indestructible. Comme il est fait de feu, il devrait avoir les mêmes vulnérabilités que le feu. Bien sûr, vous pouvez définir votre propre "enfant" de VulnerabilityObject, et par exemple, faire qu'un garde lourdement protégé ait une résistance aux attaques physiques de 50 %.
En avant pour le dernier link. On avait prévu d'équiper notre IA avec sa
propre source de lumière, n'est-ce pas ?
Allez dans Actor Browser puis dans Actor
> WorldObj > Lights_ > FlameLight > TorchLight. Créez un "enfant"
de TorchLight et nommez-le InvisibleHandTorch.
Faites-le placeable.
Effacez tous les scripts et faites Scripts > bEnableInheritScripts
à False (les torches sont contrôlées par des scripts
mais nous n'en avons pas besoin, nous voulons seulement une bête source
de lumière ressemblant à une torche.)
Changez RenderType > DrawType à DT_Sprite
pour informer l'éditeur que cet objet n'a pas de mesh. Maintenant retournez
dans la classe FireElemental et ouvrez encore
une fois la fenêtre des links. (Clic droit, Show Links)
Ajoutez le link RigidAttachment FROM FireElemental TO
InvisibleHandTorch. Mettez sa propriété m_parentBone
à HP_LeftHand et m_name
à Light Source (Comme dans le cas de Flames,
c'est un nom personnalisé).
Encore une étape et notre IA sera prêt pour le test.
Les armes
Le développement de jeu est un art d'illusions. Saviez-vous que les will-o-wisps (Petites boules de lumières flottantes et inoffensives) des Païens de DS ne sont pas des IA ? Ce sont des ascenseurs. Si de telles choses existent, alors il n'est pas étonnant qu'on doit assigner une arme à notre FireElemental, même s'il n'en utilise pas.
Allez dans le actor class browser, allez dans
Actor > WorldObj > SetDressing > AIParts > AIFakeMeleeWeapon.
C'est l'objet qu'on donne à un IA quand il se bat à mains nues. C'est presque
parfait mais quand l'IA touche quelque chose, cela fait des sons complètement
différents de ceux de "feu".
Créez son "enfant" et appelez-le AIInaudibleFakeMeleeWeapon.
Réglez sa propriété PhysicsSound > PhysSoundTag
à null, et Weapon > WeaponStimulus
à StimulusType_Fire. Cette fausse arme
ne fera pas de son et fera des dommages de feu.
Trouvez FireElemental, faites un clic droit dessus
et choisissez Edit Weapon Loadout. Sélectionnez
LeftHip. Cliquez Make Local.
Cliquez sur Add Item to Slot.
Vous verrez un sous-niveau de l'arborescence de la classe actor : ce sont
les objets qui peuvent être placés sur le corps d'un IA.
Ils ont une seule chose en commun : la propriété Weapon
> WeaponStance
Son nom est un peu trompeur car il peut s'agir aussi d'objets personnels
comme des bourses, des clés (de tels objets doivent avoir le réglage WeaponStance
à eWS_ITEM).
Choisissez AIInaudibleFakeMeleeWeapon (il devrait
être en bas de l'arborescence). Cliquez OK et
Done.
Le test
Si tout s'est bien passé, le FireElemental devrait être fonctionnel. Mettez-le quelque part sur votre essai de map. Il devrait être placé sur un point où l'on puisse l'approcher directement ou se faufiler derrière lui.
Remarque : L'éditeur vous demandera si vous voulez sauvegarder votre gamesys personnalisé. Nous avons modifié le gamesys quand nous avons ajouté des classes personnalisées, cliquez sur "Yes". L'éditeur crée des sauvegardes des gamesys antérieurs automatiquement.
Mais quelque chose manque. Le FireElemental est trop silencieux et la lumière ne change pas du tout de brillance. Si vous tuez l'IA avec des flèches à eau, il va rester immobile même mort au lieu de faire quelque chose de spectaculaire. Enlevez-le de votre map. Nous devons enrichir notre création avec quelques scripts.
Faire des scripts
Un script est une séquence de commandes. On les utilise pour mélanger de simples actions telles que allumer une lumière ou envoyer un IA en un lieu précis, avec des comportements complexes. Les scripts contiennent même des conditions qui doivent être remplies avant que les actions soient entreprises. Ce qui veut dire qu'on utilise les scripts pour apprendre aux IA comment réagir à des événements.
Ouvrez le script browser en cliquant sur Trigger Scripts dans le menu View. Sur la droite, il y a une arborescence de noms de scripts. Les noms avec des icônes de livres sont des groupes (groups). Cela n'a aucune importance quel script va dans quel groupe, mais on peut le trouver plus rapidement. Par exemple, vous pouvez créer un groupe MyScripts et y mettre tous vos scripts. Ou alors créer un groupe séparé pour chaque mission ou IA que vous faites. Un groupe appelé Test est un bon endroit pour les scripts expérimentaux que vous n'utilisez pas dans de vraies missions.
Cliquez sur New. Une fenêtre avec 6 boutons et 2 champs apparaît. Le plus petit champ contient le nom du script. Il a un nom très commun DefaultName. Changez-le en FireElementalChangeState. Vous pouvez mettre un nom différent mais comme toujours, mieux vaut mettre un nom qui décrive bien la fonction. Celui-ci sera exécuté à chaque fois que le FireElemental passe d'un état d'Ai à un autre.
Le moteur du jeu garde toutes les traces des événements importants qui apparaissent dans votre map et il détecte ceux qui remplissent une ou plusieurs conditions de scripts. Lorsque les conditions sont là, toutes les actions du script approprié sont exécutées. Votre tâche est de définir QUAND l'exécution d'un script devrait commencer et COMMENT elle doit se dérouler. Il y a donc 2 sortes de commandes : Conditions (quand / si) et actions (quoi).
Cliquez sur Condition. Comme vous pouvez le constater,
il y a beaucoup de conditions disponibles. Vous ne pouvez pas en créer de
nouvelles car il faudrait modifier les fichiers exécutables du jeu, mais
ce n'est pas grave car il y a de nombreuses conditions prédéfinies et il
y a peu de choses que vous ne pouvez pas faire (certaines ne sont pas compatibles
avec le moteur…). On ne va pas les expliquer ici car il faudrait un livre
entier, et leurs noms sont assez clairs pour deviner leurs fonctions.
Prenez le temps de parcourir l'arborescence pour voir les choix à votre
disposition.
Trouvez le groupe AI (il devrait être tout en
haut), ouvrez-le et sélectionnez When my behavior state
changes to [Enum:EStateType]. Quand il y a me,
cela signifie que le script de l'objet y est attaché. En général, les scripts
sont toujours exécutés dans le contexte de leurs possesseurs. Vous pouvez
seulement faire référence à un autre objet dans votre script lorsque l'objet
auquel vous l'avez rattaché et l'autre objet sont linked ensemble (avec
une exception, qui sera expliqué plus tard).
Les strings (chaînes de caractères) entre crochets comme [Enum:EStateType],
sont des variables que vous devez réglées manuellement. Si vous avez déjà
sélectionné la condition mentionnée au-dessus, cliquez sur OK.
Elle sera ajoutée à la liste de conditions de votre script. Le string [Enum:EStateType]
devrait être écrit en bleu pâle. Cliquez dessus. Une liste de valeurs correctes
de cette variable devrait apparaître. Dans ce cas, chaque valeur fait référence
à un des états que l'IA peut entrer. Remarquez que nous avons empêché notre
FireElemental d'entrer dans l'état "glissant" (slipping state) donc si vous
choisissez STATE_SLIP_AND_FALL, la condition ne
sera jamais atteinte et le script n'exécutera jamais ses actions. Choisissez
ANY à la place. La condition devrait être la suivante
maintenant : When my behavior state changes to [ANY].
Elle sera atteinte quand l'objet changera son état à un autre.
On peut mettre plusieurs conditions mais il en faut une seule pour notre script, cliquez sur le bouton Action. Les actions sont organisées comme les conditions. Ouvrez le groupe Sounds et sélectionnez Play sound schema [SoundSchema] at my location. Cliquez sur OK, puis sur [SoundSchema], cela ouvre le catalogue de schemas sons (sound schema browser). Trouvez fire_short_burst, sélectionnez-le et réduisez le browser. [SoundSchema] devrait changer en [fire_short_burts]. Si ça ne le fait pas, cliquez dessus et choisissez fire_short_burst du catalogue de schemas encore une fois.
L'action que vous avez ajoutée va faire exactement ce qu'elle définit :
elle va jouer un son schema.
Remarque : C'est un schema qu'on appelle
"non-looping". Il ne va être entendu qu'une seule fois. Il y a aussi des
schemas "looping". On les entend indéfiniment, à moins d'être interrompu
par un événement (comme une fin de jeu) ou un script. On peut reconnaître
les schemas looping facilement par leurs noms. Ils se terminent habituellement
par le suffixe _lp (ceci ne concerne pas les sons
d'ambiance utilisés pour la "musique" de fond dans les missions originales).
Il y a un problème avec notre script : il ne va s'exécuter qu'une seule
fois.
Règle commune : si nous voulons qu'un script s'exécute plusieurs fois, il
faut le dire.
Cliquez sur Action, mais cette fois-ci ouvrez
Scripts et sélectionnez la dernière action de
cette catégorie : Reset script conditions and actions.
Cliquez sur OK. Lorsque l'exécution du script
atteint cette action, elle retournera au début du script et attendra que
les conditions soient à nouveau remplies.
Remarque : La séquence des actions est importante.
Si vous ajoutez Reset… avant Play
[SoundSchema]… votre script n'atteindra jamais la dernière action.
Vous pouvez faire un cliquer-déposer sur une action ou une condition pour
changer l'ordre de la séquence. Mais vous ne pouvez pas mélanger des conditions
et des actions ensemble.
Votre script a un nom, une condition et deux actions. C'est tout ce qu'il faut. Fermez la fenêtre en cliquant sur OK. Si vous voulez le modifier plus tard, il suffira de le sélectionner du browser et de cliquer sur Edit. Votre script est quasiment près à être utilisé. Nous devons l'attacher à la classe FireElemental, mais nous le ferons après avoir écrit d'autres scripts.
FireElementalAngry (Notre Ai est en colère)
Dans la première partie de ce tutoriel, nous avons dit que nous voulions
plus de sons pour notre FireElemental, qu'il devienne plus bruyant et qu'il
soit plus lumineux quand il entre au combat. On va s'y atteler dès maintenant.
Créez un nouveau script et nommez-le FireElementalAngry.
Ajoutez la condition when linked AI [LinkFlavor] enters
combat. Cliquez sur [LinkFlavor]. Nous
avons déjà vu une liste ressemblante, n'est-ce pas ? Ce sont les mêmes link
flavors que nous avons choisis quand nous avons attachés des flammes au
mesh du FireElemental. Il y a quelques entrées que nous n'avons pas encore
vues. Pour chaque nom de link, il y a sa "copie" avec une tilde (~) au début.
C'est ce qu'on appelle des return links. Quand
vous créez un link ordinaire d'un objet A à un objet B, un return link de
B à A est créé automatiquement. Ils ne font rien tout seul mais cela donne
un accès plus facile aux données à l'éditeur (et à vous).
Il y a aussi 2 types spéciaux de flavors : MYSELF
et PLAYER. Ce ne sont pas vraiment des links.
MYSELF pointe toujours vers le script de l'objet
auquel il est attaché, et PLAYER pointe toujours
vers Garrett (vous pouvez donc faire référence à lui, même s'il n'y a pas
de link entre lui et votre objet). Choisissez MYSELF
et cliquez sur OK. Votre condition devrait indiquer
when linked AI [MYSELF] enters combat.
Ajoutez 3 actions Play sound schema [SoundSchema] at my location, et assignez-leur 3 schemas : furnaceflameburst, hit_arrowfire_lo, et furnaceflame_lp. Les deux premiers sont des schemas non-looping. Quand ils sont mixés, on peut entendre un rugissement féroce et terrifiant. Le dernier schema fait référence à un son en boucle d'une flamme de fournaise qui est plus fort qu'un feu de cheminée. Le FireElemental aura ce son jusqu'à ce qu'on lui "dise" d'arrêter.
Ajoutez l'action Set [Property] (Int/Float/Byte property)
to [Value] on linked objects of [LinkFlavor], changing it over period of
[Float] seconds" de la catégorie "Properties".
C'est vraiment une fonction utile dans bon nombre de situations car ça vous
permet de changer les propriétés des objets dynamiquement.
Nous avons déjà attaché une torche invisible à notre IA. Maintenant, nous
allons changer son rayon de lumière. Notre script sera attaché au FireElemental,
la torche est liée grâce à RigidAttachment.
Cliquez sur [LinkFlavor]. Choisissez RigidAttachment
de la liste mais ne fermez pas la fenêtre. Il y a un champ texte en bas.
Souvenez-vous, nous avons mis le nom Light Source
à m_name quand nous avons créé des links entre
le FireElemental et la torche.
Si nous ne le faisions pas, il n'y aurait pas de communication entre le
link RigidAttachment et les autres, et notre script
appliquerait le changement de propriété à tous les objets qui sont liés
grâce au RigidAttachment (dont les "flames").
Cliquez dans le champ texte et écrivez Light Source
(ou la valeur que vous avez assignée à m_name,
si vous en avez choisi une autre). Cliquez sur OK.
Au lieu de [LinkFlavor], il devrait y avoir [RigidAttachment:Light
Source]. Mettez les autres valeurs de cette action à
LighShape.
LightRadius, 64 et 1.0,
respectivement. 64 est deux fois la portée habituelle de la lumière d'une
torche et nous voulons qu'elle change sur une durée d'une seconde, car le
changement instantané aurait un mauvais effet.
Ajoutez Reset conditions and actions, surtout
mettez-le à la fin du script. Cliquez sur OK pour
retourner sur le script browser.
FireElementalEndCombat
Ce script est l'inverse de FireElementalAngry. Essayez de comprendre tout seul comment il fonctionne.
Conditions :
· Si AI[MYSELF] est linké, il sort du combat.
· Chercher si [HealthState] est [Equal_To]
[HealthState_Alive] sur tous les objets linkés de [MYSELF].
Actions :
· Arrêter de jouer [furnaceflame_lp] sur mon objet
· Jouer le schema son [furnaceflame_ext] sur mon
emplacement.
· Mettre [LightShape.LightRadius] (Propriété de
Int/float/Byte) à [32] sur les objets linkés de
[RigidAttachment:Light Source], le faire changer
sur une durée de [5.00] secondes.
· Redémarrer les conditions de script et les actions. (Reset script conditions
and actions)
Vous vous demandez peut-être pourquoi on vérifie si notre IA est vivant (alive) : c'est parce que la mort de l'IA est une condition spéciale de sortie du combat et nous voulons nous en occuper à part entière.
FireElementalKilled
Dans Dark Project, quand un élément de feu est atteint par une flèche à
eau, les deux objets disparaissent comme si le feu s'était soudainement
éteint.
Nous allons éteindre notre élément également puis nous détruirons son corps
invisible pour qu'il ne se transforme pas en obstacle gênant pour le joueur
et les autres IA.
Conditions :
· Si mon état de santé (Health State) change de [ANY]
à [HealthState_Dead].
Actions :
· Mettre [LightShape.LightRadius] (propriété de
Int/float/Byte) à [0] sur les objets linkés de
[RigidAttachment:Light Source], le changer sur
une durée de [2.0] secondes.
· Finir de jouer [fireplace_lp] rapidement (ne
pas finir la boucle du son en cours) sur mon objet.
· Finir de jouer [furnaceflame_lp] rapidement
(ne pas finir la boucle du son en cours) sur mon objet.
· Jouer le son schema [furnaceflame_ext] sur mon
emplacement.
· Retarder le GAME de [1.00]
secondes. (Delay)
· Mettre le [EmitterSpawnState] à [EDS_NoNewPartices]
sur les objets linkés de [RigidAttachment:Flames].
· Retarder le GAME de [2.00] secondes. (Delay)
· Détruire les objets linkés [RigidAttachment].
· Détruire les objets linkés [MYSELF].
Les deux dernières actions détruisent l'élément, les flammes et la torche. Nous n'avons pas besoin d'indiquer le nom du link car nous voulons détruire tous les objets liés ("linkés") à RigidAttachment (torche et flammes). Quand on détruit l'élément de feu, tous ses sons sont arrêtés ; le script doit se mettre en attente jusqu'à ce que le schema furnace_ext se termine (d'où les actions Delay).
Quand on met le EmitterSpawnState à EDS_NoNewParticles,
les flammes arrêtent de produire de nouveaux sprites mais les anciens sprites
ne s'estomperont qu'à la fin de leur durée de vie.
Nous n'avons pas besoin de rajouter "Reset script conditions and actions"
car chaque élément de feu ne meure qu'une fois.
FireElementalHit
C'est le dernier script que nous avons besoin de faire. La partie la plus difficile est celle du delay (retardement). Cela signifie que le script doit attendre 2 secondes avant de commencer. Cela évite au jeu de jouer des copies multiples de schema quand l'élément de feu est touché par plusieurs ennemis à la fois.
Conditions :
· Si je prends des dommages (health damage) [Greater_Than]
[0.00] de [ANY] à [ANY].
Actions :
· Jouer le son schema [furnaceflameburst] à mon
emplacement.
· Jouer le son schema [hit_arrowfire_lo] à mon
emplacement.
· Faire un delay GAME de [2.00] secondes.
· Redémarrer les conditions de script et actions. (Reset scripts conditions
and actions)
C'est tout ou presque. Retournez sur le script browser, cliquez OK
et allez vous preparer une tasse de café.
Vous devez cliquer su OK à chaque fois que vous
faites ou changez un script, pour que l'éditeur remarque le changement.
Evitez de fermer le browser dans d'autres cas, minimisez-le à la place.
Lorsque l'éditeur a terminé, trouvez le FireElemental dans le actor class
browser (Catalogue de la classe actor) et ajoutez la propriété Scripts
> TriggerScripts à ses propriétés.
Cliquez sur Add puis cliquez sur le [+]
à côté du TriggerScripts puis sur none
et enfin sur le bouton avec les 3 points. Sélectionnez un des scripts de
votre FireElemental dans votre Script
Browser puis cliquez sur Use dans l'Actor
Class Browser. Le None devrait changer et avoir
le nom du script sélectionné. Vous venez d'attacher ce script à votre classe.
Faites de même pour les autres scripts que vous avez réalisés.
Notre FireElemental est presque fini. Mettez-le dans votre map-test.
Ajoutez aussi quelques flèches à eau. Enervez votre IA, fuyez-le, tuez-le
avec les flèches à eau. Ceci vous permettra de savoir si vos scripts fonctionnent
correctement.
Les sons schemas
Un petit détail ennuyeux subsiste : les volumes des sons sont mal définis. On entend les flammes d'un feu de cheminée quand on est très près, mais les explosions de flammes sont si bruyantes qu'on peut les entendre de n'importe quel endroit de la map. C'est la faute du schema. Une de leurs fonctions est de délimiter la distance à partir de laquelle un son donné peut être audible.
Ouvrez un explorateur de documents, allez dans le répertoire où est installé
TDS (le répertoire pour l'éditeur). Ouvrez CONTENT\T3\Sounds\.
Les schemas que vous voyez dans le schema browser sont en fait des fichiers
textes et ils sont tous ici.
Ouvrez le répertoire schemas_sfx et trouvez fireplace_lp.sch.
Vous reconnaissez le nom ? C'est le son schema de notre cheminée.
Faites une copie du fichier et nommez-la fireplace_alt_lp.sch.
L'autre schema dont nous avons besoin est fire_short_burst.sch.
Créez également une copie que vous nommerez fire_short_burst_alt.sch.
Maintenant ouvrez ces deux copies avec n'importe quel éditeur de texte et
trouvez une ligne appelée radii. Il y a deux paramètres
séparés avec des virgules :
· 1 and 25 in case of fireplace_alt_lp,
· 1 and 150 in case of fire_short_burst_alt.
Le premier paramètre de chaque ligne est la distance maximum que chaque
son doit jouer au volume maximum. Le second paramètre est la distance totale
du son.
Remarquez que le fire burst (explosion de feu) a une distance six fois plus
longue que le fireplace (feu de cheminée).
Changez 25 et 150 à
50. Sauvegardez les fichiers (assurez-vous qu'ils
gardent l'extension en .sch) et retournez sur
T3Ed.
Ouvrez le sound schema browser et regardez si vos nouveaux schemas sont
sur la liste. S'ils n'y sont pas, sauvegardez tout et redémarrez l'éditeur.
La dernière étape est de remettre les anciens schemas avec leurs versions modifiées dans les propriétés et les scripts de votre FireElemental. Il faut changer la propriété Sounds > SoundAmbient et les deux scripts : FireElementalChangeState et FireElementalKilled.
A suivre
Ce tutorial prend fin ici, mais vous pouvez toujours encore mieux améliorer
l'Ai. Par exemple, vous pouvez utiliser d'autres emitters et d'autres sons
pour créer différentes sortes d'Elementals. Ou vous pouvez faire apparaître
une flèche de feu quand l'Ai meurt.
Vous pouvez effacer les links entre les jambes et les flammes du FireElemental,
on aura l'impression qu'il flotte à un mètre du sol. Vous pouvez également
modifier les paramètres de combat et d'autres propriétés : peut-être qu'une
seule flèche à eau suffira pour tuer le monstre ?
Ne craignez pas d'expérimenter des choses, c'est le meilleur moyen d'apprendre
comment cela fonctionne.
Tutoriels sur T3ed
t3ed.tutoriel.free.fr