Comment gérer un grand nombre de micros dans un jeu MMO

26

Comment les jeux comme Minecraft, ou vraiment n'importe quel jeu MMO qui a des micros, les gèrent-ils?

Dites que le terrain génère 3 gouttes de "saleté" à chaque fois que vous creusez ledit terrain. Supposons que chaque élément possède une animation de rotation calculée à chaque image. Si le nombre de micros dans le monde devient très élevé, ce serait une surcharge massive inutile dans le calcul de la trame pour un client dans un serveur donné, car il est probable que beaucoup de ces éléments de micros sont à des années-lumière de vous.

Donc, ce que je pensais, c'est que vous ne devez "faire des trucs" qu'avec les micros proches du joueur local, mais cela impliquerait toujours que chaque image que je dois vérifier si un autre élément de ramassage soit suffisamment proche pour commencer à animer.

Ma vraie question est: comment les autres MMO ont résolu ce problème?

Alakanu
la source
9
De plus, dans le contexte de Minecraft, après qu'un certain nombre d'éléments sont suffisamment proches les uns des autres (3+ du même élément dans le même blockspace), le serveur remplace les 3 instances par une instance groupée qui contient le type de bloc ( minecraft:dirt) et un compte (30), de sorte que lorsque le joueur est suffisamment proche pour le ramasser, il ajoute simplement autant de compte que possible à l'inventaire du joueur. Si le joueur n'a de la place que pour 6 objets et qu'une pile de 30 est au sol, le joueur ramassera les 6 et la pile au sol sera réduite à 24.
Zymus
6
@Zymus Il convient de noter que les performances des ticks ont en fait diminué pour un nombre modéré d'objets abandonnés, car ils recherchent tous constamment des objets à proximité.
user253751

Réponses:

48

En ne chargeant simplement que les parties du monde en mémoire qui sont proches du joueur. Tout le reste est suspendu au disque dur. Lorsqu'il y a un petit objet posé à environ deux kilomètres de là, le joueur ne peut pas le voir et ne peut pas interagir avec lui. Il n'y a donc aucune raison de le mettre à jour ou de l'envoyer au GPU pour le rendu. Plus l'objet et sa plage d'interaction sont petits, plus la plage autour du lecteur où vous devez le charger est faible.

En ce qui concerne la découverte de ce qui est proche du joueur: cela revient essentiellement à stocker le monde dans une structure de données optimisée pour la recherche spatiale. Les bons candidats pour ceux-ci sont le hachage spatial et les arbres multidimensionnels .

Philipp
la source
Merci pour la réponse rapide. Je pensais à utiliser Unity car je suppose qu'il utilise une sorte de partitionnement spatial pour vérifier les collisionneurs de déclenchement, donc je créerais un grand collisionneur de cercle autour de mon personnage et chaque élément à l'intérieur serait "animé". Est-ce une façon de mettre en œuvre votre réponse? Corrigez-moi si je me trompe, bravo!
Alakanu
2
@Alakanu Il peut être utilisé si vous voulez que les objets soient visibles sur de longues distances, mais n'effectuez certains comportements de calcul intensifs qu'à proximité du joueur (et juste faire tourner quelque chose autour de son axe y ne devrait pas être très cher). Mais le véritable défi lors de la mise en œuvre d'un jeu en monde ouvert dans Unity consiste à instancier et à détruire intelligemment les objets du jeu pendant que le joueur se déplace dans le monde (ou mieux encore: utilisez la mise en commun d'objets au lieu d'instancier et de détruire).
Philipp
Oui, le regroupement d'objets est obligatoire dans cette situation :) Ok merci beaucoup!
Alakanu
3
@Alakanu Vous pouvez consulter le concept "Minions chargées" de Minecraft pour plus d'informations sur la façon dont cette réponse s'applique à ce jeu.
T. Sar - Rétablir Monica
1
Ce n'est pas seulement vrai pour les micros. N'IMPORTE QUEL objet trop éloigné du joueur peut être traité de cette façon. Parfois même des objets proches du joueur. Imaginez une maison avec un intérieur complet, mais il n'est pas nécessaire de rendre jusqu'à ce que vous entriez dans la maison.
Zibelas
22

Vous avez deux choses très différentes à gérer:

  1. Le serveur doit gérer le monde entier, de manière autoritaire. Pour cela, une communication avec N clients (où N est "massif") est nécessaire.

  2. Le client pourrait , en principe, connaître le monde entier, mais ce n'est pas nécessaire . Pour le client, il suffit de savoir ce qui se trouve à proximité du joueur. En supposant, par exemple, un partitionnement de type grille plutôt grossier, il faudrait qu'il ne connaisse que la cellule du joueur et les 26 cellules autour du joueur (ou 8 cellules si vous avez une grille 2D). Une grille un peu plus fine est préférable, mais vous avez l'idée.

Maintenant, beaucoup de micros, qu'est-ce que "beaucoup"? Vous creusez peut-être 5 choses par seconde, c'est peut-être deux douzaines de numéros qui doivent être mis à jour sur le serveur, et le serveur devra peut- être les transmettre à un autre joueur dont la zone d'intérêt chevauche votre cellule. Pour un ordinateur, il s'agit d'une quantité de données assez ridicule et d'une quantité de calcul négligeable. Cela peut devenir un défi quand il y a des centaines / milliers de joueurs dans la même cellule (alors votre partition est trop grossière).

Le serveur n'a pas besoin de connaître, ni de se soucier de la rotation des micros ou de tels détails. Pourquoi le ferait-il?

Le client ne s'en soucie pas non plus, car ce n'est qu'un régal pour les yeux que le client peut fabriquer à la volée.

Ce qui est nécessaire du point de vue du serveur, c'est de savoir que vous creusiez (30, 40, 50) dans le nœud dans lequel vous vous trouvez, et il décide que cela engendre par exemple trois objets de type 5, ou un objet de type 7 avec un compte de 3. C'est tout ce qui compte et c'est tout ce qu'il vous dit. Il inclura également ces informations dans les données envoyées à une personne déplaçant sa zone d'intérêt sur la cellule de la grille plus tard (en supposant qu'elle soit toujours là à ce moment-là).

On dit au client que trois objets y sont apparus, bla bla. Maintenant, que le client affiche une carte ASCII où il y a maintenant un `` D '' ou qu'il montre un tas de saleté en rotation, c'est la même chose. Que les piles aient des rotations différentes ou que seules celles proches de votre joueur tournent, c'est la même chose. C'est juste des choses qui s'affichent sur votre moniteur, cela n'affecte personne d'autre.

Donc, dans le cas concret où vous souhaitez faire pivoter uniquement les tas de saleté à proximité, vous pouvez simplement effectuer une vérification de la portée de tous les objets que vous connaissez. Étant donné que l'ensemble de données n'est pas volumineux, même la force brute sur tout fonctionnera.

Vous pouvez (et devriez) en fonction de la taille de votre partitionnement, tailler trivialement les cellules de grille trop éloignées.

Vous pouvez, bien sûr, sous-partitionner davantage votre cellule et utiliser quelque chose de super intelligent. Utilisez un kd-Tree si vous voulez, mais ne vous attendez pas à des gains énormes. Vous pouvez tailler des trucs avec Manhattan Distace, ou vous pouvez trier vos trucs dans une petite grille de votre choix ... mais pourquoi?

Un contrôle de distance (distance vraiment au carré, mais c'est la même chose pour vous) n'est que deux multiplications et un ajout (optimisé pour MUL, MADD, donc vraiment seulement deux opérations), suivi d'une branche ou d'un mouvement conditionnel. C'est à peu près aussi rapide que toute autre opération qui ne taille pas des cellules de grille entières à la fois. En fait, c'est quelque chose que vous pourriez même faire sur le GPU ...

En voyant comment vous aurez quelques centaines, ou tout au plus quelques milliers de contrôles de distance par rapport à la même position (la distance au carré fonctionne très bien), vous n'avez vraiment pas beaucoup de mal à faire ce calcul, d'autant plus que c'est plutôt un cache- itération amicale sur la mémoire contiguë, et avec des mouvements conditionnels, c'est très bon marché. Quelque chose comme (pseudocode) rot = r[i] + 1; r[i] = ((dx*dx+dy*dy) < DIST_SQ) ? rot : r[i];. C'est une itération sur un tableau de quelques centaines de valeurs par image. L'ordinateur s'en fiche de tout cela, ce sont des charges et des magasins contigus, une simple ALU, pas de succursales et seulement quelques milliers d'itérations.

Ce problème (plusieurs-à-un) n'est pas la même classe de problèmes (plusieurs-à-plusieurs) que sur le serveur. Vraiment, le client n'est pas le problème.

Damon
la source
Je suis désolé, je pensais qu'il était clair que je parlais d'un client quand j'ai commencé à parler de framerate.
Alakanu
1
Mais le client n'est jamais le problème. Le client est très non massif et très local, il a besoin d'en savoir beaucoup moins que le serveur. Idéalement, le client connaît le nœud de partitionnement spatial (quel qu'il soit, disons, la grille) dans lequel se trouve le lecteur, et ceux qui l'entourent immédiatement, et c'est tout. Les mises à jour sont donc très modestes à moins qu'un millier de joueurs ne se côtoient. Normalement, vous n'avez besoin que de deltas pour un ou deux objets et du contenu d'un nouveau nœud de grille après vous être déplacé dans une direction sur plus de la moitié de la largeur d'un nœud. Tout le reste: pas votre problème.
Damon
1
Le fait est qu'être trop intelligent peut être une idée extrêmement stupide. Votre monde est nécessairement déjà divisé spatialement, avec un nombre gérable d'objets dans chaque nœud. Les processeurs modernes (et les GPU encore plus) sont bons pour traiter séquentiellement des volumes de données SoA. Ils n'aiment pas les branchements incohérents, et ils aiment encore moins l'accès incohérent à la mémoire - ce qui est pourtant exactement ce que fait "seul processus à proximité". Pour les nombres gérables (quelques centaines, quelques milliers), «tout traiter» dans une cellule est parfaitement adéquat, et probablement la meilleure chose que vous puissiez faire.
Damon
1
@Alakanu Cela ressemble à une réponse détaillée et complète à votre question, pour moi. Si vous pensez que ce n'est pas une réponse, soit vous l'avez mal comprise, soit votre question est si peu claire qu'elle a été mal comprise par Damon, moi et toutes les personnes qui ont voté en faveur de cette réponse.
David Richerby
2
@Alakanu Vous passez vraiment énormément de temps à vous plaindre aux gens qui essaient de vous aider. Bonne chance avec ça.
David Richerby
2

@ T.Sar écrit dans un commentaire que vous devriez vous pencher sur le concept de "morceau chargé" de Minecrafts pour plus d'informations. Si vous le faites, sachez que c'est assez compliqué dans Minecraft à cause des gens qui construisent des machines dans le jeu.

Une version très simplifiée suit:

Le monde est divisé en régions carrées (morceaux). Dans Minecraft, il y a également une division en hauteur, mais la plupart des mmos n'en ont pas besoin.

Le client du jeu ne se soucie que des régions proches du joueur. C'est beaucoup plus simple que de dessiner un cercle autour du joueur, mais parfaitement suffisant.

Dans Minecraft, les régions sont des blocs 16x16, et le client connaît les régions 9x9, 4 régions dans chaque direction. (4 régions est + le joueur régional est dans + 4 régions ouest = 9 régions au total. Même nord / sud)

Il n'y a rien de magique dans ces chiffres, utilisez ce qui a du sens dans votre jeu.

Le client anime uniquement les choses à l'intérieur de cette zone. Le serveur ne calcule que des choses comme les monstres errants dans les régions proches d' un joueur.

Lorsqu'un joueur se promène à l'intérieur d'une région, rien de spécial ne se produit, lorsqu'il traverse une frontière de région, le «bord de l'animation» est repoussé d'une région. Le client doit alors demander au serveur quelles régions il voit maintenant.

Il n'y a rien de mal à avoir plusieurs limites d'animation imbriquées. Par exemple, les objets animés tombent dans une zone 3x3, les monstres errants dans une zone 5x5 et montrent simplement le paysage dans une zone 9x9.

Le serveur conserve une "version figée" des régions qu'aucun joueur ne voit. Si cela prend beaucoup de mémoire, vous voudrez peut-être les décharger après un certain temps. Lorsqu'un joueur arrive ensuite, la région est rechargée sans que l'objet ne tombe. Vous devez être plus rapide la prochaine fois, joueur 1.

Stig Hemmer
la source
La distance de dessin est réglable mais dans ce cas, 8 morceaux sont 9x9 et sont une décision côté client, cela peut cependant informer le serveur d'accélérer les choses (afin que le serveur n'envoie pas de données que le client ne rendra pas). Aussi ... Doom et Quake n'ont-ils pas résolu ce problème de rendu qui n'a de sens?
SparK
À propos de la disparition des objets déposés ... dans Minecraft, l'objet ne "vieillit" que lorsqu'un joueur se trouve à proximité. Vous pouvez donc "enregistrer" un élément déposé dans un bloc non chargé et le récupérer plus tard.
SparK