Lors des Pixonic DevGAMM Talks, notre DTO Anton Grigoriev a également pris la parole. Nous, dans l'entreprise, avons déjà dit que nous travaillions sur un nouveau jeu de tir PvP et Anton a partagé certaines des nuances de l'architecture de ce projet. Il a expliqué comment construire le développement de maniÚre à ce que les changements dans la logique de jeu du client apparaissent automatiquement sur le serveur (et vice versa), et s'il est possible de ne pas écrire de code, mais de minimiser le trafic. Vous trouverez ci-dessous un enregistrement et une transcription du rapport.
Je nâapprendrai pas Ă faire quelque chose, je parlerai de la façon dont nous lâavons fait. Pour ne pas marcher sur le mĂȘme rĂąteau et profiter de notre expĂ©rience. Il y a un an et demi, nous, dans l'entreprise, ne savions pas comment fabriquer des tireurs sur des tĂ©lĂ©phones portables. Vous dites comment c'est, vous avez des robots de guerre, 100 millions de tĂ©lĂ©chargements, 1,5 million de DAU. Mais dans ce jeu, les robots sont trĂšs lents, et nous voulions faire un tireur rapide, et l'architecture de War Robots ne le permettait pas.
Nous savions comment et quoi faire, mais nous n'avions aucune expĂ©rience. Ensuite, nous avons embauchĂ© une personne qui a eu cette expĂ©rience et lui avons dit: faites la mĂȘme chose que vous avez dĂ©jĂ fait cent fois, mais en mieux. Puis ils se sont assis et ont commencĂ© Ă penser Ă l'architecture.

Entré dans le systÚme de composants d'entité (ECS). Je pense que beaucoup de gens savent ce que c'est. Tous les objets du monde sont représentés par des entités. Par exemple, un joueur, son arme, un objet sur la carte. Ils ont des propriétés décrites par les composants. Par exemple, le composant Transformer est la position du joueur dans l'espace et le composant Santé est sa santé. Il y a une logique - elle est séparée et représentée par des systÚmes. En rÚgle générale, les systÚmes sont la méthode Execute (), qui passe à travers les composants d'un certain type et fait quelque chose avec eux, avec le monde du jeu. Par exemple, MoveSystem passe en revue tous les composants de Movement, examine la vitesse dans ce composant, le paramÚtre et, sur cette base, calcule la nouvelle position de l'objet, c'est-à -dire l'écrit dans Transform.
Une telle architecture a ses propres caractéristiques. Lors du développement sur ECS, vous devez penser et faire les choses différemment. L'un des avantages est la composition plutÎt que l'héritage multiple. Rappelez-vous ce rhombique avec héritage multiple en C ++? Tous ses problÚmes. Ce n'est pas le cas chez ECS.

La deuxiÚme caractéristique est la séparation de la logique et des données, dont j'ai déjà parlé. Qu'est-ce que cela nous donne? Nous pouvons stocker l'état du monde et son histoire par lots, nous pouvons le sérialiser, nous pouvons envoyer ces données sur le réseau et les modifier en temps réel. Ce ne sont que des données en mémoire - nous pouvons modifier n'importe quelle valeur à tout moment. Ainsi, il est trÚs pratique de changer la logique du jeu (ou pour le débogage).
Il est Ă©galement trĂšs important de garder une trace de l'ordre des appels systĂšme. Tous les systĂšmes se succĂšdent, sont appelĂ©s par la mĂ©thode Execute () et, idĂ©alement, doivent ĂȘtre indĂ©pendants. En pratique, cela ne se produit pas. Un systĂšme change quelque chose dans le monde, un autre systĂšme l'utilise ensuite. Et si nous cassons cet ordre, le jeu se dĂ©roulera diffĂ©remment. Probablement pas beaucoup, mais certainement pas le mĂȘme qu'auparavant.
Enfin, l'une des fonctionnalitĂ©s principales et les plus importantes pour nous est que nous pouvons exĂ©cuter le mĂȘme code Ă la fois sur le client et sur le serveur.
Donnez au dĂ©veloppeur une opportunitĂ©, et il trouvera 99 façons et raisons de prendre sa dĂ©cision, et n'utilisera pas celles existantes. Je pense que beaucoup l'ont fait. Nous recherchions le cadre ECS Ă cette Ă©poque. Nous avons considĂ©rĂ© Entitas, Artemis C #, Ash.net et notre propre solution, qui pourrait ĂȘtre Ă©crite Ă partir de l'expĂ©rience d'un spĂ©cialiste qui est venu chez nous.

N'essayez pas de lire ce qui est écrit sur la diapositive, ce n'est pas si important. L'important est la quantité de vert et de rouge dans les colonnes. Vert signifie que la solution prend en charge les exigences, rouge - ne prend pas en charge, jaune - prend en charge, mais pas tout à fait.
Dans la colonne, ECS est potentiellement notre solution. Comme vous pouvez le voir, il fait plus frais - nous pourrions prendre en charge beaucoup plus d'exigences. En consĂ©quence, nous n'avons pas soutenu certains d'entre eux (principalement parce qu'ils n'Ă©taient pas nĂ©cessaires), et certains, sans lesquels nous ne pourrions pas continuer Ă travailler, ont dĂ» ĂȘtre faits. Nous avons choisi l'architecture, travaillĂ© longtemps, rĂ©alisĂ© une version minimalement jouable et ... fakap.

Il s'est avéré que la version la plus injouable. Le joueur recule constamment, freine, le serveur se bloque au milieu du match. Il était impossible de le jouer. Quelles étaient les raisons des échecs?
La raison n ° 1 et la plus importante est l'inexpérience. Mais comment ça? Nous avons embauché une personne expérimentée qui était censée tout faire magnifiquement. Oui, mais en réalité nous ne lui avons donné qu'une partie du travail. Nous avons dit: "Voici un serveur de jeu pour vous, travaillez dessus." Et dans notre architecture (plus à ce sujet plus tard), le client joue un rÎle trÚs important. Et c'est cette partie que nous avons donnée à un homme qui n'avait pas l'expérience nécessaire. Non, c'est un bon programmeur, sénateur - il n'y avait tout simplement aucune expérience. C'est-à -dire il n'avait aucune idée du genre de rùteau qu'il pouvait y avoir.
Raison n ° 2 - allocations irrĂ©alistes. 80 Ko / trame. Est-ce beaucoup ou pas? Si nous prenons en compte le fait que nous avons 30 images par seconde, alors en une seconde nous obtenons 2,5 Mo, et pour un match de 5 minutes, il y a dĂ©jĂ plus de 600 Mo. Bref, beaucoup. Le garbage collector commence Ă essayer intensĂ©ment de libĂ©rer toute cette mĂ©moire (quand on en demande de plus en plus), ce qui conduit Ă des pics. Ătant donnĂ© que nous voulions 30 images par seconde, ces pointes nous ont beaucoup gĂȘnĂ©s. De plus, Ă la fois sur le client et sur le serveur.
La principale raison des allocations est que nous avons constamment alloué des tableaux de données. Presque à chaque fois à chaque image. Utilisé LINQ, les expressions lambda et Photon. Photon est une bibliothÚque réseau que nous connaissons et utilisons dans War Robots. Et tout semble aller bien, mais il alloue de la mémoire à chaque fois qu'il envoie ou reçoit des données.
Si nous avons réglé les premiers problÚmes (réécrits dans nos collections personnalisées, mis en cache), il n'y avait pratiquement rien à faire avec Photon, car il s'agit d'une bibliothÚque tierce. Il était seulement possible de réduire la taille des paquets, et nous avions 5 Ko. Beaucoup? Oui Il y a MTU - c'est la taille minimale réelle des paquets qui est envoyée via UDP, sans casser le paquet en petites parties. Il fait environ 1,5 kilo-octets et nous en avions 5 (en moyenne, il y en avait plus).
En conséquence, Photon a coupé notre colis en petits et a envoyé chaque piÚce comme fiable, c'est-à -dire avec livraison garantie. Chaque fois que la piÚce n'atteignait pas, il l'envoyait encore et encore. Nous avons encore plus de latence et le réseau a mal fonctionné.
Toutes ces allocations ont conduit au fait que nous avons reçu une trame d'environ 100 millisecondes quand 33 Ă©tait nĂ©cessaire et lĂ , rendu, simulation et autres actions - tout cela prend le CPU. Tous ces problĂšmes sont complexes, c'est-Ă -dire il Ă©tait impossible d'en dĂ©cider, et tout irait bien. Il fallait les rĂ©soudre tous en mĂȘme temps.
Et un autre petit problĂšme qui Ă©tait pendant le dĂ©veloppement - un grand nombre de rĂ©fĂ©rentiels. 5 est Ă©crit sur la diapositive, mais il me semble qu'il y en avait encore plus. Tous ces rĂ©fĂ©rentiels (pour le client, le serveur de jeu, le code commun, les paramĂštres et quelque chose d'autre) ont Ă©tĂ© connectĂ©s par des sous-modules aux deux rĂ©fĂ©rentiels principaux pour le client et le serveur de jeu. C'Ă©tait difficile de travailler avec. Les programmeurs peuvent travailler avec Git, SVN, mais il y a aussi des artistes, des designers, etc. Je pense que beaucoup ont essayĂ© d'enseigner Ă un artiste ou un designer comment travailler avec un systĂšme de contrĂŽle de version. C'est vraiment difficile, donc si votre designer sait comment le faire - prenez soin de lui, c'est un employĂ© prĂ©cieux. Dans notre cas, mĂȘme les programmeurs ont paniquĂ© et, par consĂ©quent, nous avons tout rĂ©duit Ă un seul rĂ©fĂ©rentiel.
C'était une excellente solution au problÚme. Nous avons là un dossier avec un serveur et un dossier avec un client. Le serveur se compose d'un projet de serveur de jeu, d'un générateur de code et d'outils auxiliaires.

Un client est un client Unity et un code commun. Un code commun est une structure de données mondiale, c'est-à -dire Entités, composants et simulation systÚme. Ce code est principalement généré par le générateur de serveur. Il est utilisé par le serveur. C'est-à -dire Il s'agit d'une partie commune pour le client et le serveur.
La vie. Nous prenons TeamCity, le mettons sur notre référentiel, collectons et déployons le serveur. Chaque fois qu'un client change de logique générale, un serveur de jeu est assemblé ici - maintenant, un programmeur de serveur n'est plus nécessaire pour cela. Il y a généralement un serveur, un client et certaines fonctionnalités. Le client le voit à la maison, le serveur à la maison, et un jour cela fonctionnera pour eux. Dans notre cas, ce n'est pas le cas - le client peut écrire cette fonctionnalité et tout fonctionne sur le serveur.
Le match se compose d'une partie commune (désignée comme ECS) et d'une présentation (ce sont des classes unitaires de MonoBehavior, des GameObjects, des modÚles, des effets - tout ce que le monde représente). Ils ne sont pas connectés.

Entre eux, il y a des prĂ©sentateurs, qui fonctionnent avec les deux parties. Comme vous le comprenez, il s'agit de MVP (Model-View-Presenter) et n'importe laquelle de ces piĂšces peut ĂȘtre remplacĂ©e si nĂ©cessaire. Il y a une autre partie qui fonctionne avec le rĂ©seau (sur la diapositive - RĂ©seau). Il s'agit de la sĂ©rialisation des informations sur le monde, de la sĂ©rialisation des entrĂ©es, de l'envoi au serveur, de la rĂ©ception par le serveur, de la connexion au serveur, etc.
Plus de likes. Nous prenons et remplaçons cette partie par un package qui n'est pas réel, sur le réseau, mais virtuel. Nous créons un objet à l'intérieur du client et lui envoyons des messages. Il implémente une simulation de serveur - maintenant cet objet fait tout ce qui s'est passé sur le serveur de jeu. Les joueurs restants sont remplacés par des bots.

C'est fait. Nous avons obtenu le jeu et la possibilité de le tester sans serveur de jeux. Qu'est-ce que cela signifie? Cela signifie que l'artiste, aprÚs avoir fait un nouvel effet, peut cliquer sur le bouton Lecture dans l'éditeur, accéder immédiatement au match sur la carte et voir comment cela fonctionne. Ou déboguez pour les programmeurs clients ce qu'ils ont écrit.
Mais nous sommes allés plus loin et attaché à cette couche l'émulation du jitter ping des retards réseau (c'est lorsque les paquets sur le réseau n'arrivent pas dans l'ordre dans lequel ils ont été envoyés) et d'autres choses sur le réseau. En conséquence, nous avons obtenu un match pratiquement réel sans serveur de jeu. Cela fonctionne, vérifié.
Revenons à la génération de code.

J'ai dĂ©jĂ dit que nous avons un gĂ©nĂ©rateur de code dans un serveur de jeux. Il existe un langage spĂ©cifique au domaine, qui est en fait une simple classe C #. Dans ce cas, la classe SantĂ©. Nous le marquons avec nos attributs. Par exemple, il existe un attribut de composant. Il dit que la santĂ© est une composante de notre monde. Sur la base de cet attribut, le gĂ©nĂ©rateur crĂ©era une nouvelle classe C # dans laquelle il y aura un tas de choses. Ils peuvent ĂȘtre Ă©crits Ă la main, mais cela va gĂ©nĂ©rer. Par exemple, la mĂ©thode d'ajout d'un composant Ă Entity, la mĂ©thode de recherche de composants, la sĂ©rialisation des donnĂ©es, etc. Il existe un attribut du type DontSend, qui indique qu'il n'est pas nĂ©cessaire d'envoyer un champ sur le rĂ©seau - le serveur n'en a pas besoin ou le client n'en a pas besoin. Ou l'attribut Mach, qui indique que le joueur a une valeur de santĂ© maximale de mille. Qu'est-ce que cela nous donne? Au lieu d'un champ qui occupe 32 bits (int), nous envoyons 10 bits - trois fois moins. Un tel gĂ©nĂ©rateur de code nous a permis de rĂ©duire la taille des paquets de 5 Ko Ă 1.

1 Ko <1,5 - c'est-à -dire Nous avons rencontré le MTU. Photon a cessé de couper et le réseau s'est beaucoup amélioré. Presque tous ses problÚmes ont disparu. Mais nous sommes allés plus loin et avons fait une compression delta.

C'est lorsque vous envoyez un Ă©tat complet, puis uniquement ses modifications. Il nâarrive pas que le monde entier ait immĂ©diatement complĂštement changĂ©. Seules certaines parties changent constamment et ces changements sont beaucoup plus petits que l'Ă©tat lui-mĂȘme. Nous avons reçu en moyenne 300 octets, soit 17 fois moins qu'Ă l'origine.
Pourquoi est-ce nĂ©cessaire si vous ĂȘtes dĂ©jĂ entrĂ© dans MTU? Le jeu est en constante Ă©volution, de nouvelles fonctionnalitĂ©s apparaissent, et avec elles apparaissent des objets, des entitĂ©s, de nouveaux composants. La taille des donnĂ©es augmente. Si nous nous arrĂȘtions Ă 1 Ko, nous reviendrions trĂšs bientĂŽt au mĂȘme problĂšme. Maintenant, aprĂšs l'avoir réécrit pour la compression delta, nous n'atteindrons pas cela trĂšs bientĂŽt.
Maintenant, la partie la plus douce. Sync Si vous jouez à des tireurs, vous savez ce qu'est le décalage d'entrée - lorsque vous cliquez sur le bouton, et le personnage commence à se déplacer aprÚs un certain temps, par exemple, une demi-seconde. Pour certains jeux du genre mob, c'est normal. Mais dans le jeu de tir, vous voulez que le héros tire et fasse des dégùts juste là .

Pourquoi Input Lag se produit-il? Le client recueille l'entrĂ©e du joueur (entrĂ©e) et l'envoie au serveur de jeu (l'envoi prend du temps). Ensuite, le serveur de jeu le traite (Ă nouveau, le temps) et renvoie le rĂ©sultat (Ă nouveau, le temps). C'est un retard. Comment l'enlever? Il y a une chose appelĂ©e prĂ©diction - le client n'attend pas de rĂ©ponse du serveur et commence immĂ©diatement Ă essayer de faire la mĂȘme chose que le serveur de jeu, c'est-Ă -dire feint. Prend l'entrĂ©e du joueur et dĂ©marre la simulation. Nous simulons uniquement un client local, car nous ne connaissons pas la contribution des autres joueurs - ils ne viennent pas Ă nous. Par consĂ©quent, nous exĂ©cutons le systĂšme de simulation uniquement sur notre lecteur.
Tout d'abord, cela permet de rĂ©duire le temps de simulation. Le client dĂ©marre la simulation dĂšs qu'il reçoit l'entrĂ©e et a plusieurs Ă©tapes d'avance par rapport au serveur de jeu. Disons que sur cette image, il simule le tick # 20. Ă ce stade, le serveur de jeu simule le tick # 15 dans le passĂ©. Le client voit le reste du monde, encore une fois, dans le passĂ©, lui-mĂȘme - dans le futur. Pendant qu'il envoie le 20Ăšme tick au serveur, alors que cette entrĂ©e atteint, le serveur de jeu commencera dĂ©jĂ Ă simuler le 18Ăšme tick ou dĂ©jĂ le 20Ăšme. Si le 18, il le met dans le tampon, atteint le 20, traite et renvoie le rĂ©sultat.
Disons maintenant qu'il simule la tique n ° 15. TraitĂ©e, renvoie le rĂ©sultat au client. Le client a une sorte de 15e tick, 15e Ă©tat de jeu simulĂ© et le monde du jeu qu'il a prĂ©dit. La comparaison avec le serveur commence. En fait, il ne compare pas le monde entier, mais seulement son client, car nous ne sommes pas responsables du reste du monde. Nous ne sommes responsables que de nous-mĂȘmes. Si le joueur coĂŻncide, tout va bien, ce qui signifie que nous avons correctement simulĂ©, la physique a fonctionnĂ© correctement et aucune collision n'est survenue. Ensuite, nous continuons Ă simuler le 20Ăšme tick, le 21Ăšme et ainsi de suite.
Si le client / joueur ne correspond pas, cela signifie que nous nous sommes trompĂ©s quelque part. Exemple: la physique n'Ă©tant pas dĂ©terministe, elle n'a pas correctement calculĂ© notre position ou quelque chose s'est produit. Peut-ĂȘtre juste un bug. Ensuite, le client prend l'Ă©tat du serveur de jeu, car le serveur de jeu l'a dĂ©jĂ confirmĂ© (il fait confiance au serveur - s'il ne faisait pas confiance, les joueurs tricheraient), et simulerait le reste du 15 au 20. Parce que cette branche temporelle est dĂ©sormais erronĂ©e.
Créez une nouvelle branche horaire, c.-à -d. mondes parallÚles. Nous resimulons ces cinq ticks en un seul tick. Une fois que notre simulation a pris 5 millisecondes, mais si nous devons simuler 10 ticks, c'est déjà 50 millisecondes et nous ne tombons pas dans nos 30 millisecondes. Ils ont optimisé et obtenu une milliseconde - maintenant 10 ticks sont traités en 10 millisecondes. Parce qu'il y a encore du rendu.
Toutes ces choses fonctionnent sur le client, et nous l'avons donné à la personne sans l'expérience nécessaire. Moins - nous avions un fakap, et plus - que le programmeur sait maintenant comment le faire correctement.

Ce schĂ©ma a ses propres caractĂ©ristiques. Le client sur la photo de gauche tente de traquer l'ennemi. Il est au 20Ăšme tick, l'adversaire est au 15Ăšme tick. Parce que ping et le client ont 5 ticks d'avance sur le serveur. Le client tire et doit frapper avec prĂ©cision et causer des dommages, peut-ĂȘtre mĂȘme un tir Ă la tĂȘte. Mais l'image est diffĂ©rente sur le serveur - lorsque le serveur commence Ă simuler le 20e tick, l'ennemi peut dĂ©jĂ se dĂ©placer. Par exemple, si l'ennemi se dĂ©plaçait. En thĂ©orie, nous ne devrions pas obtenir. Mais si cela fonctionnait comme ça, personne ne jouerait aux tireurs en ligne en raison de ratĂ©s constants. Selon le ping, la probabilitĂ© de frapper a Ă©galement changĂ©: plus le ping est mauvais, pire vous obtenez. Par consĂ©quent, ils le font diffĂ©remment.
Le serveur prend et roule le monde entier dans le teck dans lequel le joueur a vu le monde. Le serveur sait quand il Ă©tait, le ramĂšne au 15Ăšme tick et voit l'image de gauche. Il voit que le joueur devrait avoir touchĂ© et inflige des dĂ©gĂąts Ă son adversaire dĂ©jĂ dans le 20e tick. Tout va bien. Presque. Si l'ennemi s'est enfui et a couru derriĂšre un obstacle, alors nous avons dĂ©jĂ tirĂ© Ă la tĂȘte Ă travers le mur. Mais c'est un problĂšme connu, les joueurs le savent et ne s'inquiĂštent pas. Donc ça marche, il n'y a rien Ă faire.

Ainsi, nous avons atteint 30 ticks par seconde, 30 images par seconde. Maintenant, environ 600 joueurs jouent sur notre serveur en mĂȘme temps. Il y a 6 joueurs dans le match, soit environ 100 matchs. Nous n'avons pas de programmeur serveur, nous n'en avons pas besoin. Les clients Ă©crivent toute la logique dans l'Ă©diteur Unity, Rider, en C # et cela fonctionne sur un serveur de jeux. Presque toujours. Nous avons rĂ©duit la taille des paquets de 17 fois et rĂ©duit les allocations de mĂ©moire de 80 fois - maintenant mĂȘme moins d'un kilo-octet sur le client et le serveur. Le ping moyen Ă©tait de 200 Ă 250 ms, maintenant il est de 150. 200 est la norme pour les jeux en rĂ©seau mobile, contrairement Ă un PC, oĂč tout se passe beaucoup plus rapidement, en particulier sur un rĂ©seau local.

Nous prĂ©voyons d'isoler ce qui est Ă©crit dans un cadre distinct pour l'utiliser sur d'autres projets. Mais jusqu'Ă prĂ©sent, il n'est pas question d'Open Source. Et ajoutez-y l'interpolation. Maintenant, nous avons 30 ticks par seconde, nous pouvons dessiner au fur et Ă mesure. Mais il y a des jeux oĂč 20 ticks par seconde ou 10 suffisent. Par consĂ©quent, si nous tirons 10 fois par seconde, les personnages se dĂ©placeront par Ă -coups. Par consĂ©quent, une interpolation est nĂ©cessaire. Nous avons Ă©crit notre propre bibliothĂšque rĂ©seau au lieu de Photon - il n'y a pas d'allocations de mĂ©moire lĂ -bas.
Il y a encore des parties que vous ne pouvez pas Ă©crire avec vos mains, mais gĂ©nĂ©rer du code. Par exemple, lorsque nous envoyons l'Ă©tat du monde Ă un client, nous supprimons les donnĂ©es dont il n'a pas besoin. Pendant que nous le faisons avec nos mains et lorsqu'une nouvelle fonctionnalitĂ© apparaĂźt, et que nous oublions de supprimer ces donnĂ©es, quelque chose ne va pas. En fait, cela peut ĂȘtre gĂ©nĂ©rĂ© en marquant un attribut.
Questions du public
- Qu'utilisez-vous pour la génération de code? Votre propre décision?- Tout est simple - les mains. Nous avons pensé à préparer quelque chose, mais il s'est avéré plus rapide d'écrire de nos propres mains. Suivez ce chemin, cela a bien fonctionné à l'époque et maintenant.
- Vous avez refusĂ© le dĂ©veloppeur du serveur, mais vous n'avez pas seulement rĂ©duit le temps de dĂ©veloppement car le mĂȘme code est rĂ©utilisĂ©. Unity ne prend pas en charge la derniĂšre version de C #, il a son propre moteur sous le capot. Vous ne pouvez pas utiliser .NET Core, vous ne pouvez pas utiliser les derniĂšres fonctionnalitĂ©s, certaines structures et plus encore. La performance n'en souffre-t-elle pas environ un tiers?- Lorsque nous avons commencĂ© Ă faire tout cela, nous pensions que pour utiliser non pas des classes, mais des structures, cela aurait dĂ» fonctionner beaucoup plus rapidement. Nous avons Ă©crit un prototype de l'apparence du code, de la façon dont les programmeurs utiliseront ces structures pour Ă©crire la logique. Et c'Ă©tait terriblement inconfortable. Nous nous sommes installĂ©s sur les classes et la performance que nous avons maintenant nous suffit.
- Comment vivez-vous maintenant sans interpolation? Et comment prĂ©tendez-vous ĂȘtre un joueur si l'instantanĂ© ne vient pas dans le bon cadre?- Nous avons une interpolation, seulement elle n'est pas visuelle, mais sur les paquets qui arrivent sur le rĂ©seau. Disons que nous avons les 18e, 19e et 20e Ă©tats.
Le 18 est arrivé, le 20 est arrivé, et le 19 s'est perdu ou n'a pas encore atteint - nous l'interpolons ici. Utilisez simplement la génération de code afin de ne pas écrire le code d'interpolation.- Y a-t-il d'autres astuces de vie pour compresser plus fortement les quaternions?- J'ai parlé de 2D - il n'y a qu'un angle alpha, et sur les nouveaux projets, les quaternions ont aussi leurs propres problÚmes. Des hacks de vie, je peux également dire ceci: puisque nous utilisons UDP pour que l'entrée ne soit pas perdue, nous l'envoyons par lots: de zéro à cinquiÚme entrée, puis de premiÚre à sixiÚme et ainsi de suite. C'est moins cher et l'expédition est meilleure."Mais l'ordre de reproduction des entrées sur le serveur joue un rÎle?""Oui, bien sûr." - ( , , ), 2 : , .
â . ? ? 1000 , ? , ?â , . , -, , . , 30 .
â , ?- Bien sĂ»r. , ( ), . â , , . - , , . , , , , . .
â ECS, , ? ?â 30 , . 80 , . .
â prediction. 20- - , , - â , ? , . - ?â : . (, 15-) 16-,17-,18- .
â ?â , . , , . Entity ( ), . â , . ID , .
â - â , , , ? , ?â , , . 3D , â , , - . , , . top-down, â . . , , , . .
â ?â . Cela arrive aussi.
â , , - . - . - , , , 500 , , - - . ?â .

, .. 20- 20- , . â , . : 20- , ? , . , â - . , . , « - , 21-, 18-». : «, - ». .
â .. , ?â , .
â reliable UDP â - ?â Photon, Photon reliable UDP, unreliable, c .
â ?â , -. , . , . , . , . 100%, , 80%, .
â ?â , , Photon , MTU.
â ? ?â , , , . . , . , , , .
â , , ?â , . , . , , . , - . , . , .
â / â - . , .â , . , ( ), -, , -. , . â , .
â , - . ?â , . : ECS, . , ECS . , ECS . , . , , , ( , , , , , ). 2D , , 3D â . 3D , , . . - , . , - -, .
â , ECS , . , , C#?â â .
â .. ES ? , ECS â , , , . .. ECS â , .â , , . , . â , , . , O - , , .
â , ECS- ?â -, ECS , , ( ) â , . , â . â , , . , , , ..
Pixonic DevGAMM Talks