Que recherchez-vous ?
Games

Un plaisir imprévisible : La valeur de l'aléatoire dans la conception des jeux

EDUARDO ORIZ / UNITY TECHNOLOGIESSenior Content Marketing Manager
Apr 11, 2022|10 Min
Un plaisir imprévisible : La valeur de l'aléatoire dans la conception des jeux
Cette page a été traduite automatiquement pour faciliter votre expérience. Nous ne pouvons pas garantir l'exactitude ou la fiabilité du contenu traduit. Si vous avez des doutes quant à la qualité de cette traduction, reportez-vous à la version anglaise de la page web.

Apprenez à injecter de l'aléatoire dans votre jeu pour que les joueurs restent engagés et impatients de découvrir la scène suivante. Voici le deuxième article de Christo Nobbs dans sa série sur la conception de systèmes, qui s'inscrit dans le prolongement de ses contributions à Le manuel du concepteur de jeux Unity. Consultez l'e-book pour en savoir plus sur la façon de prototyper, de concevoir et de tester un gameplay dans Unity.

Dans son précédent billet, Christo a abordé la manière dont les concepteurs peuvent créer des systèmes dans leurs jeux qui se traduisent par un gameplay intriguant et inattendu. Dans ce billet, il approfondit la question en donnant des exemples sur la façon de mettre en place la randomisation.

Image sur fond violet clair avec la couverture de l'Ebook GamePlay & Design
La randomisation ainsi que d'autres sujets connexes sont abordés dans le livre électronique gratuit intitulé The Unity game designer playbook.
Laissez des traces visuelles à vos joueurs

Vous pouvez inciter les joueurs à explorer davantage les systèmes de votre univers de jeu et à leur faire vivre des expériences uniques grâce à des messages visuels. L'article précédent, Des systèmes qui créent des écosystèmes : La conception de jeux émergents, a défini un espace de type bac à sable dans lequel tout est fait et construit en bois, avec la possibilité d'une propagation imprévisible du feu. Développons cet exemple en donnant aux joueurs la possibilité d'abattre des arbres à l'aide d'une hache.

Supposons que le paysage où se trouvent les joueurs soit plat. Ils ne savent pas dans quelle direction l'arbre tombera lorsqu'ils l'abattront. Et si certains arbres étaient des "chicots", c'est-à-dire qu'ils sont morts mais toujours debout ? Ils peuvent tomber à tout moment. Un tel élément imprévisible dans le jeu surprendra les joueurs et intensifiera leur environnement de jeu.

Vous pouvez ajouter un grand nombre d'indices visuels pour montrer aux joueurs que les chutes d'arbres sont dangereuses dans ce monde et les garder sur leurs gardes. Les indices aideront les joueurs à repérer les différences entre les arbres les plus dangereux et ceux qui sont en bonne santé et moins menaçants. Ils devront choisir comment peser les risques : Ne serait-il pas plus sage de chercher du bois de chauffage dans les arbres déjà tombés et d'éviter d'être blessé par les arbres morts qui attendent de tomber ?

En tant que concepteur, veillez à repérer les endroits où se produisent ces réactions systémiques en chaîne et à les intégrer, en apportant un soutien lorsque les joueurs les déclenchent, par exemple lorsque les arbres tombent de manière imprévisible, prennent feu et répandent ainsi un délicieux chaos.

Générer des surprises avec Unity.Engine.Random

La classe de script Random dans Unity est une classe statique qui vous fournit des approches pour générer des données aléatoires dans un jeu. Cette classe porte le même nom que la classe System.Random du .NET Framework et remplit une fonction similaire, mais diffère sur certains points essentiels, notamment parce qu'elle est 20 à 40 % plus rapide que System.Random.

Vous trouverez ci-dessous les propriétés statiques et les méthodes disponibles dans la classe Random :

Propriétés statiques

  • à l'intérieur du cercle de l'unité: Renvoie un point aléatoire à l'intérieur ou sur un cercle de rayon 1.0 (lecture seule)
  • insideUnitSphere: Renvoie un point aléatoire à l'intérieur ou sur une sphère de rayon 1,0 (en lecture seule)
  • onUnitSphere: Renvoie un point aléatoire sur la surface d'une sphère de rayon 1,0 (en lecture seule)
  • Rotation: Renvoie une rotation aléatoire (lecture seule)
  • rotationUniformes: Renvoie une rotation aléatoire avec une distribution uniforme (en lecture seule)
  • état: Obtient ou définit l'état interne complet du générateur de nombres aléatoires
  • valeur: Renvoie un flottant aléatoire dans [0.0..1.0] (l'intervalle est inclus) (lecture seule)

Méthodes statiques

  • CouleurHSV: Génère une couleur aléatoire à partir des plages HSV et alpha.
  • InitState: Initialise l'état du générateur de nombres aléatoires avec une graine
  • Gamme: Renvoie une valeur flottante aléatoire comprise entre [minInclusive..maxInclusive] (l'intervalle est inclusif).

L'article de blog précédent explorait le rôle des leviers de conception et l'utilisation de ScriptableObjects pour stocker ces valeurs. Vous pouvez remplacer ces valeurs par des plages appropriées en utilisant la fonction Random.Range d'Unity, qui renvoie un flottant aléatoire compris entre [minInclusive..maxInclusive] (la plage est inclusive). Toute valeur flottante donnée entre ces deux valeurs, y compris minInclusive et maxInclusive, apparaîtra environ une fois tous les 10 millions d'échantillons aléatoires.

Cette approche vous permet de tirer une valeur de l'intervalle défini pour votre résultat. Vous devrez tester plusieurs fourchettes pour trouver celle qui convient à vos objectifs de jeu, mais là encore, veillez à concevoir pour la fourchette que vous avez définie.

Image d'un robot blanc surveillant les incendies de branches d'arbres dans une forêt
Découvrez les systèmes modulaires dans le premier article de blog de notre série sur les concepteurs de jeux.
Une aventure aléatoire dans la forêt

Le hasard favorise l'immersion. Par exemple, disons que chaque arbre a un nombre de points de vie fixe de 100, et que chaque coup de hache enlève 25 points de vie à l'arbre. Cette tâche deviendra rapidement prévisible et donc ennuyeuse. Même si vous donnez aux arbres une fourchette de santé de 76 à 100, n'importe quel arbre sera à quatre coups de la chute. En revanche, une fourchette plus réduite, de 75 à 76 par exemple, offre une plus grande variété de résultats, puisqu'il faut entre trois et quatre coups pour qu'un arbre tombe.

Une autre façon de rendre ce scénario intéressant est d'indiquer les changements de santé par des indices visuels clairs au lieu de barres de santé. Cela permettra au joueur d'apprendre, en jouant, le nombre approximatif de coups de hache nécessaires pour faire tomber un arbre. Les repères visuels ajoutent une certaine imprévisibilité limitée qui peut être équilibrée et ajustée en fonction de l'objectif de jeu. L'utilisation de la classe Random au lieu d'une valeur fixe permet de transformer une tâche monotone en une tâche amusante.

Pour développer cet exemple, vous pourriez choisir de retirer une valeur aléatoire comprise entre 15 et 25 points de vie pour chaque coup de hache. De ce fait, les joueurs ne peuvent pas facilement prévoir le nombre de coups qu'il leur faudra pour abattre un arbre. Ils devront s'appuyer davantage sur des indices visuels pour évaluer le moment où un arbre va tomber ; des indices tels que la taille des morceaux qui s'envolent de l'arbre ou les fissures qui se forment le long du tronc, les branches qui tombent, les effets sonores, etc.

Ils ne sauront pas exactement quand chaque arbre tombera, mais au fil du temps, en abattant de plus en plus d'arbres, ils pourront faire des suppositions éclairées, ce qui améliorera leurs chances de survie.

Capture d'écran des paramètres des dégâts des coups aléatoires dans l'inspecteur
Dans le playbook du concepteur de jeux Unity, vous trouverez des exemples de création de leviers et de visualisation de champs dans l'inspecteur. Dans cette image, l'Intensité des dégâts est la valeur aléatoire qui affecte la valeur finale des dégâts - elle est affichée avec l'attribut Portée.

L'aléatoire vise à offrir aux joueurs des défis imprévisibles qui les poussent à calculer les risques et à gérer les résultats.

Voyons quelques autres exemples d'utilisation de la classe Random.

Pondération des aléas dans un jeu de cartes

Imaginez un jeu de cartes avec un ennemi IA qui joue sa carte uniquement en fonction du mouvement du joueur. Cet événement deviendrait rapidement prévisible sans aléa puisqu'il donnerait le même résultat à chaque fois.

Même en fixant la probabilité à 50 %, la randomisation est trop simple et deviendrait vite évidente pour le joueur. Au lieu de cela, essayez d'ajouter des couches de hasard basées sur les actions des joueurs. Cela permettra de créer des systèmes complexes offrant quelque chose de plus dynamique que le choix entre deux cartes dans une réserve, ou que le fait de choisir ou non cette carte parmi les nombreuses cartes de la réserve existante.

Vous pouvez ajouter des niveaux de difficulté à la table de cartes en faisant en sorte que l'ennemi privilégie certaines cartes par rapport à d'autres, en prenant des décisions en fonction d'une valeur qui lui a été attribuée ou de la complétude de sa composition avant d'attaquer, par exemple. La carte d'un boss peut multiplier ses dégâts lorsqu'elle est jouée avec d'autres cartes de pouvoir. L'ennemi attend donc d'avoir en main un certain nombre de cartes de pouvoir pour augmenter la difficulté pour le joueur. Vous pouvez ajouter du "poids" à une telle composition en augmentant ou en diminuant la probabilité que la carte "boss" soit jouée avec certaines autres cartes de pouvoir.

Image du jeu de cartes Heartstone
Hearthstone de Blizzard Entertainment reste l'un des jeux de cartes les plus populaires. Il a été lancé en 2014 et est réalisé avec Unity.
Bruit de Perlin

Vous pouvez utiliser le hasard sous différentes formes pour vos jeux. Le bruit de Perlin, par exemple, possède des qualités naturelles et génère un bruit de gradient à partir d'une graine. Essayez de l'utiliser dans Cinemachine pour créer une sensation de caméra plus organique pour les caméras de suivi à la troisième personne.

Capture d'écran de l'algorithme de bruit de Perlin dans Cinemachine
Exploitez l'algorithme de bruit Perlin dans Cinemachine pour créer des mouvements de caméra plus organiques.

Pour essayer le bruit de Perlin, consultez les packs Starter Assets - Third-person Character Controller, ou Gaia sur l'Asset Store, ainsi que la documentation sur l'utilisation de Mathf.PerlinNoise.

Image de la texture échantillonnée par le bruit de Perlin (taches noires et blanches floues)
Voir cette texture échantillonnée du bruit de Perlin. Au lieu de valeurs entièrement aléatoires en chaque point, le bruit se compose de "vagues" dont les valeurs augmentent et diminuent progressivement sur l'ensemble du modèle. Le bruit peut être utilisé comme base pour les effets de texture et l'animation, pour générer des cartes de hauteur de terrain, etc.
Attaquer les agents de l'IA

Dans une interview, Chris Butcher, l'un des ingénieurs en chef de Halo 2 des studios Bungie, a évoqué l'IA du jeu en déclarant : "L'objectif n'est pas de créer quelque chose d'imprévisible. Ce que vous voulez, c'est une intelligence artificielle qui soit cohérente, de sorte que le joueur puisse lui donner certaines informations. Le joueur peut faire des choses et s'attendre à ce que l'IA réagisse d'une certaine manière".

Image d'un robot vert se tenant au milieu d'un terrain lowpoly à l'extérieur de bâtiments cubiques.
Ces ressources sont disponibles via le pack Starter Assets - Third-person Character Controller de l'Asset Store.

Dans cette optique, comment configurer les agents de l'IA pour que votre jeu reste imprévisible et vivant ?

Une façon d'expérimenter est de combiner les Starter Assets avec les outils d'IA de l'Asset Store, tels que A* Pathfinding Project Pro, un outil qui vous permet de déplacer un agent d'IA vers un point donné.

Lorsque l'agent de l'IA se rapproche du joueur, ce dernier s'attend à être attaqué. Et si, au contraire, elle permettait d'engager une conversation ? Pourquoi ne pas ajouter plus de PNJ qui se déplacent et se fondent dans l'espace pour une sensation plus vivante ? Ces PNJ pourraient choisir les points qui leur sont attribués de façon linéaire, un par un, ou mieux encore, choisir des points logiques en fonction d'un ensemble de règles utilisant la classe Random.

Supposons que vous ayez un agent IA qui tire sur le joueur avec un arc et des flèches de faible puissance. Malheureusement pour l'agent IA, il doit se trouver à une certaine distance du personnage car la portée maximale de tir est de 10 mètres. L'IA se place devant le joueur, à 10 mètres, et tire. Cette situation n'est ni excitante ni idéale, surtout si vous ajoutez un deuxième tireur qui se bat pour la même position sur le NavMesh.

Pour un scénario plus intéressant, choisissez une zone où l'ennemi doit s'approcher du joueur. Pour ce faire, utilisez Random.insideUnitCircle, passez le résultat du vector2 dans un vector3 pour les axes X et Z, puis utilisez RandomRange pour les deux afin d'obtenir une zone avec un rayon minimum et maximum autour du joueur.

Capture d'écran du script utilisé pour qu'un agent IA sélectionne un point d'attaque près du personnage principal
Le script utilisé pour qu'un agent IA sélectionne un point d'attaque près du personnage principal
capture d'écran des leviers de conception dans l'inspecteur
Les leviers de conception vous permettent d'affiner le comportement de l'IA à partir de l'inspecteur.

L'agent de l'IA doit choisir un point situé dans un rayon d'action acceptable autour du joueur et tirer, plutôt que de s'approcher du joueur. Pour ajouter de l'animation au jeu avec un minimum de codage, appliquez ceci à tous les agents de l'IA afin qu'ils attaquent le joueur sous plusieurs angles. L'action de l'IA est prévisible jusqu'à un certain point, car vous savez que les agents doivent se trouver à une certaine distance pour attaquer, mais vous ne pouvez pas prédire les angles sous lesquels ils vont frapper.

image d'un espace lowpoly avec un réticule blanc au-dessus d'un cercle gris et vert
Le point rouge symbolise l'endroit où l'agent ennemi se rendra.

Vous pouvez vous inspirer de cet exemple en donnant à vos agents IA la possibilité d'attaquer en mêlée. Utiliser le même point aléatoire du joueur avec un rayon plus étroit. De cette façon, les agents de l'IA choisissent parmi un ensemble d'attaques lorsqu'il est temps de frapper.

Le joueur sait qu'il peut être attaqué par une formation de mêlée, mais de quelle direction ? Y aurait-il une grève de type "overarm top-down" ? Ou un grand écart de gauche à droite ? Ils doivent lire les messages télégraphiques de l'animation pour savoir ce qui les attend.

Editeur Unity

Ce scénario peut devenir complexe si vous avez plusieurs PNJ ennemis censés attaquer le joueur en même temps. Mais si vous regardez Far Cry 2 d'Ubisoft, par exemple, le joueur n'est pas attaqué par tous les ennemis en même temps. Différents ennemis attaqueront à différents moments, de manière aléatoire. Vous pouvez en savoir plus sur cet exemple et sur d'autres scénarios d'IA dans cette vidéo de Game Maker's Toolkit .

Si vous tentiez de reproduire de manière réaliste les résultats d'actions prises dans le monde réel dans votre jeu, cela nécessiterait des équations complexes et à multiples facettes ou un agent ML bien formé. En fin de compte, tout cela pourrait sembler déplacé, prendre beaucoup de temps et de frais généraux techniques à mettre en œuvre, et rester difficile à équilibrer et à contrôler avec compréhension, si l'architecture est mal conçue.

Pourtant, en utilisant la classe Random d'Unity, les concepteurs de jeux peuvent créer des résultats crédibles pour leurs scènes avec un contrôle étroit des niveaux d'excitation, en moins de temps qu'il n'en faudrait autrement. Bien entendu, le hasard n'est pas toujours approprié. La prévisibilité reste essentielle. Mais si vous les placez aux bons endroits, aux moments les plus opportuns, vous pouvez offrir à vos joueurs des expériences uniques qui les inciteront à jouer encore et encore.