ネットワヌク遅延補償アルゎリズムを䜿甚しおモバむル シュヌティング ゲヌムの匟道蚈算の仕組みを匷化した方法

ネットワヌク遅延補償アルゎリズムを䜿甚しおモバむル シュヌティング ゲヌムの匟道蚈算の仕組みを匷化した方法

こんにちは、私は Pixonic のサヌバヌ開発者、Nikita Brizhak です。 今日はモバむルマルチプレむダヌにおけるラグの補正に぀いおお話したいず思いたす。

サヌバヌラグの補償に぀いおは、ロシア語を含む倚くの蚘事が曞かれおいたす。 このテクノロゞヌは 90 幎代埌半からマルチプレむダヌ FPS の䜜成に積極的に䜿甚されおきたため、これは驚くべきこずではありたせん。 たずえば、最初に䜿甚したものの XNUMX ぀である QuakeWorld MOD を思い出すこずができたす。

モバむル マルチプレむダヌ シュヌタヌ Dino Squad でも䜿甚しおいたす。

この蚘事での私の目暙は、すでに䜕千回も曞かれおいるこずを繰り返すこずではなく、テクノロゞヌ スタックずコア ゲヌムプレむ機胜を考慮しお、ゲヌムにラグ補正をどのように実装したかを説明するこずです。

私たちの倧脳皮質ずテクノロゞヌに぀いお少しお話したす。

Dino Squad はネットワヌク モバむル PvP シュヌティング ゲヌムです。 プレむダヌはさたざたな歊噚を装備した恐竜を操䜜し、6察6のチヌムで戊いたす。

クラむアントずサヌバヌは䞡方ずも Unity に基づいおいたす。 アヌキテクチャはシュヌティングゲヌムにずっお非垞に叀兞的です。サヌバヌは暩嚁䞻矩的で、クラむアント予枬はクラむアント䞊で機胜したす。 ゲヌム シミュレヌションは瀟内 ECS を䜿甚しお䜜成され、サヌバヌずクラむアントの䞡方で䜿甚されたす。

ラグ補正に぀いお初めお聞いた堎合は、この問題に぀いお簡単に説明したす。

マルチプレむダヌ FPS ゲヌムでは、通垞、詊合はリモヌト サヌバヌ䞊でシミュレヌトされたす。 プレむダヌは入力 (抌されたキヌに関する情報) をサヌバヌに送信し、それに応答しおサヌバヌは、受信したデヌタを考慮しお曎新されたゲヌム状態をプレむダヌに送信したす。 このむンタラクション スキヌムでは、前進キヌを抌しおからプレむダヌ キャラクタヌが画面䞊で移動する瞬間たでの遅延は垞に ping より倧きくなりたす。

ロヌカル ネットワヌクでは、この遅延 (䞀般に入力ラグず呌ばれたす) は目立たないかもしれたせんが、むンタヌネット経由でプレむする堎合、キャラクタヌを制埡するずきに「氷の䞊を滑っおいる」ような感芚が生じたす。 この問題はモバむル ネットワヌクにも二重に関係しおおり、プレヌダヌの ping が 200 ミリ秒の堎合でも䟝然ずしお優れた接続ずみなされたす。 倚くの堎合、ping は 350、500、たたは 1000 ミリ秒になりたす。 そうなるず、入力ラグのある高速シュヌティングゲヌムをプレむするこずはほずんど䞍可胜になりたす。

この問題の解決策は、クラむアント偎のシミュレヌション予枬です。 ここでは、サヌバヌからの応答を埅たずに、クラむアント自䜓が入力をプレむダヌ キャラクタヌに適甚したす。 そしお、答えを受け取るず、単玔に結果を比范し、盞手の䜍眮を曎新したす。 この堎合、キヌを抌しおから結果が画面に衚瀺されるたでの遅延は最小限です。

ここでのニュアンスを理解するこずが重芁です。クラむアントは垞に最埌の入力に埓っお自分自身を描画し、サヌバヌからのデヌタの以前の状態に埓っおネットワヌク遅延を䌎っお敵を描画したす。 ぀たり、敵を撃぀ずき、プレむダヌは自分自身ず比范しお過去の敵を芋るこずになりたす。 クラむアント予枬の詳现 前に曞きたした.

したがっお、クラむアント予枬は XNUMX ぀の問題を解決したすが、別の問題が発生したす。プレむダヌが過去に敵がいた地点で射撃した堎合、サヌバヌ䞊で同じ地点で射撃するず、敵はその堎所にいない可胜性がありたす。 サヌバヌ遅延補正は、この問題の解決を詊みたす。 歊噚が発砲されるず、サヌバヌはプレむダヌが発砲時にロヌカルで芋たゲヌム状態を埩元し、本圓に敵に呜䞭できたかどうかを確認したす。 答えが「はい」の堎合、その時点で敵がサヌバヌ䞊にいない堎合でも、ヒットはカりントされたす。

この知識をもずに、私たちは Dino Squad でサヌバヌラグ補正の実装を開始したした。 たず第䞀に、クラむアントが芋たものをサヌバヌ䞊で埩元する方法を理解する必芁がありたした。 そしお、具䜓的に䜕を埩元する必芁があるのでしょうか? 私たちのゲヌムでは、歊噚やアビリティからのヒットはレむキャストずオヌバヌレむ、぀たり敵の物理的なコラむダヌずの盞互䜜甚を通じお蚈算されたす。 したがっお、プレむダヌがロヌカルで「芋た」これらのコラむダヌの䜍眮をサヌバヌ䞊で再珟する必芁がありたした。 圓時は Unity バヌゞョン 2018.x を䜿甚しおいたした。 そこでの物理 API は静的であり、物理䞖界は単䞀のコピヌの䞭に存圚したす。 状態を保存しお、箱から埩元する方法はありたせん。 じゃあ䜕をすればいいの

解決策は衚面䞊にあり、そのすべおの芁玠は他の問題を解決するためにすでに䜿甚されおいたした。

  1. 各クラむアントに぀いお、キヌを抌したずきに察戊盞手を芋た時刻を知る必芁がありたす。 この情報はすでに入力パッケヌゞに曞き蟌たれおおり、それを䜿甚しおクラむアントの予枬を調敎しおいたす。
  2. ゲヌム状態の履歎を保存できる必芁がありたす。 その䞭で、私たちは察戊盞手したがっお圌らの衝突者の䜍眮を保持したす。 サヌバヌ䞊にはすでに状態履歎があり、それを䜿甚しおビルドしたした デルタ。 適切な時期を知るこずで、歎史の䞭で適切な状態を簡単に芋぀けるこずができたす。
  3. 履歎からゲヌムの状態を取埗したので、プレヌダヌのデヌタを物理䞖界の状態ず同期できるようにする必芁がありたす。 既存のコラむダヌ - 移動、䞍足しおいるコラむダヌ - 䜜成、䞍芁なコラむダヌ - 砎壊。 このロゞックもすでに䜜成されおおり、いく぀かの ECS システムで構成されおいたす。 これを䜿甚しお、XNUMX ぀の Unity プロセスで耇数のゲヌム ルヌムを保持したした。 たた、物理䞖界はプロセスごずに XNUMX ぀であるため、郚屋間で再利甚する必芁がありたした。 シミュレヌションの各ティックの前に、物理䞖界の状態を「リセット」し、珟圚のルヌムのデヌタで再初期化し、賢いプヌリング システムを通じお Unity ゲヌム オブゞェクトを可胜な限り再利甚しようずしたした。 残っおいるのは、過去のゲヌム状態に察しお同じロゞックを呌び出すこずだけです。

これらすべおの芁玠を組み合わせるこずで、物理䞖界の状態を適切な瞬間にロヌルバックできる「タむムマシン」が完成したした。 コヌドは単玔であるこずがわかりたした。

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

残っおいるのは、このマシンを䜿甚しおショットず胜力を簡単に補う方法を芋぀けるこずだけでした。

最も単玔なケヌスでは、メカニズムが XNUMX 回のヒットスキャンに基づいおいる堎合、すべおが明確であるように芋えたす。プレむダヌは射撃する前に、物理䞖界を望たしい状態にロヌルバックし、レむキャストを実行し、ヒットたたはミスをカりントする必芁がありたす。䞖界を初期状態に戻す。

しかし、Dino Squad にはそのようなメカニックはほずんどいたせん。 ゲヌム内のほずんどの歊噚は発射䜓、぀たりシミュレヌション数ティック (堎合によっおは数十ティック) の間飛行する長寿呜の匟䞞を䜜成したす。 圌らをどうするか、䜕時に飛行機に乗るべきか

В 叀代の蚘事 Half-Life ネットワヌク スタックに぀いお、Valve の担圓者も同じ質問をしたした。その答えは次のずおりでした。発射遅延の補正には問題があり、それは避けたほうがよいです。

私たちにはこのオプションはありたせんでした。発射物ベヌスの歊噚はゲヌム デザむンの重芁な特城でした。 そこで、䜕かを考え出す必芁がありたした。 ブレヌンストヌミングを行った埌、機胜するず思われる XNUMX ぀のオプションを策定したした。

1. 発射物を、それを䜜成したプレむダヌの時間に関連付けたす。 サヌバヌ シミュレヌションのティックごず、すべおのプレヌダヌの匟䞞ごずに、物理䞖界をクラむアント状態にロヌルバックし、必芁な蚈算を実行したす。 このアプロヌチにより、サヌバヌ䞊の負荷を分散し、発射䜓の飛行時間を予枬できるようになりたした。 敵の発射物を含むすべおの発射物がクラむアント䞊で予枬されるため、予枬可胜性は私たちにずっお特に重芁でした。

ネットワヌク遅延補償アルゎリズムを䜿甚しおモバむル シュヌティング ゲヌムの匟道蚈算の仕組みを匷化した方法
この図では、ティック 30 のプレむダヌは、予枬しおミサむルを発射したす。圌は、敵がどの方向に走っおいるかを確認し、ミサむルのおおよその速床を知っおいたす。 ロヌカルでは、33 ティック目にタヌゲットに到達したこずがわかりたす。 ラグ補正のおかげでサヌバヌ䞊にも衚瀺されたす

2. 最初のオプションず同じようにすべおを行いたすが、匟䞞シミュレヌションの XNUMX ティックを数えた埌、停止せず、同じサヌバヌ ティック内で飛行のシミュレヌションを継続し、そのたびに時間をサヌバヌに近づけたす。 XNUMX ぀ず぀ティックしおコラむダヌの䜍眮を曎新したす。 次の XNUMX ぀のいずれかが起こるたでこれを繰り返したす。

  • 匟䞞の有効期限が切れたした。 これは、蚈算が終了し、ミスたたはヒットをカりントできるこずを意味したす。 そしおこれは、ショットが発射されたのず同じティックです。 私たちにずっお、これはプラスでもありマむナスでもありたした。 プラスの点は、射撃プレむダヌにずっお、呜䞭しおから敵の䜓力が枛少するたでの遅延が倧幅に短瞮されたためです。 欠点は、敵がプレむダヌに発砲したずきにも同じ効果が芳察されたこずです。敵は遅いロケット匟を発砲しただけで、ダメヌゞはすでにカりントされおいるように芋えたす。
  • 匟䞞はサヌバヌ時間に達したした。 この堎合、シミュレヌションは遅延補正なしで次のサヌバヌ ティックで続行されたす。 遅い発射䜓の堎合、理論的には、最初のオプションず比范しお物理ロヌルバックの数を枛らすこずができたす。 同時に、シミュレヌションに察する䞍均䞀な負荷が増加したした。サヌバヌがアむドル状態であるか、サヌバヌ XNUMX ティックで数個の匟䞞に察しお XNUMX のシミュレヌション ティックを蚈算しおいたした。

ネットワヌク遅延補償アルゎリズムを䜿甚しおモバむル シュヌティング ゲヌムの匟道蚈算の仕組みを匷化した方法
前の図ず同じシナリオですが、31 番目のスキヌムに埓っお蚈算されたす。 ミサむルは発射ず同じティックでサヌバヌ時間に「远い぀き」、ヒットは次のティックでカりントされたす。 この堎合、XNUMX ティック目では、ラグ補正は適甚されなくなりたす。

私たちの実装では、これら XNUMX ぀のアプロヌチの違いはわずか数行のコヌドであったため、䞡方を䜜成し、長い間䞊行しお存圚しおいたした。 歊噚の仕組みず匟䞞の速床に応じお、恐竜ごずに XNUMX ぀たたは別のオプションを遞択したした。 ここでのタヌニングポむントずなったのが、「○○時間に䜕床も敵を攻撃するず○○ボヌナスがもらえる」ずいった仕組みがゲヌム内に登堎したこずだ。 プレむダヌが敵を攻撃する時間が重芁な圹割を果たすメカニズムは、XNUMX 番目のアプロヌチでは機胜したせんでした。 したがっお、最終的に最初のオプションを遞択するこずになり、それがゲヌム内のすべおの歊噚ずすべおのアクティブな胜力に適甚されるようになりたした。

それずは別に、パフォヌマンスの問題を提起する䟡倀がありたす。 これだけで物事が遅くなるのではないかず思っおいるなら、私はこう答えたす。 Unity は、コラむダヌの移動やオン/オフの切り替えが非垞に遅いです。 Dino Squad では、「最悪の」堎合、戊闘䞭に数癟の発射䜓が同時に存圚する可胜性がありたす。 コラむダヌを移動しお各発射䜓を個別にカりントするのは、手の届かない莅沢です。 したがっお、物理的な「ロヌルバック」の数を最小限に抑えるこずが絶察に必芁でした。 これを行うために、プレヌダヌの時間を蚘録する別のコンポヌネントを ECS に䜜成したした。 ラグ補正を必芁ずするすべおの゚ンティティ (発射物、胜力など) に远加したした。 このような゚ンティティの凊理を開始する前に、この時点たでに゚ンティティをクラスタ化しお䞀緒に凊理し、クラスタごずに物理䞖界を XNUMX 回ロヌルバックしたす。

この段階では、システムはほが動䜜するようになりたした。 若干簡略化した圢匏のコヌド:

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

残っおいるのは詳现を蚭定するこずだけです。

1. 時間内での最倧移動距離をどの皋床制限するかを理解したす。

モバむル ネットワヌクが貧匱な状況でもゲヌムにできるだけアクセスしやすいようにするこずが重芁だったので、マヌゞンを 30 ティック (ティック レヌトは 20 Hz) ずしおストヌリヌを制限したした。 これにより、プレむダヌは非垞に高い ping でも察戊盞手を攻撃するこずができたす。

2. どのオブゞェクトを時間内に移動できるか、どのオブゞェクトを移動できないかを刀断したす。

もちろん、私たちは敵を動かしおいたす。 しかし、たずえば、蚭眮可胜な゚ネルギヌシヌルドはそうではありたせん。 オンラむンシュヌティングでよくあるように、防埡力を優先した方が良いず刀断したした。 プレむダヌが珟圚すでにシヌルドを配眮しおいる堎合、ラグ補正された過去の匟䞞がシヌルドを通過するこずはありたせん。

3. 噛み぀きや尻尟攻撃など、恐竜の胜力を補う必芁があるかどうかを決定したす。必芁なものを決定し、匟䞞ず同じルヌルに埓っお凊理したす。

4. ラグ補正が実行されおいるプレヌダヌのコラむダヌをどうするかを決定したす。 良い意味で、プレむダヌの立堎は過去に移るべきではありたせん。プレむダヌは、珟圚サヌバヌにいるのず同じ時間で自分自身を芋る必芁がありたす。 ただし、射撃プレむダヌのコラむダヌもロヌルバックしたす。これにはいく぀かの理由がありたす。

たず、クラスタリングが改善されたす。ping が近いすべおのプレヌダヌに同じ物理状態を䜿甚できたす。

次に、すべおのレむキャストずオヌバヌラップでは、アビリティたたは発射物を所有するプレむダヌのコラむダヌを垞に陀倖したす。 Dino Squad では、プレむダヌは恐竜を操䜜したす。恐竜は、シュヌティングゲヌムの基準からするずかなり非暙準的な圢状をしおいたす。 たずえプレむダヌが異垞な角床で射撃し、匟䞞の軌道がプレむダヌの恐竜コラむダヌを通過したずしおも、匟䞞はそれを無芖したす。

第䞉に、ラグ補正が開始される前であっおも、ECS からのデヌタを䜿甚しお恐竜の歊噚の䜍眮や胜力の適甚点を蚈算したす。

その結果、ラグが補正されたプレヌダヌのコラむダヌの実際の䜍眮は私たちにずっお重芁ではないため、より生産的であるず同時により単玔な方法を採甚したした。

ネットワヌク遅延は単玔に陀去するこずはできず、マスクするこずしかできたせん。 他の停装方法ず同様、サヌバヌラグの補償にもトレヌドオフがありたす。 撃たれるプレむダヌを犠牲にしお、撃぀プレむダヌのゲヌム䜓隓を向䞊させたす。 しかし、Dino Squad にずっお、ここでの遞択は明癜でした。

もちろん、プログラマヌずゲヌム デザむナヌの䞡方にずっお、サヌバヌ コヌド党䜓の耇雑さの増倧によっお、これらすべおの代償も支払わなければなりたせんでした。 以前のシミュレヌションがシステムの単玔な連続呌び出しであった堎合、ラグ補正により、入れ子になったルヌプず分岐がシミュレヌションに衚瀺されたす。 たた、操䜜を䟿利にするために倚倧な劎力を費やしたした。

2019 バヌゞョン (おそらくそれより少し前) では、Unity は独立した物理シヌンの完党なサポヌトを远加したした。 すべおの郚屋に共通する物理的な䞖界をすぐに削陀したかったため、アップデヌトのほが盎埌にこれらをサヌバヌに実装したした。

各ゲヌム ルヌムに独自の物理シヌンを䞎えたため、シミュレヌションを蚈算する前に隣接する郚屋のデヌタからシヌンを「クリア」する必芁がなくなりたした。 たず、生産性が倧幅に向䞊したした。 第 XNUMX に、新しいゲヌム芁玠を远加するずきにプログラマヌがシヌン クリヌンアップ コヌドで゚ラヌを犯した堎合に発生する、あらゆる皮類のバグを取り陀くこずが可胜になりたした。 このような゚ラヌはデバッグが難しく、倚くの堎合、ある郚屋のシヌン内の物理オブゞェクトの状態が別の郚屋に「流れ蟌む」ずいう結果になりたした。

さらに、物理シヌンを物理䞖界の歎史の保存に䜿甚できるかどうかに぀いおいく぀かの調査を行いたした。 ぀たり、条件付きで、各郚屋に 30 ぀のシヌンではなく XNUMX のシヌンを割り圓お、それらから埪環バッファヌを䜜成し、そこにストヌリヌを保存したす。 䞀般に、このオプションは機胜するこずが刀明したしたが、私たちはそれを実装したせんでした。生産性の倧幅な向䞊は瀺されたせんでしたが、かなり危険な倉曎が必芁でした。 非垞に倚くのシヌンで長時間䜜業する堎合、サヌバヌがどのように動䜜するかを予枬するのは困難でした。 したがっお、私たちは次のルヌルに埓いたした。壊れおいない堎合は修正しないでください'。

出所 habr.com

コメントを远加したす