Adjust Japanese documentation.

This commit is contained in:
BUGMAN
2019-11-07 23:38:39 +09:00
parent 5d4772c9b1
commit 6e02f97848
2 changed files with 53 additions and 53 deletions

View File

@@ -25,13 +25,13 @@ Windowsのビルドは[Visual Studio 2019](https://visualstudio.microsoft.com/do
## サンプルアプリケーション
ソースディレクトリ内のVector Warアプリケーションでは、2つのクライアントを同期するGGPOが搭載されています。コマンドライン引数は
ソースディレクトリ内のVector Warには、GGPOを使った2つのクライアントを同期する単純なアプリケーションが含まれています。コマンドライン引数は以下の通りです。
```
vectorwar.exe <localport> <num players> ('local' | <remote ip>:<remote port>) for each player
```
24プレイヤーゲームの始め方についての例は、binディレクトリにある.cmdファイルをご覧ください。
24プレイヤーでのゲーム開始方法の例については、binディレクトリにある.cmdファイルを参照してください。
## ライセンス

View File

@@ -4,34 +4,34 @@ GGPOネットワークライブラリ開発者ガイドは、アプリケーシ
## ゲームステートと入力
ゲームは数多くの変化するパートがあると思います。GGPOは次の2つだけに依存します。
ゲームは数多くの変化するパートがあると思います。GGPOは次の2つだけに依存します。
- **ゲームステート**はゲーム内で現在の状態全てを表します。シューティングゲームの場合、画面上にある自機と敵機の位置、ショットや敵弾の位置、敵機の残り体力、現在のスコアなどになります。
- **ゲームステート**はゲームでの全ての状態を表します。シューティングゲームの場合、画面上にある自機と敵機の位置、ショットや敵弾の位置、敵機の体力、現在のスコアなどになります。
- **ゲーム入力**はゲームステートを変更する一連のものを指します。プレイヤーが操作したジョイスティックやボタンも間違いなく含みますが、目に見えない入力も含みます。例えば、ゲーム内で何かを計算するために現在時刻を使用したならば、フレームの最初になる現在時刻も、また入力になります。
- **ゲーム入力**はゲームステートを変更する一連のものを指します。言うまでもなく、プレイヤーが操作したジョイスティックやボタンの押下が含まれますが、入力以外のものも含みます。例えば、現在時刻を使って何かを計算した場合、フレームを開始した時の時刻も入力になります。
ゲームエンジンの中にはゲームステートでも入力でもないものが多数あります。例えばオーディオやビデオレンダラーはゲームの結果に影響を与えないので、ゲームステートではありません。ゲームに影響を与えないような、特殊効果を生成する特殊効果エンジンがあれば、ゲームステートから除外することができます。
ゲームエンジンにはゲームステートでも入力でもないものが他にもたくさんあります。例えばオーディオやビデオレンダラーはゲームの結果に影響を与えないため、ゲームステートではありません。ゲームに影響を与えない特殊効果を生成する特殊効果エンジンがあったとしたら、それもゲームステートから除外できます。
## 同期にステートと入力を使用する
GGPOを使ったゲームで遊ぶ各プレイヤーは、現在遊んでいるゲームの完全なコピーを持っています。両プレイヤーが同じゲーム内容遊べるよう、GGPOは両プレイヤーにあるゲームステートのコピーを同期し続ける必要があります。プレイヤー間で各フレームごとにゲームステート全コピーを送信することは大きな負荷になってしまいます。代わりにGGPOはお互いの入力を送信し、互いのゲームを進めています。この機能のために、ゲームエンジン3つの条件を満たさねばなりません
GGPOを使ったゲームで遊ぶ各プレイヤーは、プレイしているゲームの完全なコピーを持っています。両プレイヤーが同じゲーム内容遊べるよう、保持しているゲームステートのコピーを同期し続ける必要があります。フレームが進む度にプレイヤー間でゲームステート全コピーを送信するは大きな負荷になます。代わりにGGPOはお互いの入力を送信し、各プレイヤーのゲームを進めます。これが機能するには、ゲームエンジン3つの条件を満たしている必要があります
- ゲームシミュレーション完全に決定的であること。与えられたゲームステートと入力があった場合、1フレームゲームステートを進めた際に全プレイヤーのゲームステートが全く同一の結果にならなければいけません。
- ゲームシミュレーション完全に決定的でなければなりません。つまり、特定のゲームステートと入力があった時に、ゲームステートを1フレーム進めると全プレイヤーのゲームステートが同じにならなければいけません。
- ゲームステートが完全にカプセル化され、シリアライズが可能であること。
- ゲームエンジンそのフレームのゲーム内容をレンダリングすることなく、復元、保存、フレームのシミュレーションができること。これはロールバックを実装するに使用ます。
- ゲームエンジンそのフレームのゲーム内容をレンダリングすることなく、復元、保存、フレームのシミュレーションができなくてはなりません。これはロールバックを実装するために使用されます。
## プログラミングガイド
次のセクションはあなたのアプリケーションをGGPOに移植する一連の流れを紹介します。GGPO APIの詳細な説明は、以下のGGPOリファレンスセクションを参照してください。
次のセクションはあなたのアプリケーションをGGPO上で動作させるための一連の流れを紹介しています。GGPO APIの詳細な説明については、以下のGGPOリファレンスセクションを参照してください。
### GGPOとのインターフェース
### GGPOとの繋ぎ込み
GGPOは新規および既存のゲームエンジンと簡単に繋ぎ込みができるよう設計されています。`GGPOSessionCallbacks`フックを介してアプリケーション呼び出すことにより、ほとんどのロールバックの実装を行います。
GGPOは新規および既存のゲームエンジンと簡単に繋ぎ込みができるよう設計されています。`GGPOSessionCallbacks`フックを介してアプリケーション呼び出すことにより、ほとんどのロールバックの実装を行います。
### GGPOSessionオブジェクトの生成
`GGPOSession`オブジェクトはGGPOフレームワークへのインターフェースです。ローカル、IPアドレス、対戦したいプレイヤーをバインドするためのポートを渡す`ggponet_start_session`関数を使用して作成します。またセッションが1プレイヤー側、2プレイヤー側いずれの場合でも、ゲームステートを管理するコールバック関数で満たされた`GGPOSessionCallbacks`オブジェクト渡す必要があります。全ての`GGPOSessionCallback`関数を実装しなければなりません。詳細は以下を参照してください。
として、ポート8001にバインドしている別プレイヤーと同じホストで新しいセッションを開始する場合、次のようになります。
`GGPOSession`オブジェクトはGGPOフレームワークへのインターフェースです。ローカルのポートと、対戦したいプレイヤーのIPアドレスとポートを`ggponet_start_session`関数をして作成します。またゲームステートを管理するコールバック関数で満たされた`GGPOSessionCallbacks`オブジェクトと、このセッションで遊ぶプレイヤーの数を渡す必要があります。全ての`GGPOSessionCallback`関数を実装しなければなりません。詳細は以下を参照してください。
えば、ポート8001にバインドされた別のプレイヤーと同じホストで新しいセッションを開始する場合、次のようになります。
```
GGPOSession ggpo;
@@ -64,9 +64,9 @@ GGPOは新規および既存のゲームエンジンと簡単に繋ぎ込みが
ggpo_close_session(ggpo);
```
### プレイヤーの場所を送信
### プレイヤーの場所を送信する
GGPOSessionオブジェクトを作成した時、ゲームに参加しているプレイヤーの数を渡しましたが、どのように連携をするか実際には説明していません。そのためには、それぞれのプレイヤーを表す`GGPOPlayer`オブジェクトを渡して、`ggpo_add_player`呼び出します。以下は2プレイヤーで遊ぶ時に`ggpo_add_player`を使用する際の例になります。
GGPOSessionオブジェクトを作成した時、ゲームに参加しているプレイヤーの数を渡しましたが、実際にそれらを連携をする方法について説明していませんでした。これを行うには、各プレイヤーを表す`GGPOPlayer`オブジェクトを`ggpo_add_player`関数に渡して呼び出します。次の例は、2人用のゲームでの`ggpo_add_player`の使い方です。
```
GGPOPlayer p1, p2;
@@ -85,10 +85,10 @@ result = ggpo_add_player(ggpo, &p2, &player_handles[1]);
### ローカルと遠隔プレイヤーによる入力の同期
入力の同期は各ゲームフレームの最初に行われます。各ローカルプレイヤー`ggpo_add_local_input`の呼び出しと、遠隔プレイヤーの入力を取得するために`ggpo_synchronize_input`の呼び出しをすることによって行われます。
`ggpo_synchronize_inputs`の戻り値必ず確認するようにしてください。`GGPO_OK`以外の値を返す場合、ゲームステートを次に進めるべきではありません。僅かな間、遠隔プレイヤーからパケットを受信していなかったり、内部で行われる予測の限界に達することがあるために、こういったことは頻繁に発生します。
入力の同期は各ゲームフレームの最初に行われます。各ローカルプレイヤーに対する`ggpo_add_local_input`の呼び出しと、遠隔プレイヤーの入力を取得する`ggpo_synchronize_input`の呼び出しによって行われます。
`ggpo_synchronize_inputs`の戻り値必ず確認するようにしてください。`GGPO_OK`以外の値が返ってきた場合、ゲームステートを進めないでください。これは通常、GGPOがしばらくの間、遠隔プレイヤーからパケットを受信せず、内部の予測制限に達したことで発生します。
例えば、ローカルゲームのコードがのよう場合、
例えば、ローカルゲームのコードがのようになっている場合、
```
GameInputs &p1, &p2;
@@ -97,7 +97,7 @@ result = ggpo_add_player(ggpo, &p2, &player_handles[1]);
AdvanceGameState(&p1, &p2, &gamestate); /* send p1 and p2 to the game */
```
次のように変更するべきです。
次のように変更する必要があります。
```
GameInputs p[2];
@@ -121,11 +121,11 @@ result = ggpo_add_player(ggpo, &p2, &player_handles[1]);
}
```
ロールバック中に発生したものでも、フレームごとに`ggpo_synchronize_inputs`を呼び出す必要があります。ゲームステートを進めるためには、ローカルコントローラーから得られた値を読むのではなく、常に`ggpo_synchronize_inputs`から返された値を使用してください。ロールバック中`ggpo_synchronize_inputs`前のフレームに使われた値とともに、`ggpo_add_local_input`に渡された値を置き換えます。また、ロールバックの影響を緩和するためにローカルプレイヤー向けの入力遅延を加えた場合、フレーム遅延が終えるまで`ggpo_add_local_input`に渡され入力は`ggpo_synchronize_inputs`に返されません。
ロールバック中に発生したものも含め、全てのフレーム`ggpo_synchronize_inputs`を呼び出す必要があります。ゲームステートを進めるためには、ローカルコントローラーから得られた値を読むのではなく、常に`ggpo_synchronize_inputs`から返された値を使用してください。ロールバック中`ggpo_synchronize_inputs``ggpo_add_local_input`に渡された値を前のフレームに使われた値に置き換えます。また、ロールバックの影響を緩和するためにローカルプレイヤー向けの入力遅延を加えた場合、`ggpo_add_local_input`に渡され入力はフレーム遅延が終わるまで`ggpo_synchronize_inputs`に返されません。
### 保存、復元、解放コールバックの実装
定期的にゲームステートを保存、復元するために、GGPOは`load_game_state``save_game_state`コールバックを使用します。`save_game_state`関数はゲームの現在のステートを復元し、`buffer`出力パラメーター内に戻るために、十分な情報を含んだバッファーを作成する必要があります。`load_game_state`関数は以前に保存したバッファーからゲームステートを復元します。例えば、
GGPOはゲームステートを定期的に保存または復元するために、`load_game_state``save_game_state`コールバックを使用します。`save_game_state`関数はゲームの現在のステートを復元し、それを`buffer`出力パラメーターで返すのに十分な情報を含バッファーを作成する必要があります。`load_game_state`関数は以前に保存したバッファーからゲームステートを復元します。例えば、
```
struct GameState gamestate; // Suppose the authoritative value of our game's state is in here.
@@ -151,7 +151,7 @@ ggpo_load_game_state_callback(unsigned char *buffer, int len)
}
```
これ以上必要がなくなった時は、GGPOがあなたの`save_game_state`コールバックにある割り当てたメモリを解放するために、`free_buffer`コールバックを呼び出します。
不要になった、GGPO`free_buffer`コールバックを呼び出して、`save_game_state`コールバック割り当てたメモリを解放します。
```
void __cdecl
@@ -163,49 +163,49 @@ ggpo_free_buffer(void *buffer)
### 残っているコールバックの実装
前述のように、`GGPOSessionCallbacks`構造体にはオプションのコールバックありません。少なくとも`return true`である必要がありますが、残りのコールバックはすぐに実装する必要はありません。詳細については`ggponet.h`のコメントを参照してください。
前述のように、`GGPOSessionCallbacks`構造体にはオプション扱いのコールバックありません。これらは少なくとも`return true`である必要がありますが、残りのコールバックは必ずしもすぐに実装する必要はありません。詳細については`ggponet.h`のコメントを参照してください。
### ggpo_advance_frameとggpo_idle関数の呼び出し
いよいよ終わりに近づいてきています。大丈夫、お約束します。最後のステップはゲームステート1フレームごとに進んだら、毎回GGPO通知することです。1フレームを終えた後、次のフレームを開始する前に`ggpo_advance_frame`を呼び出すだけです。
いよいよ終わりに近づいてきました。大丈夫、お約束します。最後のステップはゲームステート1フレーム進める度にGGPO通知することです。1フレームを終えた後、次のフレームを開始する前に`ggpo_advance_frame`を呼び出すだけです。
GGPOは内部記録パケットを送受信するために、一定の時間が必要になります。GGPOに許可したミリ秒単位で、最低でもフレームごとに1回は`ggpo_idle`関数を呼び出す必要があります。
GGPOは内部記録を行うパケットを送受信するために、一定の時間が必要になります。GGPOに許可したミリ秒単位で、最低でもフレームごとに1回は`ggpo_idle`関数を呼び出す必要があります。
## アプリケーションのチューニング: フレーム遅延 vs 投機的実行
遅延を感じさせないようにするために、GGPOはフレーム遅延と投機的実行の両方を使用します。アプリケーション開発者が入力遅延フレーム数はどの程度にするか、については選択できます。もしゲームのフレーム数よりパケットの送信に時間がかか場合、GGPO投機的実行を使って残りの遅延を隠します。もし希望すれば、ゲーム中でもこの数字を変更できます。フレーム遅延の適切な値はゲームによって依存します。以下は役に立つヒントです
GGPOは遅延を感じさせないようにするために、フレーム遅延と投機的実行の両方を使用します。これは、アプリケーション開発者が入力遅延させるフレーム数選択できるようにすることで実現します。もしゲームのフレーム数よりパケットの送信に時間がかかった場合、GGPO投機的実行を使って残りの遅延を隠します。この数値は、必要に応じてゲーム中でも調整することができます。フレーム遅延の適切な値はゲームに大きく依存します。役に立つヒントをいくつか紹介しましょう
まずはゲームを遊ぶ感覚に影響を与えない範囲で、フレーム遅延を出来るだけ大きく設定してみてください。例えば格闘ゲームであればドット単位の精度を要する操作、寸分違わぬタイミング、極めて素早く動かすアーケードコントローラーといった要素があります。こういったゲームの場合、中級プレイヤーの大半は2フレームの遅延に気付き、上級プレイヤーであれば1フレームの遅延に気付くこともあります。一方、厳密なタイミング操作を必要としないボードゲームやパズルゲームなら、45のフレーム遅延であればユーザーが遅延に気付き始める前に上手くゲームを進められるかもしれません。
まずはゲームを遊ぶ感覚に影響を与えない範囲で、フレーム遅延を出来るだけ大きく設定してみてください。例えば格闘ゲームでドット単位の精度、寸分違わぬタイミング、非常に正確なアーケードコントローラーの操作が必要となります。このタイプのゲームでは、ほとんどの中級プレイヤーは2フレームの遅延に気付き、上級プレイヤーであれば1フレームの遅延に気付くこともあります。一方、厳密な操作を必要としないボードゲームやパズルゲームであれば、45のフレーム遅延を設定すればユーザーが気付く前に上手くゲームを進められるかもしれません。
フレーム遅延を大きく設定するもうひとつの理由は、ロールバック中に発生し得るグリッチ(不具合)を排除することにあります。ロールバックが長くなればなるほど、間違った予測フレームを一時的に実行したこと、本来存在しないシーンを継ぎ接ぎした様子が表示される可能性が高くなるからです。例えば、ユーザーがボタンを押した瞬間にちょうど2フレーム分全画面フラッシュする仕様のゲームがあったとします。フレーム遅延を1に設定し、パケット送信に4フレームかかったとすると、この場合ロールバックは約3フレーム分(4 - 1 = 3)になります。フラッシュがロールバックの最初のフレーム発生した場合、2フレームのフラッシュはロールバックで消えてしまい、遠隔で遊ぶプレイヤーはフラッシュ演出が見えなくなってしまいます。この場合、さらに大きなフレーム遅延値を設定するか、ロールバック発生後までフラッシュを遅らせるようビデオレンダラーを再設計するか、のどちらかになります
フレーム遅延を大きく設定するもうひとつの理由は、ロールバック中に発生し得るグリッチ(不具合)を排除することにあります。ロールバックが長くなればなるほど、間違った予測フレームを一時的に実行したことによって生じた、本来存在しないシーンを継ぎ接ぎした様子が表示される可能性が高くなります。例えば、ユーザーがボタンを押した瞬間に2フレーム画面フラッシュが起きるゲームがあったとします。フレーム遅延を1に設定し、パケット送信に4フレームかかった場合ロールバックは約3フレーム分(4 - 1 = 3)になります。フラッシュがロールバックの最初のフレーム発生した場合、2フレームのフラッシュはロールバックによって完全に消失してしまい、遠隔で遊ぶプレイヤーはフラッシュ演出を見ることができなくなります。この場合、さらに大きなフレーム遅延値を設定するか、ロールバック発生後までフラッシュを遅らせるようビデオレンダラーを再設計するのが良いでしょう
## サンプルアプリケーション
ソースディレクトリ内のVector Warアプリケーションでは、2つのクライアントを同期するGGPOが搭載されています。コマンドライン引数は以下の通りです。
ソースディレクトリ内のVector Warには、GGPOを使った2つのクライアントを同期する単純なアプリケーションが含まれています。コマンドライン引数は以下の通りです。
```
vectorwar.exe <localport> <num players> ('local' | <remote ip>:<remote port>) for each player
```
24プレイヤーゲーム開始方法についての例は、binディレクトリにある.cmdファイルを参照してください。
24プレイヤーでのゲーム開始方法の例については、binディレクトリにある.cmdファイルを参照してください。
## ベストプラクティスとトラブルシューティング
以下はGGPOへアプリケーションを移植する際に検討したいベストプラクティスの一覧です。最初の段階からゲームを始めなくとも、ここで推奨している多くは簡単に理解することができます。多くのアプリケーションは推薦している以下の手法に準拠しています。
以下はアプリケーションをGGPO上で動作させる際に検討したいベストプラクティスの一覧です。これら推奨事項は、まだゲームを作り初めていない段階でも簡単に理解できます。多くのアプリケーションは既にほとんどの推奨事項を満たしています。
### ゲームステートゲームステートを分け
### ゲームステートを非ゲームステートから分離す
GGPOは定期的にゲームにおける全ステートの保存と復元を要求します。多くのゲームにとって、保存が必要となるステートはゲーム全体のなかでも小さな要素にあたります。ビデオやオーディオレンダラー、テーブルの検索、テクスチャー、サウンドデータ、コードセグメントは大半の場合、フレームからフレームまで一定か、ゲームステートの計算には関与しません。これら保存復元する必要ありません。
GGPOは定期的にゲームステート全体の保存と復元を要求します。ほとんどのゲームにおいて、保存が必要ステートはゲーム全体のごく一部です。通常、ビデオやオーディオレンダラー、テーブルの検索、テクスチャー、サウンドデータ、コードセグメントは、フレームごとに不変であるか、ゲームステートの計算には影響しません。これら保存または復元する必要ありません。
ゲームステートから非ゲームステートを出来る限り分けましょう。例えば、全ゲームステートをC言語の構造体カプセル化することを考えるかもしれません。この二つは、ゲームステートであるものそうでないもの、保存復元のコールバック実装に必要でないものをはっきりと分けてくれます(詳細についてはリファレンスガイドを参照してください)。
できるだけゲーム以外の状態をゲームステートから分離する必要があります。例えば、全ゲームステートをC言語の構造体カプセル化することを考えるかもしれません。こは、ゲームステートであるものそうでないものが明確に区別され、保存復元のコールバック実装が簡単になります(詳細についてはリファレンスガイドを参照してください)。
### ゲームステートを進める際の固定時間を定義する
GGPOは時折、ロールバックやフレームごとにアプリケーションに対してシングルステップ実行をする必要があります。もしあなたのゲームステート可変ティックレートで進めているのであれば難しい実行です。レンダーループがそうでなくとも、ゲームステートの進行はフレームにつき固定時間でするようにしてください。
GGPOはフレームごとにアプリケーションのロールバックとシングルステップ実行を必要とすることがあります。もしゲームステート可変ティックレートで進めている場合、実行は困難になります。レンダーループがそうでない場合でも、フレームごとに固定時間単位でゲームステートを進めるようにしてください。
### ゲームループでのレンダリングからゲームステートの更新を分離する
### ゲームループ内にあるレンダリングからゲームステートの更新を分離する
GGPOはロールバック中、事前フレームコールバックを何度も呼び出します。ロールバックが完了するまで、ロールバック中に発生したエフェクトやサウンドは先延ばしする必要があります。これはレンダーステートからゲームステートを分離することで簡単に実現できます。完了したら、以下のようになるでしょう。
GGPOはロールバック中に、advance frameコールバックを何度も呼び出します。ロールバック中に発生するエフェクトやサウンドはロールバックが完了するまで先延ばしする必要があります。これはゲームステートとレンダーステートを分離することで最も簡単に実現できます。分離が出来たら、ゲームループは次のようになるでしょう。
```
Bool finished = FALSE;
@@ -221,29 +221,29 @@ GGPOはロールバック中、事前フレームコールバックを何度も
while (!finished);
```
言い換えると、ゲームステートは入力のみで決定され、レンダリングコードは現在のゲームステートによって動作をします。レンダリングなしで一連の入力をもとに簡単にゲームステートを進める方法であるべきです。
言い換えると、ゲームステートは入力のみで決定され、レンダリングは現在のゲームステートによって実行される必要があります。また、レンダリングせずに一連の入力をにゲームステートを簡単に進める方法が必要です。
### ゲームステートの進行決定的であること
### ゲームステートの進行決定的であることを確認する
ゲームステートを特定したら、必ず次のゲームステートはゲーム入力のみ計算されるようにしてください。ゲームステートと入力が全て正しく識別できたなら自然と発生しますが、時として手が込む作業です。以下は見落としやすい内容です。
ゲームステートを特定したら、次のゲームステート入力のみから計算されることを確認します。これは、全てのゲームステートと入力正しく識別できていれば自然とそうなりますが、時には注意が必要です。見落とされがちなことをいくつか紹介します。
#### 乱数ジェネレーターに気を付ける
次のゲームステートを計算するうえで、多くのゲームは乱数を使用します。もし乱数を使っていたら、それが両プレイヤーがフレーム0の時に乱数ジェネレーターのシードが同一であること、そしてあなたのゲームステート内に乱数ジェネレーターのステート含まれていること、といった完全に決定的であることを必ず確認してください。この両方を確認すれば、GGPOが特定のフレームロールバックが何回も必要になろうとも、そのフレームで生成された乱数は必ず同じになります。
次のゲームステートを計算するうえで、多くのゲームは乱数を使用します。もし乱数を使う場合、それらが完全に決定的であること、乱数ジェネレーターのシードが両プレイヤーの0フレーム目で同じであること、乱数ジェネレーターの状態がゲームステート含まれていることを確認してください。これらのことが行われていれば、特定のフレームに対して生成される乱数は、GGPOがのフレームロールバックする回数に関係なく、常に同じになります。
#### 外部の時刻情報(壁時計時間)に気を付ける
ゲームステートの計算に現在時刻を使う場合は注意してください。このことでゲームに影響を与える、または他のゲームステートに導く可能性があります(例: タイマーを乱数ジェネレーターのシードに使う)。2のコンピューターもしくはゲームコンソールの時刻は、ほぼ同期することはなく、ゲームステート計算に時刻を使用すると同期の問題につながります。ゲームステートに時刻の使用する、またはフレームに入力する一部として、プレイヤーの1人のために現在時刻を含める、そして計算に時刻を常に使うといったことは排除するべきです。
ゲームステートの計算に現在時刻を使う場合は注意してください。ゲームに影響を与えたり、別のゲームステートに導く可能性があります(例: 乱数ジェネレーターのシードにタイマーを使う)。2のコンピューターまたはゲームコンソールの時刻同期することはほとんどないため、ゲームステート計算に時刻を使用すると同期のトラブルに繋がります。ゲームステートに時刻を使うのを止めるか、プレイヤーの現在時刻をフレームへの入力の一部として含め、常にその時刻を使って計算を行う必要があります。
ゲームステート計算における外部の時刻情報の使用は構いません(例: 画面上のエフェクト時間計算、またはオーディオサンプルの減衰)。
ゲームステート以外の計算に外部の時刻情報を使う分には問題ありません(例: 画面上のエフェクト時間計算オーディオサンプルの減衰など)。
### ダングリングポインターに気を付ける
ゲームステート動的に割り当てられたメモリを含んでいる場合、データの保存復元をする際には、ポインター再配置する保存と復元関数に十分気を付けてください。緩和するひとつの方法として、ポインターの代わりに割り当てられたメモリを参照するため、ベースとオフセットを使用します。これ再配置が必要なポインターの数大幅に減できます。
ゲームステート動的に割り当てられたメモリが含まれる場合、データの保存復元の際に十分に気を付けながらポインター再配置を行ってください。これを緩和するひとつの方法、ポインターの代わりにベースとオフセットを使って割り当てられたメモリを参照することです。これにより再配置が必要なポインターの数大幅に減らすことができます。
### 静的変数、またはほかの隠れたステートに気を付ける
### 静的変数隠れたステートに気を付ける
あなたのゲームで使われている言語は、全てのステート追跡するのを難しくさせる機能があるかもしれません。C言語にある静的自動変数はこの動の一例にあたります。全ての箇所を探し、保存できる形式変換する必要があります。例として、この2つを比較してください。まずはこちらから
ゲームが記述されている言語は、全てのステート追跡を困難にさせる機能があるかもしれません。C言語静的自動変数はこの動の一例です。該当する全ての箇所を探し出し、保存可能な形式変換する必要があります。例えば、以下を見比べてください
```
// This will totally get you into trouble.
@@ -254,7 +254,7 @@ GGPOはロールバック中、事前フレームコールバックを何度も
}
```
にこちらを
のように書き換えます
```
// If you must, this is better
static int global_counter = 0; /* move counter to a global */
@@ -274,14 +274,14 @@ GGPOはロールバック中、事前フレームコールバックを何度も
}
```
### GGPO同期テスト機能をたくさん使用しましょう
### GGPO同期テスト機能をたくさん使ましょう
GGPOにあなたのアプリケーションを移植したら、漏れが出やすいゲームステートの可能性がある同期問題、この追跡ができる`ggpo_start_synctest`関数を使ことができます。
あなたのアプリケーションがGGPO上で動作するようになったら、`ggpo_start_synctest`関数を使ってゲームステートの漏れによる同期問題を追跡することができます。
この同期テストセッションは特別に作られもので、シミュレーション決定論におけるエラーを探すために設計されたシングルプレイヤーセッションです。同期テストセッション実行すると、GGPOはあなたのゲーム内でフレームごとに1フレームのロールバックを行います。初回に行った時のフレームステートとロールバック中に行ったステートを比較し、異なっていればエラーを発生させます。ゲーム実行中に`ggpo_log`関数を使用した場合、エラーを追跡するために初期フレームのログとロールバックフレームのログを比較することができます。
この同期テストセッションは、シミュレーション決定論におけるエラーを探すために設計された特別なシングルプレイヤーセッションです。同期テストセッション実行すると、GGPOは全てのフレームに対して1フレームのロールバックを行います。フレームが最初に実行されたときのステートとロールバック中に実行されたステートを比較し、それらが異なっていた場合はエラーを発生させます。ゲーム実行中に`ggpo_log`関数を使用すると、初回フレームのログとロールバックフレームのログを比較してエラーを追跡することができます。
ゲームコードを書いている時に開発システムで継続的に同期テストを実行することで、同期を発生するバグをすぐに見つけることができます。
ゲームコードを書いている時に開発システムで同期テストを継続的に実行することで、同期ズレの原因となったバグをすぐに見つけることができます。
## さらに詳しく知りたい方は
このドキュメントはGGPOの基本的な機能を紹介しています。さらに知りたい方は、`ggponet.h`ヘッダーにあるコメント、そしてコードを直接読むことをお勧めします。それではみなさん頑張ってください!
このドキュメントはGGPOの基本的な機能について紹介しました。さらに知りたい方は、`ggponet.h`ヘッダーにあるコメント、そしてコードを直接読むことをお勧めします。それではみなさん頑張ってください!