Artillerie et physique dans un jeu vidéo

Quand un jeu vidéo imite la technologie du monde réel

Créer des armes pour un jeu vidéo est une entreprise complexe. Les armes doivent être efficaces, agréables à jouer et suffisamment crédibles, sans pour autant rendre leur coût en performances trop élevé. La plupart des jeux vidéo prennent donc des raccourcis :

  • les balles traversent souvent un niveau en une seule frame [^frame], permettant de simuler le tir en un seul test de collision ;
  • les dégâts dits de zone se contentent de causer des dégâts dans un rayon bien défini ;
  • la localisation des dégâts sur la victime se borne habituellement à des zones bonus qui causent plus de dégâts.

Est-il possible de faire quelque chose de plus réaliste avec les contraintes d'un jeu vidéo ? C'est le pari que nous avons fait ! Voyons ensemble quelques techniques pour contourner les limites des moteurs physiques.

Un peu de contexte

Cet article est rédigé par l'équipe du jeu vidéo Helium Rain et a initialement été publié en anglais sur notre Devblog. Notre jeu est une space sim qui inclut du combat dans l'espace, avec un objectif de réalisme. Nous avions besoin d'une arme antiaérienne [^AA] efficace pour défendre les vaisseaux majeurs des attaques de chasseurs, ce qui nous a conduit à développer un canon antiaérien en nous inspirant des technologies modernes.

L'artillerie antiaérienne à l'époque des guerres mondiales

Il y a historiquement deux options pour se débarrasser de chasseurs : utiliser des chasseurs en riposte, ou embarquer des canons antiaériens sur les cibles à protéger. La seconde option est celle qui nous intéresse ici. Ces canons répondent à des contraintes lourdes : il faut toucher une cible mouvante de quelques mètres de large qui évolue à plusieurs kilomètres d'altitude avec un obus qui met dix secondes à toucher sa cible, et une technologie de visée qui pendant les deux guerres mondiales se bornait essentiellement à l'estimation humaine.

Au cours de la Première Guerre mondiale, on comprend donc bien vite que les simples obus ne sont pas une solution et on développe des obus qui explosent près de la cible, projetant des fragments métalliques dans toutes les directions. A l'époque, il n'y a que deux façons de déterminer l'instant de l'explosion : soit un seuil de temps, soit un seuil d'altitude. Les deux méthodes exigent un réglage avant le tir.

Tout change avec la Seconde Guerre mondiale qui voit l'introduction des fusées de proximité, des obus explosant quand ils détectent une cible près d'eux. Ces projectiles détectent les objets métalliques et rendent les batteries antiaériennes extrêmement efficaces ; à l'époque, les Alliés prennent toutes les dispositions nécessaires pour que cette technologie reste secrète, quitte à limiter son déploiement. C'est cette technologie que nous simulons dans la vidéo ci-dessus - comme vous pouvez le voir, son efficacité est certaine.

Détonateur de proximité des années 50 (Wikimedia)

Contrairement à l'essentiel des jeux, nous avons décidé de simuler physiquement ces obus d'artillerie. La première étape est de recréer une fusée de proximité. Notre version est perfectionnée : l'obus explose quand il entre dans un rayon de détonation, mais il peut aussi exploser quand il détecte qu'il a atteint sa distance minimale à la cible.

On met donc en place une distance d'activation en dessous de laquelle l'obus est armé. Une fois armé, si l'obus détecte que sa distance à la cible augmente, il explose. Nos obus ont une distance d'activation de 50m et une distance de détonation de 3m.

16.7ms chrono

16.7 millisecondes, c'est le temps qui se déroule entre deux images (ou frames) d'un jeu vidéo quand ce dernier a un débit d'image de 60fps, la valeur habituellement visée par les développeurs. C'est un intervalle de temps très court pendant lequel on doit mettre à jour toute la logique du jeu, transmettre une scène au moteur de rendu 3D, effectuer un rendu et l'afficher. Le budget de temps pour les projectiles, lui, est de l'ordre d'une seule milliseconde : pas question de passer toute une frame à calculer des positions de projectiles !

Et pourtant, 16.7 millisecondes c'est énorme : pendant ce temps-là, un obus a le temps de parcourir près de quinze mètres, la taille d'un petit vaisseau. Chaque mise à jour du moteur physique va déplacer un obus de cette distance. On comprend mieux pourquoi les balles de jeux vidéo ne sont jamais simulées physiquement !

L'implémentation de notre mécanisme est donc très difficile. La simulation de la physique est faite pas à pas, chaque pas de simulation correspondant au calcul de tout ce qui s'est passé dans le monde du jeu entre le temps présent et le temps de la prochaine frame. Comme nos obus sont très rapides (près de 800 m/s), on ne peut pas se contenter de vérifier la proximité à la fin de chaque pas, puisqu'on pourrait traverser une cible pendant ce temps !

Trajectoire d'un obus vis-à-vis d'une cible

C'est ce qu'on appelle l'effet tunnel dans les jeux vidéo. Cette anomalie peut permettre à des balles de traverser des murs, par exemple. Les moteurs physiques pour le jeu vidéo ne permettent simplement pas de simuler de telles vitesses.

Pour corriger le problème, il nous faut calculer la distance minimale entre la cible et la trajectoire de l'obus, ainsi que le point associé sur la trajectoire. Si ce point n'est pas sur le segment (S0S1) correspondant à la frame courante , cette distance minimale n'est pas utilisable.

Différentes positions de l'obus pendant un tick

Si la distance minimale est inférieure à la distance de détonation, l'obus explose. La position initiale de l'explosion n'est en revanche pas le point de distance minimale D, car le projectile doit toujours exploser à la distance de détonation et jamais en dessous. En effet, ce point de distance minimale peut parfaitement être situé à l'intérieur du vaisseau - un cas typique d'effet tunnel. Une telle explosion causerait des dégâts irréalistes.

Pour éviter cela, on calcule la position où la distance à la cible est égale à la distance de détonation, pour exploser à cet endroit précis. Une conséquence amusante de cet ajustement est qu'il faut parfois faire revenir un obus en arrière pour exploser.

Explosion à la position précédente

Si on n'est pas encore à la distance de détonation, mais déjà en dessous de la distance d'activation, l'obus est armé. Dans ce pas de simulation ou dans les pas ultérieurs, si l'obus a été armé et que la distance augmente, l'obus explose.

Explosion au plus près

Pluie de métal

Dans la plupart des jeux vidéo, les obus explosent avec des dégâts dans un rayon fixe et tous les ennemis proches sont touchés. Habituellement, ils reçoivent plus de dégâts s'ils sont proches du centre. Dans le monde réel, l'explosion du projectile en elle-même ne cause pas de dégâts, c'est le nuage de fragments métalliques qu'elle projette qui cause le plus de dégâts. Plus une cible est proche du centre de l'explosion, plus la probabilité d'être touché est grande. Notre simulation reproduit ce comportement.

Dans Helium Rain, les vaisseaux n'ont pas de points de vie. Ils sont constitués de dizaines de composants individuels, chaque composant ayant son modèle de collision. Les obus simulent des centaines de fragments touchant les vaisseaux alentour, pour rendre les effets du tir aussi réalistes que possible. Une explosion sur le côté gauche d'un vaisseau endommagera donc des composants sur sa gauche, sans affecter son côté droit. La méthode la plus simple pour implémenter cette simulation serait de créer autant de traces [^trace] physiques qu'il y a de projectiles, mais elle serait bien trop coûteuse en performances, et inefficace, car très peu de rayons toucheraient leur cible.

Tracé de rayon pour des fragments

Pour augmenter la densité du nuage de fragments tout en diminuant la charge du processeur, il devient nécessaire d'optimiser. On sélectionne d'abord toutes les cibles dans un rayon raisonnable (en rouge sur les images suivantes), et pour chacune d'entre elles on calcule l'intersection entre leur sphère circonscrite [^bounding] (en magenta) et une sphère centrée sur l'obus (en jaune) qui représente la distance à la cible. Le ratio de surface entre la sphère jaune et l'intersection des deux sphères correspond au ratio entre le nombre de fragments que devrait émettre la munition et le nombre de fragments que l'on doit vraiment simuler (en bleu). Un dessin pour comprendre…

Tracé de rayon optimisé pour des fragments

Il suffit ensuite de simuler les fragments utiles et de propager les dégâts correspondants aux composants touchés. Voici le résultat en 3D, dans le jeu, avec la visualisation correspondant à l'image précédente.

Tracé de rayon pour des fragments, vu en 3D

Pour aller plus loin…

Ces obus sont encore très dangereux à utiliser, car ils ne font pas la différence entre alliés et ennemis, puisqu'ils se comportent comme des détecteurs de métaux. Plus simplement, tirer sur un ennemi proche peut causer des dégâts à son propre vaisseau. Pour pallier ce problème, les obus sont équipés d'un timer, qui ne permet à l'obus de s'armer que quand il est à une certaine distance du canon. Ce timer permet aussi de désactiver un obus qui aurait dépassé sa cible.

La vidéo au début de l'article montre l'efficacité de ces obus. Bien sûr, on pourrait encore développer le sujet en parlant des tourelles qui tirent ces projectiles, puisqu'elles doivent anticiper les mouvements des adversaires et compenser ceux du joueur ; ou encore de l'intelligence artificielle qui régit les pilotes des vaisseaux pour leur permettre d'échapper à ces obus… Ce qu'il faut retenir, c'est qu'il est possible de simuler très précisément des mécanismes réels dans un jeu vidéo.

En bonus, voici un petit extrait du code du jeu, issu du calcul du nombre de fragments à simuler.

1
2
3
4
5
6
7
    float ApparentRadius = FMath::Sqrt(FMath::Square(CandidateDistance) + FMath::Square(CandidateSize));
    float Angle = FMath::Acos(CandidateDistance/ApparentRadius);
    float ExposedSurface = 2 * PI * ApparentRadius * (ApparentRadius - CandidateDistance);
    float TotalSurface = 4 * PI * FMath::Square(ApparentRadius);
    float ExposedSurfaceRatio = ExposedSurface / TotalSurface;

    int FragmentCount = ShellDescription->GunCharacteristics.AmmoFragmentCount * ExposedSurfaceRatio;

On peut noter que le niveau de mathématiques requis pour ce genre de travail est très accessible. Les notions basiques de géométrie sont suffisantes.

Merci de votre lecture ! :)


  1. Une frame est une image faisant partie d'une vidéo. On parle de frames per seconds pour désigner le débit d'images à l'écran, par exemple.  

  2. On parle d'antiaérien parce que le terme est courant, il est inutile de préciser qu'il n'y a pas d'air dans l'espace. 

  3. Une trace, dans le jeu vidéo, est un segment entre deux points qui teste l'existence d'objets physiques. Imaginez une balle dans un jeu de tir. 

  4. La sphère circonscrite ou bounding sphere est la plus petite sphère qui contient tout un objet, en 3D. 


32 commentaires

Plus simplement, tirer sur un ennemi proche peut causer des dégâts à son propre vaisseau. Pour pallier ce problème, les obus sont équipés d'un timer, qui ne permet à l'obus de s'armer que quand il est à une certaine distance du canon.

Dans l'espace, ca empêche pas le backfire d'un obus, même à grande distance. Car les éclats vont avoir une impulsion donnée par l'explosion. Si la distribution est sphérique, y'a des éclats qui vont revenir vers celui qui a tiré. Avec une distribution conique, c'est déjà moins le cas.

Et puis le truc dans l'espace, c'est qu'un fragment tiré qui ne percute rien continue jusqu'à percuter quelque chose… Il faudrait donc continuer à le simuler jusqu'à ce qu'il ait dépassé tous les vaisseaux.

A noter que dans l'espace, un simple projectile suffit déjà à faire beaucoup de dégât. On en parlait un peu ici : https://zestedesavoir.com/forums/sujet/3201/mass-effect-et-les-projectiles

+0 -0

Du coup, on en revient aux choix qui sont faits dans les jeux-vidéos, il est forcément nécessaire d'oublier le réalisme. Sinon, effectivement il faudrait gérer chaque fragment pour vérifier qu'il n'aille pas toucher un vaisseau, et là les PC vont exploser ^^

+2 -0

Oui, normalement à n'importe quelle distance un vaisseau devrait pouvoir être touché par un éclat d'obus. Nous avons choisi de limiter la portée des éclats pour des raisons de performances mais aussi parce qu'au bout d'une certaine distance, la probabilité d'être touché par l'un d'entre eux devient très faible. C'est dommage de simuler des projectiles à longue distance qui ne toucherons probablement jamais rien, et ça peut être frustrant d'être touché par un projectile invisible une fois sur 5000 sans comprendre vraiment pourquoi.

Ce n'est pas "dans l'espace" que les projectiles font mal, mais c'est plutôt que dans l'espace on peut croiser des projectiles à très très haute vitesse : plusieurs milliers de mètres par secondes. Dans notre cas les obus et les explosions surviennent dans le même référentiel que les vaisseaux et les éclats ont une vitesse relative aux vaisseaux raisonnable : environ 2000 m/s. Par contre dans Helium Rain, les éclats ne font pas moins mal à la limite de portée de l'explosion, mais on a juste moins de chance d'être touché.

Une dev-stream sur UE 4 ? Je suis trés curieux de voir comment vous travaillez dessus :) .

Ardakaniz

On ne fera probablement pas de dev stream. La principale raison étant que c'est en fait peu utile parce que nous avons un usage réduit des outils d'UE4 :

  • on n'utilise pas du tout l'éditeur de niveaux puisque les niveaux sont générés ;
  • on utilise l'éditeur de matériaux de façon exceptionnelle maintenant que le master material des vaisseaux est fixé ;
  • on utilise très peu le script graphique ;
  • on n'a simplement pas d'animation, de vertex painting, de terrains ou autres travaux exigeant de bosser dans UE4.

Le genre de jeu qu'on développe est relativement à part et on n'a donc pas grand chose à montrer du développement, c'est surtout du code et de la 3D.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte