Kumaha urang ningkatkeun mékanika itungan balistik pikeun penembak sélulér kalayan algoritma kompensasi latency jaringan

Kumaha urang ningkatkeun mékanika itungan balistik pikeun penembak sélulér kalayan algoritma kompensasi latency jaringan

Hai, kuring Nikita Brizhak, pamekar server ti Pixonic. Dinten abdi hoyong ngobrol ngeunaan ngimbangan lag dina multiplayer mobile.

Seueur tulisan anu ditulis ngeunaan kompensasi lag server, kalebet dina basa Rusia. Ieu teu heran, saprak téhnologi ieu geus aktip dipake dina kreasi multiplayer FPS saprak ahir 90s. Salaku conto, anjeun tiasa émut kana mod QuakeWorld, anu mangrupikeun salah sahiji anu munggaran ngagunakeunana.

Kami ogé ngagunakeun éta dina penembak multipél sélulér Dino Squad.

Dina tulisan ieu, tujuan kuring henteu ngulang naon anu parantos ditulis sarébu kali, tapi pikeun nyarioskeun kumaha urang ngalaksanakeun kompensasi lag dina kaulinan urang, kalayan nganggap tumpukan téknologi sareng fitur midangkeun inti.

Sababaraha kecap ngeunaan cortex sareng téknologi urang.

Dino Squad mangrupikeun penembak PvP sélulér jaringan. Pamaén ngontrol dinosaurus anu dilengkepan ku rupa-rupa senjata sareng silih tarung dina tim 6v6.

Duanana klien sareng server dumasar kana Unity. Arsitékturna cukup klasik pikeun némbak: serverna otoriter, sareng prediksi klien tiasa dianggo pikeun klien. Simulasi kaulinan ditulis nganggo ECS di bumi sareng dianggo dina server sareng klien.

Upami ieu pertama kalina anjeun nguping ngeunaan kompensasi lag, ieu mangrupikeun wisata ringkes kana masalah éta.

Dina kaulinan FPS multiplayer, pertandingan biasana simulasi dina server jauh. Pamaén ngirimkeun input maranéhna (inpormasi ngeunaan kenop dipencet) ka server, sarta dina respon server ngirim aranjeunna hiji kaayaan kaulinan diropéa nyokot kana akun data narima. Kalawan skéma interaksi ieu, reureuh antara mencét kenop maju jeung momen karakter pamuter ngalir dina layar bakal salawasna leuwih gede ti ping.

Bari dina jaringan lokal reureuh ieu (populér disebut input lag) bisa jadi unnoticeable, nalika maén via Internet nyiptakeun rasa "ngageser dina és" nalika ngadalikeun karakter. Masalah ieu dua kali relevan pikeun jaringan sélulér, dimana kasus nalika ping pamuter 200 mdet masih dianggap sambungan anu saé. Seringna ping tiasa 350, 500, atanapi 1000 mdet. Lajeng janten ampir teu mungkin keur maénkeun hiji jujur ​​gancang kalayan input lag.

Solusi pikeun masalah ieu nyaéta prediksi simulasi sisi klien. Di dieu klien sorangan nerapkeun input ka karakter pamuter, tanpa ngantosan respon ti server. Sareng nalika jawabanna ditampi, éta ngan ukur ngabandingkeun hasil sareng ngapdet posisi lawan. Reureuh antara mencét kenop jeung mintonkeun hasil dina layar dina hal ieu minimal.

Kadé ngartos nuansa dieu: klien nu salawasna draws sorangan nurutkeun input panungtungan na, sarta musuh - kalawan reureuh jaringan, nurutkeun kaayaan saméméhna ti data ti server. Nyaéta, nalika shooting di musuh, pamaén nilik anjeunna kaliwat relatif ka dirina. Langkung seueur ngeunaan prediksi klien urang nulis tadi.

Ku kituna, prediksi klien solves hiji masalah, tapi nyiptakeun sejen: lamun pamaén némbak di titik dimana musuh geus kaliwat, dina server nalika shooting di titik anu sarua, musuh bisa euweuh di tempat éta. Server lag santunan usaha pikeun ngajawab masalah ieu. Nalika pakarang dipecat, server restores kaayaan kaulinan anu pamaén nempo lokal dina waktu makéna, jeung pariksa naha anjeunna bener bisa geus pencét musuh. Lamun jawaban téh "enya," hit diitung, sanajan musuh geus euweuh dina server dina titik éta.

Angkatan sareng pangaweruh ieu, urang mimiti nerapkeun santunan lag server di Dino Squad. Anu mimiti, urang kedah ngartos kumaha carana mulangkeun server naon nempo klien? Jeung naon kahayang kudu dibalikeun? Dina kaulinan urang, hits tina pakarang jeung kamampuhan diitung ngaliwatan raycasts na overlays - nyaeta, ngaliwatan interaksi jeung colliders fisik musuh. Sasuai, urang diperlukeun pikeun baranahan posisi colliders ieu, nu pamaén "ningali" lokal, dina server. Waktu éta kami ngagunakeun Unity versi 2018.x. API fisika aya statik, dunya fisik aya dina salinan tunggal. Teu aya deui jalan pikeun ngahemat kaayaanana teras malikkeun tina kotakna. Janten naon anu kedah dilakukeun?

Solusina aya dina permukaan; sadaya unsurna parantos dianggo ku urang pikeun ngarengsekeun masalah anu sanés:

  1. Pikeun unggal klien, urang kudu nyaho iraha waktuna manéhna nempo lawan nalika manéhna mencét kenop. Kami parantos nyerat inpormasi ieu kana pakét input sareng dianggo pikeun nyaluyukeun prediksi klien.
  2. Urang kudu bisa nyimpen sajarah nagara kaulinan. Di dinya urang bakal nyekel posisi lawan urang (jeung ku kituna colliders maranéhanana). Kami parantos ngagaduhan sajarah kaayaan dina server, kami dianggo pikeun ngawangun délta. Nyaho waktos anu pas, urang tiasa mendakan kaayaan anu leres dina sajarah.
  3. Ayeuna urang boga kaayaan kaulinan tina sajarah di leungeun, urang kudu bisa nyingkronkeun data pamuter jeung kaayaan dunya fisik. Colliders anu aya - pindah, anu leungit - nyiptakeun, anu teu perlu - ngancurkeun. Logika ieu ogé parantos ditulis sareng diwangun ku sababaraha sistem ECS. Urang dipaké pikeun nahan sababaraha kamar kaulinan dina hiji prosés Unity. Sareng saprak dunya fisik mangrupikeun hiji per prosés, éta kedah dianggo deui antara kamar. Sateuacan unggal keletik tina simulasi, urang "ngareset" kaayaan dunya fisik sarta reinitialized eta kalawan data pikeun kamar ayeuna, nyobian ngagunakeun deui objék kaulinan Unity saloba mungkin ngaliwatan sistem pooling palinter. Sadaya anu tetep nyaéta pikeun ngungkabkeun logika anu sami pikeun kaayaan kaulinan ti jaman baheula.

Ku ngahijikeun sadaya elemen ieu, urang ngagaduhan "mesin waktos" anu tiasa ngagulung deui kaayaan dunya fisik ka momen anu pas. Kodeu tétéla saderhana:

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

Sadaya anu tetep nyaéta pikeun terang kumaha ngagunakeun mesin ieu pikeun gampang ngimbangan nembak sareng kamampuan.

Dina kasus pangbasajanna, nalika mékanika didasarkeun kana hiji hitscan, sadayana sigana jelas: sateuacan pamuter némbak, anjeunna kedah ngagulung deui dunya fisik ka kaayaan anu dipikahoyong, ngalakukeun raycast, ngitung hit atanapi sono, sareng mulangkeun dunya kana kaayaan awal.

Tapi aya saeutik pisan mékanika sapertos di Dino Squad! Kalolobaan pakarang dina kaulinan nyieun projectiles - pélor lila-cicing nu ngapung pikeun sababaraha ticks simulasi (dina sababaraha kasus, puluhan ticks). Naon anu kudu dipigawé kalayan aranjeunna, jam sabaraha maranéhna kudu ngapung?

В artikel kuna ngeunaan tumpukan jaringan Satengah-Kahirupan, guys ti klep nanya ka sual sarua, sarta jawaban maranéhanana éta: santunan projectile lag masalah, sarta eta leuwih hade ulah eta.

Urang teu boga pilihan ieu: pakarang dumasar projectile éta fitur konci desain kaulinan. Ku kituna urang kungsi datang nepi ka hiji hal. Saatos sababaraha brainstorming, kami ngarumuskeun dua pilihan anu sigana tiasa dianggo:

1. Urang dasi projectile ka waktu pamaén anu nyiptakeun eta. Unggal keletik tina simulasi server, pikeun unggal bullet unggal pamuter, urang gulung deui dunya fisik ka kaayaan klien tur ngalakukeun itungan perlu. Pendekatan ieu ngamungkinkeun pikeun gaduh beban anu disebarkeun dina server sareng waktos hiber projectiles anu tiasa diprediksi. Predictability éta hususna penting pikeun urang, saprak urang gaduh sadayana projectiles, kaasup projectiles musuh, diprediksi on klien nu.

Kumaha urang ningkatkeun mékanika itungan balistik pikeun penembak sélulér kalayan algoritma kompensasi latency jaringan
Dina gambar, pamuter dina keletik 30 seuneu misil dina antisipasi: anjeunna ningali arah mana musuh ngajalankeun sarta weruh laju perkiraan tina misil. Lokal anjeunna ningali yén anjeunna pencét targét dina keletik ka-33. Hatur nuhun kana santunan lag, éta ogé bakal muncul dina server

2. Urang ngalakukeun sagalana sarua jeung dina pilihan kahiji, tapi, sanggeus diitung hiji keletik tina simulasi bullet, urang teu eureun, tapi terus simulate hiber na dina keletik server sarua, unggal waktu bringing waktu na ngadeukeutan ka server. hiji-hiji centang jeung ngamutahirkeun posisi collider. Urang ngalakukeun ieu dugi salah sahiji dua hal kajadian:

  • Pelor geus béak. Ieu ngandung harti yén itungan anu leuwih, urang bisa cacah hiji miss atawa hit. Sareng ieu dina keletik anu sami dimana shot dipecat! Pikeun urang ieu duanana tambah jeung minus. A tambah - sabab pikeun pamuter shooting ieu nyata ngurangan reureuh antara hit jeung panurunan dina kaséhatan musuh. downside teh nya eta éfék anu sarua ieu dititénan nalika lawan dipecat dina pamuter nu: musuh, eta bakal sigana, ngan dipecat rokét slow, sarta karuksakan ieu geus diitung.
  • Bullet geus ngahontal waktu server. Dina hal ieu, simulasi na bakal dituluykeun dina keletik server salajengna tanpa santunan lag. Pikeun projectiles slow, ieu téoritis bisa ngurangan jumlah rollbacks fisika dibandingkeun pilihan kahiji. Dina waktu nu sarua, beban henteu rata dina simulasi ngaronjat: server éta boh dianggurkeun, atawa dina hiji keletik server ieu ngitung belasan simulasi ticks pikeun sababaraha pélor.

Kumaha urang ningkatkeun mékanika itungan balistik pikeun penembak sélulér kalayan algoritma kompensasi latency jaringan
Skenario sarua sakumaha dina gambar saméméhna, tapi diitung nurutkeun skéma kadua. The misil "bray up" kalawan waktu server di keletik sarua yén shot lumangsung, sarta hit bisa diitung salaku awal salaku keletik salajengna. Dina keletik ka-31, dina hal ieu, kompensasi lag henteu diterapkeun deui

Dina palaksanaan kami, dua pendekatan ieu béda dina ngan sababaraha garis kode, jadi kami dijieun duanana, sarta pikeun lila aranjeunna eksis dina paralel. Gumantung kana mékanika pakarang jeung laju bullet, urang milih hiji atawa pilihan séjén pikeun tiap dinosaurus. Titik balik di dieu nyaéta penampilan dina kaulinan mékanika sapertos "upami anjeun pencét musuh sababaraha kali dina waktos sapertos kitu, kéngingkeun bonus sapertos kitu." Sakur montir dimana waktos pamuter pencét musuh maénkeun peran anu penting nampik damel sareng pendekatan kadua. Ku kituna urang réngsé nepi bade kalawan pilihan kahiji, sarta ayeuna manglaku ka sadaya pakarang jeung sagala abilities aktip dina kaulinan.

Kapisah, éta patut raising masalah kinerja. Upami anjeun ngira yén sadaya ieu bakal ngalambatkeun, kuring ngajawab: éta. Unity cukup laun dina mindahkeun colliders sareng mareuman sareng mareuman. Dina Dino Squad, dina kasus "paling awon", tiasa aya sababaraha ratus projectiles anu aya sakaligus dina tempur. Pindah colliders pikeun ngitung unggal projectile individual mangrupa méwah unaffordable. Ku alatan éta, éta kacida diperlukeun pikeun urang ngaleutikan jumlah fisika "rollbacks". Jang ngalampahkeun ieu, urang dijieun komponén misah di ECS nu urang ngarekam waktu pamuter urang. Urang ditambahkeun kana sagala éntitas anu merlukeun santunan lag (projectiles, abilities, jsb). Sateuacan urang ngamimitian ngolah éntitas sapertos kitu, urang ngumpulkeun aranjeunna dina waktos ayeuna sareng ngolah aranjeunna babarengan, ngagulung deui dunya fisik sakali pikeun unggal klaster.

Dina tahap ieu urang boga sistem umumna jalan. Kodena dina bentuk anu rada saderhana:

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

Sadaya anu tetep nyaéta pikeun ngonpigurasikeun detil:

1. Ngartos sabaraha ngawatesan jarak maksimum gerak dina waktu.

Penting pikeun urang ngajantenkeun kaulinan sabisa-gancang dina kaayaan jaringan sélulér anu goréng, ku kituna urang ngawatesan carita kalayan margin 30 centang (kalayan laju centang 20 Hz). Hal ieu ngamungkinkeun pamaén pencét lawan sanajan dina pings pisan tinggi.

2. Nangtukeun objék mana anu tiasa digerakkeun dina waktosna sareng mana anu henteu.

Kami, tangtosna, ngagerakkeun lawan. Tapi tameng énergi anu tiasa dipasang, contona, henteu. Kami mutuskeun yén éta langkung saé pikeun masihan prioritas kana kamampuan pertahanan, sapertos anu sering dilakukeun dina shooters online. Upami pamaén parantos nempatkeun tameng dina waktos ayeuna, pélor anu dikompensasi lag ti jaman baheula henteu kedah ngapung.

3. Mutuskeun naha perlu pikeun ngimbangan kamampuhan dinosaurus: ngegel, mogok buntut, jsb Urang mutuskeun naon diperlukeun tur ngolah aranjeunna nurutkeun aturan sarua salaku pélor.

4. Nangtukeun naon anu kudu dipigawé kalayan colliders pamaén pikeun saha santunan lag keur dipigawé. Dina cara anu saé, posisina henteu kedah ngalih ka jaman baheula: pamaén kedah ningali dirina dina waktos anu sami dimana anjeunna ayeuna aya dina server. Najan kitu, urang ogé gulung deui colliders pamuter shooting, tur aya sababaraha alesan pikeun ieu.

Kahiji, éta ngaronjatkeun clustering: urang tiasa nganggo kaayaan fisik sarua pikeun sakabéh pamaén kalawan ping nutup.

Bréh, dina sakabéh raycasts na tumpang tindihna urang salawasna ngaluarkeun colliders pamaén anu boga kamampuhan atawa projectiles. Dina Dino Squad, pamaén ngadalikeun dinosaurus, anu ngagaduhan géométri anu henteu standar ku standar penembak. Malah lamun pamuter nu némbak dina sudut mahiwal jeung lintasan bullet urang ngaliwatan collider dinosaurus pamuter urang, bullet bakal malire eta.

Thirdly, urang ngitung posisi pakarang dinosaurus atawa titik aplikasi tina kamampuh ngagunakeun data ti ECS malah saméméh mimiti santunan lag.

Hasilna, posisi nyata colliders pamaén lag-ditebus teu penting pikeun urang, jadi urang nyandak hiji leuwih produktif sarta dina waktos anu sareng jalur basajan.

Latensi jaringan teu bisa saukur dihapus, ngan bisa masked. Sapertos metode nyamur anu sanés, kompensasi lag server gaduh tradeoffs na. Ieu ngaronjatkeun pangalaman kaulinan tina pamuter anu shooting di expense tina pamuter keur ditémbak di. Pikeun Dino Squad, kumaha oge, pilihan di dieu jelas.

Tangtosna, sadayana ieu ogé kedah dibayar ku paningkatan pajeulitna kode server sacara gembleng - boh pikeun programer sareng désainer kaulinan. Upami sateuacana simulasi mangrupikeun sauran sistem anu sederhana, teras kalayan kompensasi lag, puteran sareng dahan nested muncul di jerona. Urang ogé nyéépkeun seueur usaha pikeun ngajantenkeun damel.

Dina versi 2019 (sareng panginten sakedik sateuacanna), Unity nambihan dukungan pinuh pikeun pamandangan fisik mandiri. Urang dilaksanakeun aranjeunna dina server ampir langsung saatos update, sabab urang hayang gancang meunang leupas tina dunya fisik umum pikeun sakabéh kamar.

Urang masihan unggal kamar kaulinan pamandangan fisik sorangan sahingga ngaleungitkeun kabutuhan "ngabersihan" adegan tina data kamar tatangga saméméh ngitung simulasi. Anu mimiti, éta masihan paningkatan anu signifikan dina produktivitas. Bréh, éta ngamungkinkeun pikeun ngaleungitkeun sakabeh kelas bug anu timbul lamun programmer nyieun kasalahan dina kode pamandangan cleanup nalika nambahkeun elemen kaulinan anyar. Kasalahan sapertos kitu sesah di-debug, sareng sering nyababkeun kaayaan objék fisik dina pamandangan hiji kamar "ngalir" ka kamar anu sanés.

Salaku tambahan, urang ngalakukeun sababaraha panalungtikan naha pamandangan fisik tiasa dianggo pikeun nyimpen sajarah dunya fisik. Hartina, kondisional, allocate teu hiji adegan ka unggal kamar, tapi 30 adegan, sarta nyieun panyangga siklik kaluar aranjeunna, nu nyimpen carita. Sacara umum, pilihan tétéla bisa dipake, tapi urang teu nerapkeun eta: teu némbongkeun naon gélo kanaékan produktivitas, tapi merlukeun parobahan rada picilakaeun. Éta hésé ngaduga kumaha server bakal kalakuanana nalika digawé pikeun lila kalawan jadi loba pamandangan. Ku alatan éta, urang nuturkeun aturan: "Lamun henteu peupeus, ulah ngalereskeun eta".

sumber: www.habr.com

Tambahkeun komentar