Seaside Laboratory

Articles

ラグに立ち向かえ!GGPO の低遅延ネットコードの仕掛け

オンライン対戦の遅延を軽減する技術「GGPO」がどのような仕組みになっているのか知るために、公式サイトで紹介されている記事「Fight the Lag! The Trick Behind GGPO's Low Latency Netcode」の一部を興味本位で日本語に翻訳したものです。英語が得意なわけでもないので間違いもあるかと思いますがご了承ください。

より技術的な内容については GitHub にある「GGPO とは」を参照。

Hiding The Lag (ラグを隠す)

In GGPO, the local actions performed by the player always happen instantly, and are always correct. This is a great property to have, as the responsiveness of the avatar to the controller is often the most important aspect in the player's enjoyment of the online experience.

GGPO では、プレーヤーによって行われるローカルのアクションは常に瞬時に発生し、常に正しいものとなります。コントローラーに対するキャラクターの応答性はオンライン体験を楽しむ上で重要な要素であることが多いため、これは素晴らしい特性です。

Furthermore, any long-term effects caused by remote players whose inputs have already been received are also correct. For example, if your opponent in Street Fighter threw a fireball at you a few frames ago, the behavior of that fireball is completely deterministic and cannot be affected by your opponent's future inputs. Therefore, the fireball will appear to move correctly immediately after your local simulation state has determined that the player actually did throw the fireball. This makes the timing and experience of dealing with a fireball online identical to the experience offline, which is important, as dealing with fireballs is a major part of Street Fighter!

さらに、既に入力が受信されているリモートプレーヤーによって引き起こされる長期的な影響も正しいものとなります。例えばストリートファイターで対戦相手が数フレーム前に飛び道具を撃っていた場合、その飛び道具の挙動は完全に決定論的であり、対戦相手の未来の入力の影響を受けることはありません。そのためローカルのシミュレーションが実際に飛び道具を撃ったと判断した直後に飛び道具は正しく動くようになります。これによりオンラインで飛び道具を扱うタイミングや感覚はオフラインと同じになり、飛び道具はストリートファイターの主軸であるため重要です!

Similarly, opponents usually cannot change the arc of their jumps after initiating them, so dealing with your opponent descending from a jump, perhaps with a well-placed Dragon Punch, is identical both online and offline. So if everything appears to be correct all the time, where'd the latency go? The latency is hidden in the window between when your opponent initiates an action and your simulation realizes that an action was performed. The time lost in that window is effectively skipped to your simulation. For example, suppose both you and your opponent are playing a game of Street Fighter on a network that takes 60ms to send a packet from one console to the next. When your opponent executes a move, his simulation will process the controller inputs immediately. To him, the move comes out right away, since local inputs are sent to the simulation immediately and are always correct. Your simulation, however, will not notice that your opponent performed a move for another 60ms, when a packet arrives from the network carrying that input. When it does, GGPO will instruct the game to rewind 60ms, correct your opponent's input, and fast-forward the game simulation 60ms back to the current time. As a result, on your console you will not see the first 60ms of animation of whatever your opponent did. It is as if the move began 60ms into the animation, from your perspective.

同様に、対戦相手は通常、ジャンプした後に軌道を変えることができないため、落下してきた相手に昇龍拳をうまく当てて対処することはオンラインでもオフラインでも同じです。では、全てが正しく見える場合、遅延はどこへ行ったのでしょうか?遅延は対戦相手がアクションを開始してからシミュレーションがアクションの実行を認識するまでの間に隠されています。この間に失われた時間は事実上シミュレーションによってスキップされます。例えばあなたと対戦相手がゲーム機からのパケット送信に 60ms かかるネットワークでストリートファイターをプレイしているとします。対戦相手がアクションを実行すると対戦相手のシミュレーションは即座にコントローラーの入力を処理します。ローカルの入力は常に正しいためアクションは瞬時に反映されます。しかし、あなたのシミュレーションがそのアクションに気が付くのはネットワークから入力を含んだパケットが届く 60ms 後となります。そのとき GGPO は、ゲームを 60ms 巻き戻し、対戦相手の入力を修正し、シミュレーションを 60ms 早送りして現在の時間に戻します。その結果、相手が何をしたにせよ、ゲーム機上には最初の 60ms のアニメーションが表示されません。あなたの視点から見るとアニメーションが 60ms 後から開始したように見えます。

This is not ideal, but the alternative is to delay the entire simulation by 60ms, including local inputs. In practice, losing those 60ms of animation usually results in a greatly preferable user experience. This is partially due to the greatly increased responsiveness of local actions, but is also because most of the time those 60ms just don't matter that much. To illustrate this, let's look at a specific example.

これは理想的ではありませんが、代替案はローカル入力を含めシミュレーション全体を 60ms 遅らせることです。実際には、60ms のアニメーションをスキップすることで非常に優れたユーザー体験を得ることができます。これは、ローカルアクションの応答性が大幅に向上したことによるものですが、多くの場合、この 60ms はさほど重要ではないからです。これを説明するために具体例を見てみましょう。

Most attacks in Street Fighter have three phases: start-up, execution, and recovery (see Figure 4). The start-up of a move is how long a move takes to become active after the user presses the button. While there is usually some animation state associated with the start-up of a move, the move isn't actually doing any damage yet. The serious business happens in the execution window; if the opponent overlaps your move's active region at any time during the execution window, the game simulation will register it as a hit. A hit causes the simulation to start new animations, play audio, subtract some life from your opponent, and lots of other effects. It's a big deal as far as simulation state is concerned. Recovery is simply the duration after a move executes before you can perform another one. These numbers are usually measured in frames, and the first thing competitive Street Fighter players do when a new game comes out is to mine the frame data for every move in the game to begin researching tactics. If we look at the frame data for one of the most beloved and fastest Street Fighter games on the market, Super Street Fighter ii turbo (http://nki.combovideos.com/flame.html), we see that a vast majority of the moves have a start-up of at least four frames (or 66ms). That's incredibly important to us, because it means that on a 120ms ping connection, it's possible to resolve almost every rollback before the execution of a move, which means that the visual and audio glitches that result from incorrect predictions are almost always limited to the animation of the start-up of a move, not the result of hitting your opponent. Modern broadband connections from Los Angeles to New York typically have a faster ping than 120ms. In fact, anecdotal evidence shows that GGPO's techniques scale well up to latency experiences on broadband connections worldwide;

ストリートファイターのほとんどの攻撃には「発生」「持続」「硬直」の 3 つの段階があります (図 4 を参照)。発生は、ユーザーがボタンを押してから攻撃が持続になるまでの時間です。通常、攻撃が開始されるときにアニメーションが表示されますが、まだダメージを与えていません。重大なことは持続段階で行われます。持続中に攻撃の当たり判定が相手と重なった場合、シミュレーションはそれをヒットとして記録します。ヒットすると、シミュレーションは新しいアニメーションを開始し、サウンドを再生し、相手の体力を減らし、その他多くの効果をもたらします。シミュレーションの状態において、これは大きな意味を持ちます。硬直は、持続が終わってから次の動作が行えるようになるまでの時間です。これらの時間はフレーム単位で表されており、新作が発売されたときにストリートファイターのプレーヤーが最初にすることは、全ての技のフレームデータを調べ、攻略を始めることです。ストリートファイターの中でも人気があり、最速のゲームである「スーパーストリートファイター II X」のフレームデータを見ると、多くの技の発生には少なくとも 4 フレーム (66ms) かかることがわかります。これは非常に重要なことで、ping が 120ms の接続であれば技の発生前にほぼ全てのロールバックを行うことが可能であることを意味します。つまり、誤った予測による視覚的または聴覚的な不具合は、ほとんどの場合、対戦相手にヒットした結果ではなく発生アニメーションに限定されるということです。ロサンゼルスとニューヨーク間での最新のブロードバンド接続では、通常 ping は 120ms よりも高速です。実際、GGPO の技術は世界中のブロードバンド接続での遅延に十分対応できることが実証されています。

図 4: 一般的なブロードバンド接続であれば攻撃の発生段階で遅延を隠すことができます。

GGPO's Prediction Algorithm (GGPO の予測アルゴリズム)

A good prediction algorithm can minimize the visual glitches that can occur during a rollback. The prediction algorithm's job is to anticipate the player inputs arriving from the network at frame N + (L / F) given the inputs for all previous frames 1...N, where N is the input last received from the remote player, L is the one-way packet transmission time, and F is the frequency at which the game executes its game state.

優れた予測アルゴリズムはロールバック中に発生する画面の乱れを最小限に抑えることができます。予測アルゴリズムの仕事は、以前の全てのフレーム 1 ~ N までの入力が与えられたときに、フレーム N + (L / F) にネットワークから到着するプレーヤー入力を予測することで、N はリモートプレーヤーから最後に受信した入力、L は片道のパケット送信時間、F はゲームを実行する頻度です。

The quality of the prediction algorithm is a function of the frequency and severity of the rendering glitches caused by a missed prediction. For example, in a game of PONG, a misprediction will cause the opponent's paddle to jump to a new position on the screen. Getting the paddle position wrong is important, of course, but an algorithm that is always wrong but only off by a few pixels is preferable to one that is only wrong 10% of the time but causes the opponent's paddle to jump by one-quarter of the screen length.

予測アルゴリズムの品質は予測ミスによるレンダリング不具合の頻度と深刻さの関数です。例えば PONG というゲームでは予測を誤ると対戦相手のパドル (ラケット) が関係のない位置にジャンプしてしまいます。パドルの位置を間違えることは重大ですが「常に間違っているが数ピクセルの誤差しか生じない」アルゴリズムの方が「間違える確率は 10% だが画面の 4 分の 1 の距離をジャンプしてしまう」アルゴリズムよりも好ましいです。

Naturally, the best-possible prediction algorithm is game-specific, and probably player-specific as well. GGPO's built-in algorithm is designed to work well with fighting games and beat-'em-ups, though it should work equally well for shooters, maze-solving games (PACMAN, GAUNTLET) and most other arcade games. To date, no developer who has used GGPO has seen the need to implement his own prediction.

当然、最適な予測アルゴリズムはゲーム毎に異なり、おそらくプレーヤー毎にも異なります。GGPO の組み込みアルゴリズムは格闘ゲームやベルトアクションゲームでうまく機能するように設計されていますが、シューティングゲーム、迷路型のゲーム (パックマン、ガントレット)、および他のほとんどのアーケードゲームでも同様に機能するはずです。これまでのところ、GGPO を使っている開発者の中で独自の予測アルゴリズムを実装する必要性を感じた人はいません。

The built-in prediction algorithm assumes future inputs will be identical to the inputs most recently received from the remote player. This caps the number of prediction errors to the number of times the input state changes per interval. While simple, this has proved to work quite well in avoiding the most jarring visual effects. For example, the character moving backward or forward will often cause the screen to scroll left or right. Getting that position wrong will result in the entire screen jumping to the left or right during a rollback. By assuming the joystick remains held in one direction, we ensure that the screen scrolls smoothly in the most common case: running to the right. The worst case (rapidly toggling the joystick from left to right) seldom occurs in actual gameplay.

組み込みの予測アルゴリズムは未来の入力がリモートプレーヤーから最後に受信した入力と同一であると想定しています。これにより予測エラーの数は入力状態が変化する回数に抑えられます。これは単純ですが最も不快な画面効果を回避する上で非常にうまく機能することが確認されています。例えば、キャラクターが前後に移動すると画面が左右にスクロールすることがよくあります。その位置を間違えるとロールバック中に画面全体が左右にジャンプします。ジョイスティックが一方向に保たれたままであると想定することで、最も一般的なケース (右への移動) では画面がスムーズにスクロールするようにします。最悪のケース (ジョイスティックを左右に素早く動かす) は実際のゲームプレイでは滅多に発生しません。

Time-permitting, it may be preferable to use the game-state of the previous 1...N frames to provide a better prediction algorithm than the one built into GGPO. For example, in a game of Pong, it might be better to predict that the remote opponent will always move their paddle in a manner to intercept the ball, regardless of their previous inputs. In practice, however, incorporating the possibility of a misprediction into the design of the game to minimize the effect of a rollback has a better payoff, as it's impossible to completely eliminate them. For example, if you have a screen-scroll effect when a player moves on the screen, consider basing it on the average position of the player over several frames or the position of the player K frames ago (where K >= the expected latency in a game), which will minimize (or completely eliminate) the screen-jumping glitch during a rollback. If you have any effects that start on the first frame of execution (e.g. a dramatic screen blackout starting a Super Combo in a fighting game), consider starting the effect on the Kth frame instead of the 0th frame so that the effect is always based on confirmed inputs instead of predicted inputs. Obviously, there is some give-and-take between the responsiveness of the game when played offline and the smoothness when played online using GGPO, but it is an option for designers.

時間が許すのであれば、以前の 1 ~ N フレームのゲーム状態を使って GGPO に組み込まれているものよりも優れた予測アルゴリズムを提供することが望ましい場合があります。例えば PONG では、以前の入力に関係なくリモートプレーヤーは「常にボールを受け取るためにパドルを移動する」と予測する方が良い場合があります。ただし、実際には予測ミスを完全に排除することが不可能であるため、ロールバックの影響を最小限に抑えるためにゲームデザインに予測ミスの可能性を織り込むことはより良い結果をもたらします。例えばプレーヤーが画面上を移動するときにスクロールが発生する場合、数フレームに渡るプレーヤーの平均位置や K フレーム前のプレーヤーの位置を基準にすることを検討してください (K >= ゲームで予想される遅延)。これによりロールバック中の画面ジャンプが最小限に抑えられます (または完全に無くなります)。最初のフレームで開始するエフェクトがある場合 (例: 格闘ゲームでのスーパーコンボ開始時の暗転)、0 フレーム目ではなく K フレーム目にエフェクトを開始することを検討してください。これによりエフェクトは常に、予測された入力ではなく確認された入力に基づきます。もちろん、オフラインでプレイしたときの応答性と GGPO を使用してオンラインでプレイしたときの滑らかさの間には多少のトレードオフがありますが、それはゲームデザイナーにとって選択肢のひとつです。