Jinsi tulivyoboresha mbinu za hesabu za balestiki kwa mpiga risasiji wa simu kwa kutumia kanuni ya fidia ya muda wa mtandao

Jinsi tulivyoboresha mbinu za hesabu za balestiki kwa mpiga risasiji wa simu kwa kutumia kanuni ya fidia ya muda wa mtandao

Habari, mimi ni Nikita Brizhak, msanidi seva kutoka Pixonic. Leo ningependa kuzungumza juu ya fidia kwa kuchelewa kwa wachezaji wengi wa rununu.

Nakala nyingi zimeandikwa juu ya fidia ya lag ya seva, pamoja na kwa Kirusi. Hii haishangazi, kwani teknolojia hii imetumika kikamilifu katika uundaji wa FPS ya wachezaji wengi tangu miaka ya 90. Kwa mfano, unaweza kukumbuka mod ya QuakeWorld, ambayo ilikuwa moja ya kwanza kuitumia.

Pia tunaitumia kwenye Kikosi chetu cha Dino cha wachezaji wengi wa rununu.

Katika makala hii, lengo langu si kurudia yale ambayo tayari yameandikwa mara elfu, lakini kueleza jinsi tulivyotekeleza fidia ya lag katika mchezo wetu, kwa kuzingatia stack yetu ya teknolojia na vipengele vya msingi vya uchezaji.

Maneno machache kuhusu gamba na teknolojia yetu.

Dino Squad ni mpiga risasiji wa mtandao wa PvP wa rununu. Wachezaji hudhibiti dinosaur zilizo na aina ya silaha na kupigana katika timu 6v6.

Mteja na seva zote zinatokana na Umoja. Usanifu ni wa kawaida kabisa kwa wapiga risasi: seva ni ya kimabavu, na utabiri wa mteja hufanya kazi kwa wateja. Uigaji wa mchezo umeandikwa kwa kutumia ECS ya ndani na hutumiwa kwenye seva na mteja.

Ikiwa hii ni mara yako ya kwanza kusikia kuhusu fidia iliyochelewa, hapa kuna safari fupi kuhusu suala hilo.

Katika michezo ya FPS ya wachezaji wengi, mechi hiyo kawaida huigwa kwenye seva ya mbali. Wachezaji hutuma ingizo lao (maelezo kuhusu funguo zilizobonyezwa) kwa seva, na kwa kujibu seva huwatumia hali iliyosasishwa ya mchezo ikizingatia data iliyopokelewa. Kwa utaratibu huu wa mwingiliano, ucheleweshaji kati ya kubonyeza kitufe cha mbele na wakati herufi ya kichezaji inaposogea kwenye skrini itakuwa kubwa kila wakati kuliko ping.

Wakati kwenye mitandao ya ndani ucheleweshaji huu (unaojulikana sana kama kuchelewa kwa pembejeo) huenda usionekane, unapocheza kupitia Mtandao huleta hisia ya "kuteleza kwenye barafu" wakati wa kudhibiti mhusika. Tatizo hili linafaa mara mbili kwa mitandao ya simu, ambapo kesi wakati ping ya mchezaji ni 200 ms bado inachukuliwa kuwa muunganisho bora. Mara nyingi ping inaweza kuwa 350, 500, au 1000 ms. Halafu inakuwa haiwezekani kucheza mpiga risasi haraka na bakia ya pembejeo.

Suluhisho la tatizo hili ni utabiri wa uigaji wa upande wa mteja. Hapa mteja mwenyewe anatumia pembejeo kwa tabia ya mchezaji, bila kusubiri jibu kutoka kwa seva. Na jibu linapopokelewa, linalinganisha tu matokeo na kusasisha nafasi za wapinzani. Ucheleweshaji kati ya kubonyeza kitufe na kuonyesha matokeo kwenye skrini katika kesi hii ni ndogo.

Ni muhimu kuelewa nuance hapa: mteja daima huchota kulingana na pembejeo yake ya mwisho, na maadui - kwa kuchelewa kwa mtandao, kulingana na hali ya awali kutoka kwa data kutoka kwa seva. Hiyo ni, wakati wa kumpiga adui, mchezaji humuona zamani jamaa na yeye mwenyewe. Zaidi kuhusu utabiri wa mteja tuliandika hapo awali.

Kwa hivyo, utabiri wa mteja hutatua tatizo moja, lakini hutengeneza jingine: ikiwa mchezaji anapiga risasi mahali ambapo adui alikuwa hapo awali, kwenye seva wakati wa kupiga risasi katika hatua sawa, adui anaweza kuwa hayupo tena mahali hapo. Majaribio ya fidia ya kuchelewa kwa seva kutatua tatizo hili. Silaha inapofyatuliwa, seva hurejesha hali ya mchezo ambayo mchezaji aliona mahali ulipo wakati wa kufyatua risasi, na kuangalia ikiwa kweli angeweza kumpiga adui. Ikiwa jibu ni "ndiyo," hit inahesabiwa, hata kama adui hayuko kwenye seva wakati huo.

Tukiwa na ujuzi huu, tulianza kutekeleza fidia ya kuchelewa kwa seva katika Dino Squad. Kwanza kabisa, tulipaswa kuelewa jinsi ya kurejesha kwenye seva kile mteja aliona? Na ni nini hasa kinachohitaji kurejeshwa? Katika mchezo wetu, mipigo kutoka kwa silaha na uwezo huhesabiwa kupitia miale na viwekeleo - yaani, kupitia mwingiliano na migongano halisi ya adui. Ipasavyo, tulihitaji kutoa tena nafasi ya migongano hii, ambayo mchezaji "aliona" ndani ya nchi, kwenye seva. Wakati huo tulikuwa tukitumia toleo la Unity 2018.x. API ya fizikia hapo ni tuli, ulimwengu wa kimwili upo katika nakala moja. Hakuna njia ya kuokoa hali yake na kisha kurejesha kutoka kwa sanduku. Basi nini cha kufanya?

Suluhisho lilikuwa juu ya uso; vitu vyake vyote tayari vilikuwa vimetumiwa na sisi kutatua shida zingine:

  1. Kwa kila mteja, tunahitaji kujua ni saa ngapi aliona wapinzani alipobonyeza funguo. Tayari tumeandika maelezo haya kwenye kifurushi cha kuingiza data na tukaitumia kurekebisha utabiri wa mteja.
  2. Tunahitaji kuwa na uwezo wa kuhifadhi historia ya majimbo ya mchezo. Ni ndani yake kwamba tutashikilia misimamo ya wapinzani wetu (na kwa hivyo wagongana wao). Tayari tulikuwa na historia ya hali kwenye seva, tuliitumia kujenga delta. Kujua wakati unaofaa, tunaweza kupata hali sahihi katika historia kwa urahisi.
  3. Kwa kuwa sasa tuna hali ya mchezo kutoka kwa historia mkononi, tunahitaji kuwa na uwezo wa kusawazisha data ya mchezaji na hali ya ulimwengu halisi. Migongano iliyopo - hoja, kukosa - kuunda, zisizo za lazima - kuharibu. Mantiki hii pia ilikuwa tayari imeandikwa na ilijumuisha mifumo kadhaa ya ECS. Tuliitumia kuhifadhi vyumba kadhaa vya michezo katika mchakato mmoja wa Umoja. Na kwa kuwa ulimwengu wa kimwili ni mmoja kwa kila mchakato, ilibidi utumike tena kati ya vyumba. Kabla ya kila tiki ya uigaji, "tunaweka upya" hali ya ulimwengu halisi na kuuanzisha upya kwa data ya chumba cha sasa, tukijaribu kutumia tena vitu vya mchezo wa Unity kadri tuwezavyo kupitia mfumo mahiri wa kuunganisha. Kilichobakia ni kuomba mantiki sawa kwa hali ya mchezo kutoka zamani.

Kwa kuweka vipengele hivi vyote pamoja, tulipata "mashine ya saa" ambayo inaweza kurudisha hali ya ulimwengu kwa wakati ufaao. Nambari hiyo iligeuka kuwa rahisi:

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;
     }
}

Kilichobaki ni kujua jinsi ya kutumia mashine hii kufidia risasi na uwezo kwa urahisi.

Katika hali rahisi, wakati mechanics inategemea hitscan moja, kila kitu kinaonekana kuwa wazi: kabla ya mchezaji kupiga risasi, anahitaji kurudisha ulimwengu wa mwili kwa hali inayotaka, kufanya raycast, kuhesabu hit au kukosa, na. kurudisha ulimwengu katika hali ya awali.

Lakini kuna makanika machache kama haya katika Kikosi cha Dino! Silaha nyingi kwenye mchezo huunda makombora - risasi za muda mrefu ambazo huruka kwa kupe kadhaa za kuiga (katika hali zingine, kupe kadhaa). Nini cha kufanya nao, wanapaswa kuruka saa ngapi?

Π’ makala ya kale kuhusu safu ya mtandao ya Half-Life, wavulana kutoka Valve waliuliza swali lile lile, na jibu lao lilikuwa hili: fidia ya lag ya projectile ni shida, na ni bora kuizuia.

Hatukuwa na chaguo hili: silaha zinazotegemea projectile zilikuwa kipengele kikuu cha muundo wa mchezo. Kwa hivyo tulilazimika kuja na kitu. Baada ya kujadiliana kidogo, tuliandaa chaguzi mbili ambazo zilionekana kufanya kazi:

1. Tunafunga projectile kwa wakati wa mchezaji aliyeiumba. Kila tiki ya uigaji wa seva, kwa kila kitone cha kila mchezaji, tunarudisha ulimwengu halisi kwenye hali ya mteja na kufanya hesabu zinazohitajika. Mbinu hii ilifanya iwezekane kuwa na mzigo uliosambazwa kwenye seva na muda wa ndege unaotabirika wa projectiles. Utabiri ulikuwa muhimu sana kwetu, kwani tunayo projectiles zote, pamoja na projectiles za adui, zilizotabiriwa kwa mteja.

Jinsi tulivyoboresha mbinu za hesabu za balestiki kwa mpiga risasiji wa simu kwa kutumia kanuni ya fidia ya muda wa mtandao
Katika picha, mchezaji aliye kwenye tick 30 anarusha kombora kwa kutarajia: anaona ni upande gani adui anakimbia na anajua kasi ya takriban ya kombora. Ndani ya nchi anaona kwamba alipiga shabaha kwenye tiki ya 33. Shukrani kwa fidia ya lag, itaonekana pia kwenye seva

2. Tunafanya kila kitu sawa na katika chaguo la kwanza, lakini, baada ya kuhesabu tiki moja ya simulation ya risasi, hatuachi, lakini tunaendelea kuiga ndege yake ndani ya tiki sawa ya seva, kila wakati kuleta wakati wake karibu na seva. tiki moja baada ya nyingine na kusasisha nafasi za kugongana. Tunafanya hivi hadi moja ya mambo mawili yatatokea:

  • Muda wa risasi umekwisha. Hii ina maana kwamba mahesabu ni juu, tunaweza kuhesabu miss au hit. Na hii ni kwa tiki sawa ambayo risasi ilipigwa! Kwetu hii ilikuwa ni pamoja na kupunguza. Kuongeza - kwa sababu kwa mchezaji wa risasi hii ilipunguza sana kuchelewesha kati ya hit na kupungua kwa afya ya adui. Upande wa chini ni kwamba athari sawa ilionekana wakati wapinzani walipiga risasi kwa mchezaji: adui, inaonekana, alipiga roketi ya polepole tu, na uharibifu ulikuwa tayari umehesabiwa.
  • Kitone kimefikia wakati wa seva. Katika kesi hii, uigaji wake utaendelea katika tiki inayofuata ya seva bila fidia yoyote ya lag. Kwa makadirio ya polepole, hii inaweza kupunguza kinadharia idadi ya kurudi nyuma kwa fizikia ikilinganishwa na chaguo la kwanza. Wakati huo huo, mzigo usio na usawa kwenye uigaji uliongezeka: seva haikuwa na kitu, au katika tiki moja ya seva ilikuwa ikikokotoa tiki kadhaa za uigaji kwa risasi kadhaa.

Jinsi tulivyoboresha mbinu za hesabu za balestiki kwa mpiga risasiji wa simu kwa kutumia kanuni ya fidia ya muda wa mtandao
Hali sawa na katika picha ya awali, lakini imehesabiwa kulingana na mpango wa pili. Kombora "lilishikana" na wakati wa seva kwenye tiki sawa na ambayo risasi ilitokea, na hit inaweza kuhesabiwa mapema kama tiki inayofuata. Katika alama ya 31, katika kesi hii, fidia ya lag haitumiki tena

Katika utekelezaji wetu, mbinu hizi mbili zilitofautiana katika mistari michache tu ya kanuni, kwa hivyo tuliunda zote mbili, na kwa muda mrefu zilikuwepo sambamba. Kulingana na mitambo ya silaha na kasi ya risasi, tulichagua chaguo moja au nyingine kwa kila dinosaur. Jambo la kubadilisha hapa lilikuwa mwonekano katika mchezo wa mekanika kama vile "ikiwa utampiga adui mara nyingi sana kwa wakati fulani, pata bonasi." Fundi yeyote ambapo wakati ambapo mchezaji alimpiga adui alichukua jukumu muhimu alikataa kufanya kazi na mbinu ya pili. Kwa hivyo tulimaliza kwenda na chaguo la kwanza, na sasa linatumika kwa silaha zote na uwezo wote wa kufanya kazi kwenye mchezo.

Kwa kando, inafaa kuinua suala la utendaji. Ikiwa ulidhani kuwa haya yote yangepunguza mambo, ninajibu: ni. Umoja ni polepole sana katika kusogeza vigonga na kuziwasha na kuzima. Katika Kikosi cha Dino, katika hali "mbaya zaidi", kunaweza kuwa na projectiles mia kadhaa zilizopo wakati huo huo katika mapigano. Kusogeza vigonga ili kuhesabu kila projectile kivyake ni anasa isiyoweza kumudu. Kwa hiyo, ilikuwa ni lazima kabisa kwetu kupunguza idadi ya "rellbacks" za fizikia. Ili kufanya hivyo, tuliunda sehemu tofauti katika ECS ambayo tunarekodi wakati wa mchezaji. Tuliiongeza kwa vyombo vyote vinavyohitaji fidia ya lag (projectiles, uwezo, nk). Kabla hatujaanza kuchakata huluki kama hizo, tunazikusanya kwa wakati huu na kuzichakata pamoja, tukirudisha ulimwengu halisi mara moja kwa kila kundi.

Katika hatua hii tuna mfumo wa kufanya kazi kwa ujumla. Nambari yake katika fomu iliyorahisishwa:

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);
               }
          }
     }
}

Iliyobaki ni kusanidi maelezo:

1. Kuelewa ni kiasi gani cha kupunguza umbali wa juu wa harakati kwa wakati.

Ilikuwa muhimu kwetu kufanya mchezo kufikiwa iwezekanavyo katika hali ya mitandao duni ya simu, kwa hivyo tulidhibiti hadithi kwa ukingo wa kupe 30 (kwa kiwango cha tiki cha 20 Hz). Hii inaruhusu wachezaji kugonga wapinzani hata kwenye pings za juu sana.

2. Tambua ni vitu gani vinaweza kusogezwa kwa wakati na ambavyo haviwezi.

Sisi, bila shaka, tunawahamisha wapinzani wetu. Lakini ngao za nishati zinazoweza kusakinishwa, kwa mfano, sio. Tuliamua kuwa ni bora kutoa kipaumbele kwa uwezo wa ulinzi, kama inavyofanywa mara nyingi katika wapiga risasi mtandaoni. Ikiwa mchezaji tayari ameweka ngao kwa sasa, risasi za fidia ya lag kutoka zamani hazipaswi kuruka kupitia hiyo.

3. Amua ikiwa ni muhimu kulipa fidia kwa uwezo wa dinosaurs: bite, mgomo wa mkia, nk. Tuliamua kile kilichohitajika na kuzishughulikia kulingana na sheria sawa na risasi.

4. Amua nini cha kufanya na migongano ya mchezaji ambaye fidia ya lag inafanywa. Kwa njia nzuri, msimamo wao haupaswi kubadilika kuwa siku za nyuma: mchezaji anapaswa kujiona wakati huo huo ambao yuko kwenye seva. Walakini, sisi pia tunarudisha nyuma migongano ya mchezaji wa risasi, na kuna sababu kadhaa za hii.

Kwanza, inaboresha mkusanyiko: tunaweza kutumia hali sawa ya kimwili kwa wachezaji wote walio na pings za karibu.

Pili, katika miale na mwingiliano wote huwa hatujumuishi vigongana vya mchezaji ambaye anamiliki uwezo au makombora. Katika Kikosi cha Dino, wachezaji hudhibiti dinosaurs, ambazo zina jiometri isiyo ya kawaida kulingana na viwango vya ufyatuaji. Hata kama mchezaji atapiga risasi kwa pembe isiyo ya kawaida na njia ya risasi ikapita kwenye mpigo wa dinosaur wa mchezaji, risasi itaipuuza.

Tatu, tunahesabu nafasi za silaha ya dinosaur au hatua ya matumizi ya uwezo wa kutumia data kutoka kwa ECS hata kabla ya kuanza kwa fidia ya lag.

Matokeo yake, nafasi halisi ya wagongaji wa mchezaji wa fidia ya lag sio muhimu kwetu, kwa hiyo tulichukua njia yenye tija zaidi na wakati huo huo rahisi.

Ucheleweshaji wa mtandao hauwezi tu kuondolewa, unaweza kufunikwa tu. Kama njia nyingine yoyote ya kujificha, fidia ya kuchelewa kwa seva ina biashara zake. Inaboresha hali ya uchezaji ya mchezaji ambaye anapiga risasi kwa gharama ya mchezaji anayepigwa risasi. Kwa Kikosi cha Dino, hata hivyo, chaguo hapa lilikuwa dhahiri.

Kwa kweli, haya yote pia yalipaswa kulipwa na ugumu ulioongezeka wa nambari ya seva kwa ujumla - kwa watengeneza programu na wabuni wa mchezo. Ikiwa mapema simulation ilikuwa simu rahisi ya mlolongo wa mifumo, basi kwa fidia ya lag, matanzi yaliyowekwa na matawi yalionekana ndani yake. Pia tulitumia juhudi nyingi kuifanya iwe rahisi kufanya kazi nayo.

Katika toleo la 2019 (na labda mapema kidogo), Unity iliongeza usaidizi kamili kwa matukio huru ya kimwili. Tulizitekeleza kwenye seva mara moja baada ya sasisho, kwa sababu tulitaka kuondokana na ulimwengu wa kimwili wa kawaida kwa vyumba vyote.

Tulipa kila chumba cha mchezo eneo lake halisi na hivyo kuondoa hitaji la "kufuta" tukio kutoka kwa data ya chumba cha jirani kabla ya kukokotoa uigaji. Kwanza, ilitoa ongezeko kubwa la tija. Pili, ilifanya iwezekane kuondoa kundi zima la mende zilizotokea ikiwa programu ilifanya makosa katika msimbo wa kusafisha eneo wakati wa kuongeza vipengele vipya vya mchezo. Hitilafu kama hizo zilikuwa ngumu kutatua, na mara nyingi zilisababisha hali ya vitu vya kawaida katika eneo la chumba kimoja "kumiminika" kwenye chumba kingine.

Kwa kuongeza, tulifanya utafiti ili kujua ikiwa matukio ya kimwili yanaweza kutumika kuhifadhi historia ya ulimwengu wa kimwili. Hiyo ni, kwa masharti, usitenge onyesho moja kwa kila chumba, lakini matukio 30, na utengeneze bafa ya mzunguko kutoka kwao, ili kuhifadhi hadithi. Kwa ujumla, chaguo liligeuka kuwa la kufanya kazi, lakini hatukuitekeleza: haikuonyesha ongezeko lolote la ujinga katika tija, lakini ilihitaji mabadiliko ya hatari. Ilikuwa ngumu kutabiri jinsi seva ingefanya kazi wakati wa kufanya kazi kwa muda mrefu na matukio mengi. Kwa hivyo, tulifuata sheria: ".Ikiwa haijavunjika, usiirekebishe'.

Chanzo: mapenzi.com

Kuongeza maoni