Comment fonctionnent les balles dans les jeux vidéo?

64

Je suis tombé sur cette question lorsque je concevais un jeu vidéo en C #.

Si nous considérons des jeux tels que Battlefield ou Call of Duty , des centaines voire des milliers de balles volent en même temps. Les événements sont déclenchés constamment, et d'après ce que je sais, cela consomme beaucoup de puissance de traitement… ou le fait-il? Je souhaite savoir comment divers développeurs de jeux gèrent les puces (2D et 3D) et quelle est la méthode la plus efficace pour chacune d’elles.

J'ai lu la question Comment les balles sont-elles simulées dans les jeux vidéo? mais cela ne concerne pas le fonctionnement des puces du point de vue de la conception du programme.

J'ai eu quelques idées, mais chacune a ses inconvénients:


Méthode la plus efficace à laquelle je pouvais penser (pour les jeux 2D):

Supposons que je crée une classe appelée Bullet et que, aussi longtemps que l'utilisateur maintienne un bouton enfoncé, un objet Bullet sera créé toutes les 0,01 seconde. Cette balle a:

  • 1 vélocité

  • 2 Position de départ d'où il est tiré

  • 3 texture Sprite

  • 4 Un effet de frappe

Puisque la balle serait sa propre classe, elle pourrait gérer elle-même les écouteurs dessinant, bougeant et agissant.

Ne serait-il pas difficile pour le processeur de traiter des milliers de ces objets en cours d'instanciation, puis de destruction (lorsqu'un effet de frappe est déclenché)? Espace RAM?


Méthode efficace pour les jeux 3D - Une autre pensée que j'avais était la suivante:

Disons que je crée une classe d'arme. Cette arme a diverses caractéristiques, dont certaines:

  • 1 Détecter le point de visée de l'arme et déterminer si elle vise une cible

  • 2 Déclenchez une animation du tir au fusil

  • 3 Possède une méthode doDamage () qui indique quelque chose à soustraire la santé quel que soit le type de pistolet visé

  • 4 Notifie une classe d'animation de balle lorsque le bouton est enfoncé

Je pourrais ensuite créer une classe statique, disons BulletAnimation, qui pourrait recevoir une notification indiquant l'origine du pistolet, l'emplacement de ce pistolet (pour la destination de la balle) et des informations sur un sprite et une vitesse appropriés à utiliser pour la balle. . Cette classe dessine ensuite les images-objets (sur un nouveau fil, idk) en fonction des deux positions et de l'image-objet souhaitée, afin de simuler le tir d'une balle.


Ce dernier semble beaucoup plus difficile à coder, et ne faudrait-il pas beaucoup de puissance de traitement pour appeler constamment le statique afin de le faire pour des milliers de balles à la fois? Obtenir des mises à jour constantes sur les positions de départ et d'arrivée serait également difficile.

Ma question est la suivante: quelle est la méthode la plus efficace pour les créateurs de jeux? Cette méthode change-t-elle des jeux 2D aux jeux 3D?

Eric
la source
16
Notez que même aujourd'hui, la plupart des jeux ne simulent pas le vol de balles. Pour tout ce qui est considéré comme "assez rapide", une analyse de résultats est effectuée à la place. En principe, on suppose que l'impact se produit au même moment où vous appuyez sur la gâchette. Dans tous les cas, "des centaines ou des milliers de balles" n’est pas vraiment une grosse somme - c’est quelque chose que les jeux possèdent depuis les toutes premières consoles (divers jeux bullet-hell), des milliers de fois moins puissant que les machines actuelles. Vous devez juste vous assurer de ne faire que le moins de travail possible par balle :)
Luaan
42
Les balles fonctionnent généralement via la pew-pew-pewtechnologie :)
MonkeyZeus
5
Il n'y a jamais des centaines ou des milliers de balles qui volent en même temps. Il n'y a pas d'arme qui les tire aussi vite. Même le puissant Phalanx arrive à 75 balles par seconde. Sur la base du "champ de tir effectif" répertorié sur Wikipedia, les balles volent pendant environ 3 secondes tout au plus, de sorte qu'un Phalanx peut mettre 225 balles en l'air en même temps. Un M16 dépasse environ 12 tours / s et ne peut pas maintenir ce taux (le maximum pour un tir soutenu est de 0,25 tour / s). Il n'y a tout simplement pas autant d'armes à feu tirées à la fois!
Cort Ammon
3
Juste pour faire remarquer ceci, il n’est jamais bon de créer des objets individuels en tant que classes quand ils sont si simples. Il est de loin préférable d’avoir une instance bulletField pour chaque type de puce. Le léger surcoût de la longueur du code et ainsi de suite vous épargnera un mot supplémentaire de 4 octets par balle (si le type est un entier). De plus, un objet peut facilement analyser une liste.
Le grand canard
4
@Cort - C'est vrai, à condition qu'il n'y ait qu'une seule arme à feu dans l'espace de jeu. Le PO mentionnait des jeux comme Battlefield et CoD, où des dizaines de joueurs pouvaient lancer des armes automatiques simultanément. Il n’est pas déraisonnable qu’il y ait un nombre ridicule si chaque partie était réellement comptabilisée physiquement dans l’espace.
Jesse Williams

Réponses:

78

Je peux certainement comprendre pourquoi vous pensez qu’il serait difficile de les simuler, mais il ya suffisamment de contraintes sur les balles (vraiment tous les projectiles) pour les rendre plus faciles.

  1. Ils sont généralement simulés comme un seul point, plutôt que comme un élément de volume. Cela facilite considérablement la détection des collisions, car maintenant je n'ai plus besoin que de collisions contre des surfaces très simples, comme une ligne contre un cercle.

  2. Nous savons comment ils vont se déplacer, il n’ya donc pas beaucoup d’informations à stocker ou à calculer pour eux. Votre liste était assez précise. Nous aurons généralement quelques éléments supplémentaires, comme par exemple qui a tiré la balle et quel est son type.

  3. Étant donné que tous les projectiles seront très similaires, nous pouvons les pré-allouer afin d’éviter toute surcharge de les créer dynamiquement. Je peux allouer un tableau de 1 000 projectiles, et maintenant, ils ne sont accessibles qu’avec un index et ils sont tous séquentiels en mémoire, leur traitement sera donc rapide.

  4. Ils ont une durée de vie / plage fixe, ce qui me permet de supprimer les anciennes puces et de recycler la mémoire en nouvelles puces très rapidement.

  5. Une fois qu'ils ont touché quelque chose, je peux aussi les faire expirer, leur vie est donc finie.

  6. Puisque nous savons à quel moment ils ont été créés, si nous en avons besoin de nouveaux et que nous n'avons pas de billets gratuits dans notre liste préaffectée, je peux simplement récupérer les plus anciens et les recycler, et les gens ne remarqueront pas que les balles expirent un peu plus tôt .

  7. Ils sont rendus sous forme de sprites (généralement) ou de modèles low poly et occupent très peu d’espace à l’écran. Ils sont donc rapides à restituer.

Tenant compte de toutes ces choses, les balles ont tendance à être relativement bon marché. Si notre budget est déjà consommé par les balles et les rend, nous le remanierions généralement pour limiter le nombre de tirs pouvant être tirés à la fois (vous le constaterez dans de nombreux jeux d'arcade anciens), utilisez des armes à rayon qui se déplacent instantanément. , ou ralentissez le taux d’incendie pour vous assurer de respecter le budget.

Tom K
la source
12
Je ne suis pas d’accord avec 5), ce qui complique la situation dans les jeux modernes. Dans les tireurs précédents, c'était acceptable, de nos jours, même la COD permet aux joueurs de tirer à travers des murs en bois. 6) serait inacceptable pour tout système concurrentiel, bien que ce soit un problème rare.
SBoss
17
@SBoss reformule ensuite: "Une fois qu’ils ont touché un objet qu’ils ne peuvent pas pénétrer, je peux aussi les faire expirer, de sorte qu’ils ont une durée de vie limitée.". Et pour 6, vous pouvez obtenir le pire des cas en plafonnant le taux d'incendie maximal par caractère, puis en maintenant un tableau de longueurs num_characters * max_bullets_per_character
variés
14
@SBoss Je pense que # 6 est plus pour par exemple. Jeux spatiaux descendants, dans lesquels une balle au mouvement lent peut parcourir une grande distance de l'écran avant de frapper quelque chose / de disparaître. Évidemment, ce n'est pas un problème dans les jeux de type CoD, où les balles bougent rapidement et atteignent rapidement les limites du monde.
BlueRaja - Danny Pflughoeft
1
La grande majorité des jeux ne modélise pas du tout les puces (balistique externe). La plupart des jeux utilisent une technique appelée "hit-scanning".
Aron
44

L’un des moyens les plus efficaces d’implémenter des puces consiste probablement à utiliser ce que l’on appelle des hitscan . Sa mise en œuvre est plutôt simple: lorsque vous tirez, vous vérifiez si le pistolet vise (éventuellement en utilisant un rayon pour trouver l'entité / l'objet / le maillage le plus proche), puis vous le frappez, lui infligeant des dégâts. Si vous voulez donner l’impression que vous avez tiré une balle invisible invisible à déplacement rapide, vous pouvez la simuler en ajoutant un léger retard en fonction de la distance avant d’endommager.

Cette approche suppose essentiellement que le projectile tiré a une vitesse infinie et est généralement utilisé pour des types d’armes tels que les lasers et les faisceaux de particules / canons et peut-être même certaines formes de fusils de tireur d’ élite .

L’approche suivante consisterait à modéliser la balle tirée comme un projectile, modélisé comme sa propre entité / objet sujet à la collision et éventuellement à la gravité et / ou à la résistance de l’air qui modifie sa direction et sa vitesse. Il est plus complexe que l'approche de numérisation à la volée en raison des équations de physique supplémentaires, et nécessite plus de ressources en raison de la présence d'un objet puce, mais peut fournir des puces plus réalistes.

En ce qui concerne la gestion des collisions entre des balles à base de projectiles et d’autres objets dans le jeu, la détection des collisions peut être grandement simplifiée en triant vos objets en quad ou octrees . Les octrees sont principalement utilisés dans les jeux en 3D, tandis que les quadtrees peuvent être utilisés dans les jeux en 2D ou en 3D. L'utilisation de l'un de ces arbres présente l'avantage de réduire considérablement le nombre de contrôles de collision possibles. Par exemple, si vous avez 20 objets actifs dans le niveau, sans utiliser l'une de ces arborescences, vous devrez vérifier que tous les 20 sont en conflit avec la puce. En divisant les 20 objets entre les feuilles (nœuds d'extrémité) de l'arborescence, vous pouvez réduire le nombre de contrôles au nombre d'entités présentes dans la même feuille que la puce.

En ce qui concerne ces approches - hitscan et projectile, les deux peuvent être utilisés librement dans des jeux 2D ou 3D. Cela dépend plus de la nature de l'arme et de la manière dont le créateur a décidé que l'arme fonctionnerait.

Seta
la source
Les informations sur le modèle de conception, Hitscan et quad / octrees sont vraiment utiles. Merci également pour l'info!
Eric
8
Si vous ne poursuivez pas un hitscan mais simulez les projectiles, ils peuvent se faufiler à travers des objets minces parce qu'ils se déplacent si rapidement. Dans ce cas, rappelez-vous que tout dans les jeux est faux. Même si une balle ne fait que quelques centimètres de long, vous pouvez effectuer la détection de collision comme si la balle mesurait un mètre de long. De cette façon, vous pouvez toujours faire de belles simulations de largage de balles et de temps de vol sans trop vous inquiéter des balles déformant les objets sans les toucher :).
Roy T.
2
Existe-t-il des jeux dans lesquels la physique des balles (par opposition aux obus de canon, par exemple) respecte des éléments tels que la gravité (la chute de balle), la résistance de l'air? (C’est-à-dire des jeux autres que des jeux spécialisés où l’accent est mis sur le tir à la cible de précision ou quelque chose du genre: FPS, etc.) Je ne suis pas un joueur, mais je suis surpris que ce niveau de fidélité soit (même parfois) nécessaire.
davidbak
3
@davidbak: cela dépend fortement de la rencontre typique dans le jeu et du réalisme attendu du genre de jeu. Si vous combattez principalement (seulement?) Au combat rapproché, alors ce niveau de fidélité n’est pas nécessaire. Mais si l'option du combat à longue distance existe (par exemple, des tireurs d'élite ou des archers dans un environnement plus semblable à un RPG), la gravité affectant les missiles est aujourd'hui plutôt attendue. Si vous dirigez votre lance-roquettes vers le haut, vous vous attendez toujours à ce que la fusée atterrisse et explose quelque part, non? Pourtant, les trajectoires ne sont pas toujours calculées à partir de la physique réelle, il s'agit simplement d'une approximation (pour des raisons de performances)
hoffmale
1
@davidbak Battlefield depuis la mauvaise compagnie 2 de Bad Company. Pour les fusils, les pistolets, les obus de chars, les fusées, tout. Battlefield 3 est gratuit sur Origin, vous pouvez vérifier (IIRC). Battlefield 4, bien sûr, a également cette "fonctionnalité". Un autre jeu où vous pouvez voir ceci est "Sniper Elite". 2 ou 3 sont les nouveaux titres. La physique joue un rôle important dans ce jeu.
Apache
7

Je ne suis en aucun cas un expert, mais pour répondre à votre question, oui, vous auriez besoin de beaucoup de ces choses que vous mentionnez.

Pour votre exemple 2D, vous pourriez avoir une position et une vitesse pour une balle. (Vous pouvez également avoir besoin d'une durée de vie ou d'une distance maximale, en fonction de la manière dont vous avez implémenté vos puces.) Cela impliquerait généralement 2 valeurs (x, y). Si elles étaient des flotteurs, c'est 16 octets. Si vous avez 100 balles, cela ne représente que 1600 octets ou environ 1,5k. Ce n'est rien sur une machine aujourd'hui.

Ensuite, vous mentionnez les sprites. Vous n'aurez besoin que d'un seul sprite pour représenter chaque puce. Sa taille dépend de la profondeur de bits sur laquelle vous dessinez et de la taille à laquelle elle doit apparaître à l'écran. Même non compressé à, disons, 256x256 en 32 bits par canal, cela représente 1 Mo pour le sprite. (Et ce serait très gros!) Vous dessineriez le même sprite à chaque emplacement de balle, mais cela ne prendrait pas de mémoire supplémentaire pour chaque copie du sprite. Ce serait similaire pour un effet de frappe.

Vous parlez de tirer toutes les 0,01 secondes. Ce serait 100 balles par seconde de votre arme. Même pour une arme futuriste, c'est beaucoup! Selon cet article de Wikipédia :

Lorsque la gâchette est actionnée, le taux auquel les cartouches sont déclenchées est le taux cyclique. La cadence de tir cyclique typique est de 600 à 900 tr / min pour les fusils d’assaut, de 1 000 à 1 100 tr / min dans certains cas, de 900 à 1 200 tr / min pour les mitraillettes et les pistolets mitrailleurs et de 600 à 1 200 tr / min pour les mitrailleuses. Les M134 Miniguns montés sur des hélicoptères d'attaque et d'autres véhicules de combat peuvent atteindre des cadences de tir supérieures à 100 coups par seconde (6 000 tr / min).

Donc, ce serait le taux d'un hélicoptère d'attaque!

Pour un grand monde comme vous le mentionnez dans Battlefield / Call of Duty / etc., ils peuvent calculer toutes ces positions de balle, mais ne pas les dessiner toutes si l'action est éloignée. Ou ils peuvent ne pas les simuler jusqu'à ce que vous soyez proches. (Je dois admettre que je devine un peu sur cette partie car je n'ai pas travaillé sur quelque chose d'aussi gros.)

utilisateur1118321
la source
6

Ne serait-il pas difficile pour le processeur de traiter des milliers de ces objets en cours d'instanciation, puis de destruction (lorsqu'un effet de frappe est déclenché)? Espace RAM?

Je pense que vous sous-estimez à quel point les ordinateurs sont rapides. Ce fut parfois un problème sur les systèmes des années 80 et 90. C'est en partie pourquoi les Space Invaders d'origine ne vous permettent pas de tirer une autre balle tant que la balle actuelle n'est pas tombée. Certains jeux ont souffert du "ralentissement" s’il y avait trop de sprites à l’écran.

De nos jours, cependant? Vous disposez de suffisamment de puissance de traitement pour des milliers d'opérations par pixel nécessaires à la texturation et à l'éclairage. Il n'y a pas de problème avec des milliers d'objets en mouvement; Cela vous permet de créer un terrain destructible (par exemple, Red Faction) où chaque fragment traite les collisions avec d'autres fragments et suit une courbe balistique.

Vous devez faire preuve de prudence en matière d'algorithme - vous ne pouvez pas utiliser l'approche naïve consistant à vérifier chaque objet par rapport à un autre objet lorsque vous avez des milliers d'objets. Les puces ne vérifient généralement pas les collisions avec d'autres balles.

Petite anecdote: la première version de Doom en réseau (l’original des années 90) envoyait un paquet sur le réseau pour chaque balle tirée. Lorsqu'un ou plusieurs joueurs ont la mitrailleuse, le réseau pourrait être submergé. Les années 90 étaient pleines de gens jouant illicitement à Doom sur des réseaux universitaires ou professionnels qui rencontraient des problèmes avec leurs administrateurs réseau lorsque le réseau devenait inutilisable.

pjc50
la source
Je me demande comment la scie à chaîne a travaillé dans ce contexte
reas0n
1
IIRC, le vrai problème avec le premier destin du réseau est qu’il évitait de devoir envoyer chaque paquet séparément à chaque joueur adverse en utilisant des paquets de diffusion. Cela réduisait le nombre de paquets envoyés, mais entraînait malheureusement une charge de calcul considérable sur toutes les machines du réseau, y compris celles qui ne jouaient pas.
Supercat
1

Je suis loin d'être un expert mais je travaille sur un jeu de tir 2D multijoueur pendant mon temps libre.

Ma méthode

Il existe différentes classes de puces entre le client et le serveur (même en mode hors connexion, une instance de serveur est démarrée sur un processus distinct auquel le jeu "principal" est connecté).

À chaque tick (60 par seconde), le client établit un relèvement entre le pointeur de la souris du joueur et le centre de l'écran (où se trouve leur personnage) et fait partie des informations envoyées au serveur. Si le joueur tire également à ce moment (en supposant que l'arme est chargée et prête), une instance de balle côté serveur est créée, avec simplement quelques coordonnées et un dommage de base (qui découle des statistiques de l'arme qui a tiré il). L’instance de balle utilise ensuite certaines fonctions mathématiques pour calculer les vitesses X et Y à partir du relèvement que nous avons collecté auprès du client.

Pour chaque coup suivant, la balle se déplace de ces coordonnées et réduit les dégâts de base d'un montant prédéfini. Si cette valeur passe au-dessous de 1 ou frappe un objet solide dans le monde, l'instance de balle est supprimée et, comme les collisions de points de test sont incroyablement peu coûteuses en 2D, même les armes à tir rapide ont un impact négligeable sur les performances.

En ce qui concerne le client, les informations par balle ne sont pas réellement reçues sur le réseau (cela s’est avéré inutile dans les tests), mais dans le cadre de la mise à jour par tick, chaque caractère dispose d’un booléen "déclenché" qui, si true, est créé par un client local. objet bullet qui fonctionne presque exactement comme les serveurs, la seule différence est qu’il a un sprite.

Cela signifie que bien que la balle que vous voyez ne soit pas une représentation tout à fait exacte de celle-ci sur le serveur, toute différence serait à peine perceptible, voire pas du tout, pour un joueur, et les avantages du réseau l'emportent sur les incohérences.

Note sur les différentes méthodes

Certains jeux, y compris le mien, déplacent les balles à chaque coche comme s'il s'agissait d'objets physiques, tandis que d'autres créent simplement un vecteur dans le sens de la prise de vue ou calculent le trajet complet de la balle dans le coche où elle a été créée, par exemple dans Contre- Jeux de grève. Il existe quelques petites astuces côté client pour masquer cela, telles qu'une animation du tir de la balle, mais à toutes fins pratiques, chaque balle n'est qu'un laser .

Avec les modèles 3D qui peuvent avoir des hitbox complexes, il est standard de tester les collisions contre un simple bornier FIRST, et si cela réussit, passez à une détection de collision plus «détaillée».

TheCatOfWar
la source
0

C'est ce qu'on appelle la détection de collision. Pour ce faire, les ordinateurs 8 bits utilisaient du matériel graphique lecteur-missile. Les moteurs de jeu modernes utilisent des moteurs physiques et de l’algèbre linéaire. La direction actuelle d'une arme est représentée par un vecteur 3D. Cela fournit une ligne infinie dans la direction du feu. Chaque objet en mouvement a une ou plusieurs sphères de délimitation car c'est l'objet le plus simple à détecter une collision avec une ligne. Si les deux se croisent, c'est un succès, sinon ce n'est pas le cas. Mais la scène peut être gênante, il faut donc la vérifier également (à l’aide de volumes hiérarchiques). L'objet le plus proche qui a une intersection est celui qui a été touché.

Mikael
la source