Wéi mir d'Mechanik vu ballistesche Berechnungen fir e mobilen Shooter mat engem Netzwierklatenzkompensatiounsalgorithmus verbessert hunn

Wéi mir d'Mechanik vu ballistesche Berechnungen fir e mobilen Shooter mat engem Netzwierklatenzkompensatiounsalgorithmus verbessert hunn

Salut, ech sinn den Nikita Brizhak, e Serverentwéckler vu Pixonic. Haut wëll ech iwwer d'Kompensatioun vun der Lag am mobilen Multiplayer schwätzen.

Vill Artikele goufen iwwer Serverlag Kompensatioun geschriwwen, och op Russesch. Dëst ass net iwwerraschend, well dës Technologie aktiv an der Schafung vu Multiplayer FPS zënter de spéiden 90er benotzt gouf. Zum Beispill kënnt Dir un de QuakeWorld Mod erënneren, deen ee vun deenen éischten war fir se ze benotzen.

Mir benotzen et och an eisem mobilen Multiplayer Shooter Dino Squad.

An dësem Artikel ass mäi Zil net ze widderhuelen wat scho dausend Mol geschriwwe gouf, mee ze soen wéi mir Lag Kompensatioun an eisem Spill implementéiert hunn, andeems Dir eis Technologiestack a Kär Spillfeatures berücksichtegt.

E puer Wierder iwwer eis Cortex an Technologie.

Dino Squad ass en Netzwierk mobilen PvP Shooter. D'Spiller kontrolléieren Dinosaurier equipéiert mat enger Vielfalt vu Waffen a kämpfen sech an 6v6 Teams.

Souwuel de Client wéi de Server baséieren op Unity. D'Architektur ass zimmlech klassesch fir Shooter: de Server ass autoritär, an d'Client Prognose funktionnéiert op de Clienten. D'Spillsimulatioun gëtt mat interner ECS geschriwwen a gëtt souwuel um Server wéi och um Client benotzt.

Wann dëst déi éischte Kéier ass datt Dir iwwer Lag Kompensatioun héieren hutt, hei ass e kuerzen Ausfluch an d'Thema.

A Multiplayer FPS Spiller gëtt de Match normalerweis op engem Fernserver simuléiert. D'Spiller schécken hir Input (Informatioun iwwer d'Schlësselen gedréckt) op de Server, an als Äntwert schéckt de Server hinnen eng aktualiséiert Spillstat, déi de kritt Donnéeë berücksichtegt. Mat dësem Interaktiounsschema ass d'Verspéidung tëscht dem Forward-Taste an de Moment wou de Spiller Charakter sech um Bildschierm bewegt ëmmer méi grouss wéi de Ping.

Wärend op lokalen Netzwierker kann dës Verzögerung (populär Inputlag genannt) onmerkbar sinn, wann Dir iwwer den Internet spillt, entsteet et e Gefill vu "op Äis rutschen" wann Dir e Charakter kontrolléiert. Dëse Problem ass duebel relevant fir mobil Netzwierker, wou de Fall, wann de Ping vum Spiller 200 ms ass, nach ëmmer als eng exzellent Verbindung ugesi gëtt. Dacks kann de Ping 350, 500 oder 1000 ms sinn. Da gëtt et bal onméiglech e schnelle Shooter mat Input Lag ze spillen.

D'Léisung fir dëse Problem ass Client-Säit Simulatiounsprognose. Hei applizéiert de Client selwer den Input fir de Spiller Charakter, ouni op eng Äntwert vum Server ze waarden. A wann d'Äntwert kritt ass, vergläicht et einfach d'Resultater an aktualiséiert d'Positiounen vun de Géigner. D'Verspéidung tëscht engem Schlëssel drécken an d'Resultat um Écran an dësem Fall ze weisen ass minimal.

Et ass wichteg d'Nuance hei ze verstoen: de Client zitt sech ëmmer no sengem leschten Input, a Feinde - mat Netzwierkverzögerung, laut dem fréiere Staat vun den Daten vum Server. Dat ass, wann Dir op e Feind schéisst, gesäit de Spiller hien an der Vergaangenheet relativ zu sech selwer. Méi iwwer Client Prognose mir geschriwwen virdrun.

Sou léist Client Prognose ee Problem, awer schaaft en aneren: wann e Spiller op de Punkt schéisst wou de Feind an der Vergaangenheet war, op de Server wann se op dee selwechte Punkt schéisst, kann de Feind net méi op där Plaz sinn. Serverlag Kompensatioun probéiert dëse Problem ze léisen. Wann eng Waff ofgeschoss gëtt, restauréiert de Server de Spillzoustand, deen de Spiller an der Zäit vum Schoss lokal gesinn huet, a kontrolléiert ob hien wierklech de Feind getraff hätt. Wann d'Äntwert "Jo" ass, gëtt den Hit gezielt, och wann de Feind zu deem Zäitpunkt net méi um Server ass.

Arméi mat dësem Wëssen hu mir ugefaang Serverlag Kompensatioun am Dino Squad ëmzesetzen. Éischt vun all, mir hu misse verstoen wéi op de Server ze restauréieren wat de Client gesinn huet? A wat muss genee restauréiert ginn? An eisem Spill, Hits vu Waffen a Fäegkeeten ginn duerch Strahlen an Iwwerlager berechent - dat heescht duerch Interaktioune mat de kierperleche Kollidere vum Feind. Deementspriechend musse mir d'Positioun vun dëse Kollideren reproduzéieren, déi de Spiller lokal "gesi" huet, um Server. Zu där Zäit hu mir Unity Versioun 2018.x benotzt. D'Physik API do ass statesch, déi kierperlech Welt existéiert an enger eenzeger Kopie. Et gëtt kee Wee fir säin Zoustand ze späicheren an et dann aus der Këscht ze restauréieren. Also wat ze maachen?

D'Léisung war op der Uewerfläch; all seng Elementer ware scho vun eis benotzt fir aner Probleemer ze léisen:

  1. Fir all Client musse mir wëssen op wéi enger Zäit hien Géigner gesinn huet wann hien d'Schlësselen gedréckt huet. Mir hunn dës Informatioun schonn an den Input Package geschriwwen a benotzt fir d'Client Prognose unzepassen.
  2. Mir mussen fäeg sinn d'Geschicht vu Spillstaaten ze späicheren. Et ass an et datt mir d'Positioune vun eise Géigner halen (an dofir hir Kollideren). Mir haten schonn eng Staat Geschicht op de Server, mir benotzt et ze bauen deltas. Wësse déi richteg Zäit, kënne mir einfach de richtege Staat an der Geschicht fannen.
  3. Elo datt mir de Spillzoustand aus der Geschicht an der Hand hunn, musse mir Spillerdaten mat dem Zoustand vun der kierperlecher Welt synchroniséieren. Bestehend colliders - réckelen, vermësst - schafen, onnéideg - zerstéieren. Dës Logik war och scho geschriwwe ginn a bestoung aus e puer ECS Systemer. Mir hunn et benotzt fir verschidde Spillraim an engem Unity Prozess ze halen. A well déi kierperlech Welt eng pro Prozess ass, huet se missen tëscht Zëmmeren nei benotzt ginn. Virun all Tick vun der Simulatioun, "zrécksetzen" mir den Zoustand vun der kierperlecher Welt an hunn se mat Daten fir den aktuelle Raum nei initialiséiert, a probéiert Unity Spillobjekter sou vill wéi méiglech duerch e schlau Pooling System ze benotzen. Alles wat blouf war déiselwecht Logik fir de Spillstaat aus der Vergaangenheet opzeruffen.

Andeems Dir all dës Elementer zesummesetzt, hu mir eng "Zäitmaschinn" kritt, déi den Zoustand vun der kierperlecher Welt op de richtege Moment zréckrolle konnt. De Code huet sech als einfach erausgestallt:

public class TimeMachine : ITimeMachine
{
     //История игровых состояний
     private readonly IGameStateHistory _history;

     //Текущее игровое состояние на сервере
     private readonly ExecutableSystem[] _systems;

     //Набор систем, расставляющих коллайдеры в физическом мире 
     //по данным из игрового состояния
     private readonly GameState _presentState;

     public TimeMachine(IGameStateHistory history, GameState presentState, ExecutableSystem[] timeInitSystems)
     {
         _history = history; 
         _presentState = presentState;
         _systems = timeInitSystems;  
     }

     public GameState TravelToTime(int tick)
     {
         var pastState = tick == _presentState.Time ? _presentState : _history.Get(tick);
         foreach (var system in _systems)
         {
             system.Execute(pastState);
         }
         return pastState;
     }
}

Alles wat blouf war erauszefannen wéi dës Maschinn ze benotzen fir Schëss a Fäegkeeten einfach ze kompenséieren.

Am einfachsten Fall, wann d'Mechanik op engem eenzegen Hitscan baséiert, schéngt alles kloer ze sinn: ier de Spiller schéisst, muss hien déi kierperlech Welt op de gewënschten Zoustand zréckrollen, e Raycast maachen, den Hit oder de Miss zielen, an d'Welt an den initialen Zoustand zréckginn.

Awer et gi ganz wéineg sou Mechanik am Dino Squad! Déi meescht vun de Waffen am Spill kreéieren Projektilen - laang lieweg Kugelen déi fir verschidde Simulatiounstécker fléien (an e puer Fäll Dosende vun Zecken). Wat maache mat hinnen, wéi vill Zäit solle se fléien?

В antike Artikel iwwer den Half-Life Netzwierkstack, hunn d'Jungs vu Valve déiselwecht Fro gefrot, an hir Äntwert war dëst: Projektillag Kompensatioun ass problematesch, an et ass besser et ze vermeiden.

Mir hunn dës Optioun net: Projektil-baséiert Waffen waren e Schlëssel Feature vum Spilldesign. Also hu mir eppes misse kommen. No e puer Brainstorming hu mir zwou Optiounen formuléiert déi schéngen ze schaffen:

1. Mir binden de Projektil un d'Zäit vum Spiller deen et erstallt huet. All Tick vun der Serversimulatioun, fir all Kugel vun all Spiller, rullen mir d'kierperlech Welt zréck an de Client Staat a maachen déi néideg Berechnungen. Dës Approche huet et méiglech eng verdeelt Belaaschtung op de Server an prévisibel Fluchzäit vu Projektilen ze hunn. Prévisibilitéit war besonnesch wichteg fir eis, well mir hunn all projectiles, dorënner feindlech projectiles, virausgesot op de client.

Wéi mir d'Mechanik vu ballistesche Berechnungen fir e mobilen Shooter mat engem Netzwierklatenzkompensatiounsalgorithmus verbessert hunn
Op der Foto schéisst de Spiller um Tick 30 eng Rakéit an Erwaardung: hie gesäit a wéi eng Richtung de Feind leeft a weess déi ongeféier Geschwindegkeet vun der Rakéit. Lokal gesäit hien, datt hien den 33. Tick op d'Zil getraff huet. Dank der Lag Kompensatioun wäert et och um Server schéngen

2. Mir maachen alles d'selwecht wéi an der éischter Optioun, awer, nodeems mir een Tick vun der Kugel Simulatioun gezielt hunn, stoppen mir net, awer si simuléiere weider säi Fluch am selwechte Server Tick, all Kéier bréngt seng Zäit méi no un de Server een duerch een Tick an Aktualiséierung vun collider Positiounen. Mir maachen dat bis eng vun zwou Saache geschitt:

  • D'Kugel ass ofgelaf. Dëst bedeit datt d'Berechnungen eriwwer sinn, mir kënnen eng Miss oder en Hit zielen. An dat ass bei deemselwechten Tick an deem de Schoss geschoss gouf! Fir eis war dëst souwuel e Plus an e Minus. E Plus - well fir de Schéissspiller dëst d'Verspéidung tëscht dem Hit an der Ofsenkung vun der Gesondheet vum Feind wesentlech reduzéiert huet. De Nodeel ass datt deeselwechten Effekt observéiert gouf wann d'Géigner op de Spiller geschoss hunn: de Feind, wéi et schéngt, huet nëmmen eng lues Rakéit gebrannt, an de Schued war scho gezielt.
  • D'Kugel huet Serverzäit erreecht. An dësem Fall wäert seng Simulatioun an der nächster Server Tick weider ouni Lag Kompensatioun. Fir lues projectiles, dëst kéint theoretesch d'Zuel vun de Physik rollbacks am Verglach zu der éischter Optioun reduzéieren. Zur selwechter Zäit ass d'ongläich Belaaschtung op der Simulatioun eropgaang: de Server war entweder Idle, oder an engem Servertick huet et eng Dosen Simulatiouns-Ticks fir verschidde Kugelen berechent.

Wéi mir d'Mechanik vu ballistesche Berechnungen fir e mobilen Shooter mat engem Netzwierklatenzkompensatiounsalgorithmus verbessert hunn
Déi selwecht Szenario wéi am virdrun Bild, mä berechent no der zweeter Schema. D'Rakéit huet d'Serverzäit an der selwechter Tick "gefaang" wéi de Schoss geschitt ass, an den Hit kann esou fréi wéi den nächsten Tick gezielt ginn. Beim 31. Tick, an dësem Fall, gëtt d'Lat Kompensatioun net méi ugewannt

An eiser Ëmsetzung ënnerscheeden dës zwou Approche an nëmmen e puer Zeilen vum Code, sou datt mir béid erstallt hunn, a fir eng laang Zäit existéiere se parallel. Ofhängeg vun der Mechanik vun der Waff an der Geschwindegkeet vun der Kugel, hu mir eng oder aner Optioun fir all Dinosaurier gewielt. De Wendepunkt hei war d'Erscheinung am Spill vun der Mechanik wéi "wann Dir de Feind sou vill Mol an esou enger Zäit trefft, kritt esou an esou e Bonus." All Mechaniker wou d'Zäit wou de Spiller de Feind getraff huet eng wichteg Roll gespillt huet refuséiert mat der zweeter Approche ze schaffen. Also hu mir endlech mat der éischter Optioun gaang, an et gëllt elo fir all Waffen an all aktiv Fäegkeeten am Spill.

Separat ass et derwäert d'Thema vun der Leeschtung ze erhéijen. Wann Dir geduecht hutt datt dat alles géif verlangsamen, äntweren ech: et ass. Unity ass zimmlech lues an der Beweegung vu Kollideren an auszeschalten an auszeschalten. Am Dino Squad, am "schlëmmste" Fall, kënnen e puer honnert Geschosse gläichzäiteg am Kampf existéieren. Beweegen Kollidere fir all Projektil individuell ze zielen ass en onbezuelbare Luxus. Dofir war et absolut néideg fir eis d'Zuel vun de Physik "Rollbacks" ze minimiséieren. Fir dëst ze maachen, hu mir eng separat Komponent an ECS erstallt an deem mir d'Spillzäit ophuelen. Mir hunn et un all Entitéiten bäigefüügt, déi Lag Kompensatioun erfuerderen (Projektiler, Fäegkeeten, etc.). Ier mir ufänken sou Entitéiten ze veraarbechten, clustere mir se zu dëser Zäit a veraarbecht se zesummen, rullt déi kierperlech Welt eemol fir all Cluster zréck.

Op dëser Etapp hu mir eng allgemeng funktionéierend System. Säi Code an enger e bësse vereinfacht Form:

public sealed class LagCompensationSystemGroup : ExecutableSystem
{
     //Машина времени
     private readonly ITimeMachine _timeMachine;

     //Набор систем лагкомпенсации
     private readonly LagCompensationSystem[] _systems;
     
     //Наша реализация кластеризатора
     private readonly TimeTravelMap _travelMap = new TimeTravelMap();

    public LagCompensationSystemGroup(ITimeMachine timeMachine, 
        LagCompensationSystem[] lagCompensationSystems)
     {
         _timeMachine = timeMachine;
         _systems = lagCompensationSystems;
     }

     public override void Execute(GameState gs)
     {
         //На вход кластеризатор принимает текущее игровое состояние,
         //а на выход выдает набор «корзин». В каждой корзине лежат энтити,
         //которым для лагкомпенсации нужно одно и то же время из истории.
         var buckets = _travelMap.RefillBuckets(gs);

         for (int bucketIndex = 0; bucketIndex < buckets.Count; bucketIndex++)
         {
             ProcessBucket(gs, buckets[bucketIndex]);
         }

         //В конце лагкомпенсации мы восстанавливаем физический мир 
         //в исходное состояние
         _timeMachine.TravelToTime(gs.Time);
     }

     private void ProcessBucket(GameState presentState, TimeTravelMap.Bucket bucket)
     {
         //Откатываем время один раз для каждой корзины
         var pastState = _timeMachine.TravelToTime(bucket.Time);

         foreach (var system in _systems)
         {
               system.PastState = pastState;
               system.PresentState = presentState;

               foreach (var entity in bucket)
               {
                   system.Execute(entity);
               }
          }
     }
}

Alles wat blouf war d'Detailer ze konfiguréieren:

1. Verstinn wéi vill d'maximal Distanz vun der Bewegung an der Zäit ze limitéieren.

Et war wichteg fir eis d'Spill esou zougänglech wéi méiglech a Bedéngungen vun aarme mobilen Netzwierker ze maachen, sou datt mir d'Geschicht mat enger Marge vun 30 Ticks limitéiert hunn (mat engem Tickrate vun 20 Hz). Dëst erlaabt d'Spiller Géigner och bei ganz héije Pings ze schloen.

2. Bestëmmt wéi eng Objete kënnen an der Zäit geréckelt ginn a wéi eng net.

Mir beweegen natierlech eise Géigner. Awer installéierbar Energieschëlder, zum Beispill, sinn net. Mir hunn décidéiert datt et besser war fir d'Verteidegungsfäegkeet Prioritéit ze ginn, sou wéi et dacks an Online Shooters gemaach gëtt. Wann de Spiller schonn e Schëld an der heiteger plazéiert huet, sollten d'Lag-kompenséiert Kugelen aus der Vergaangenheet net duerch fléien.

3. Entscheeden ob et néideg ass fir d'Fähigkeiten vun den Dinosaurier ze kompenséieren: Biss, Schwanzstéck, etc. Mir hunn decidéiert wat néideg war a veraarbecht se no de selwechte Reegelen wéi Kugelen.

4. Bestëmmen wat mat der colliders vun de Spiller ze maachen fir déi lags Kompensatioun gesuergt. Op eng gutt Manéier soll hir Positioun net an d'Vergaangenheet réckelen: de Spiller soll sech an der selwechter Zäit gesinn an där hien elo um Server ass. Mir rullen awer och d'Kollider vum Schéissspiller zréck, an et gi verschidde Grënn dofir.

Éischtens, verbessert et Clustering: mir kënnen déi selwecht kierperlech Staat fir all Spiller mat enk Pings benotzen.

Zweetens, an all Raycasts an Iwwerlappungen ausschléisse mir ëmmer d'Colliders vum Spiller aus, deen d'Fäegkeeten oder d'Projektilen besëtzt. Am Dino Squad kontrolléieren d'Spiller Dinosaurier, déi éischter net-Standard Geometrie no Shooterstandards hunn. Och wann de Spiller an engem ongewéinleche Wénkel schéisst an d'Streck vun der Kugel duerch den Dinosaurierkollider vum Spiller passéiert, wäert d'Kugel et ignoréieren.

Drëttens berechnen mir d'Positioune vun der Waff vum Dinosaurier oder de Punkt vun der Uwendung vun der Fäegkeet mat Daten aus der ECS och virum Ufank vun der Lag Kompensatioun.

Als Resultat ass d'real Positioun vun de Kollidere vum lag-kompenséierte Spiller onwichteg fir eis, also hu mir e méi produktiven a gläichzäiteg méi einfache Wee gemaach.

Netzlatenz kann net einfach ewechgeholl ginn, et kann nëmme maskéiert ginn. Wéi all aner Method vun Verkleedung, Serverlag Kompensatioun huet seng tradeoffs. Et verbessert d'Spillerfahrung vum Spiller deen op Käschte vum Spiller erschoss gëtt. Fir den Dino Squad war de Choix hei awer evident.

Natierlech huet dat alles och misse bezuelt ginn duerch déi verstäerkte Komplexitéit vum Servercode als Ganzt - souwuel fir Programméierer wéi och Spilldesigner. Wa fréier d'Simulatioun en einfache sequentiellen Uruff vu Systemer war, da mat Lag Kompensatioun, erschéngen nestéiert Schleifen a Filialen dran. Mir hunn och vill Effort verbruecht fir et bequem ze maachen mat ze schaffen.

An der 2019 Versioun (a vläicht e bësse méi fréi) huet Unity voll Ënnerstëtzung fir onofhängeg kierperlech Szenen bäigefüügt. Mir hunn se bal direkt nom Update um Server implementéiert, well mir wollte séier vun der kierperlecher Welt, déi an all Zëmmer gemeinsam ass, lass ginn.

Mir hunn all Spillraum seng eege physesch Szen ginn an domat eliminéiert de Besoin fir d'Szen aus den Donnéeën vum Nopeschraum ze "läschen", ier Dir d'Simulatioun berechnen. Als éischt huet et eng bedeitend Erhéijung vun der Produktivitéit ginn. Zweetens, et huet et méiglech gemaach vun enger ganzer Klass vu Bugs lass ze ginn, déi entstane sinn wann de Programméierer e Feeler am Szenebotzcode gemaach huet wann nei Spillelementer bäigefüügt ginn. Esou Feeler ware schwéier ze Debuggen, a si hunn dacks dozou gefouert datt den Zoustand vu kierperlechen Objeten an der Szen vun engem Raum an en anere Raum "fléisst".

Zousätzlech hu mir e puer Fuerschunge gemaach ob kierperlech Szenen benotzt kënne fir d'Geschicht vun der kierperlecher Welt ze späicheren. Dat ass, bedingt, all Zëmmer net eng Szen ze verdeelen, mee 30 Szenen, a maacht e zyklesche Puffer aus hinnen, an deem d'Geschicht gespäichert gëtt. Am Allgemengen huet d'Optioun erausgestallt datt se funktionnéiert, awer mir hunn et net ëmgesat: et huet keng verréckt Erhéijung vun der Produktivitéit gewisen, awer erfuerderlech zimlech riskant Ännerungen. Et war schwéier virauszesoen wéi de Server sech géif behuelen wann Dir eng laang Zäit mat sou vill Szenen schafft. Dofir hu mir d'Regel gefollegt: "Wann se net brëcht, fix se net".

Source: will.com

Setzt e Commentaire