ゲームエンジン用にコルーチンを実装したが、結局使わなかった
自分のゲームエンジンのために、ジェネレーターによるコルーチン・システムを構築した。コードは122行、テストは12個。コールバック地獄の問題を解決し、スクリプトを直線的な記述にすることができた。
しかし、ゲームロジックにはそれを使わないことに決めた。代わりに有限状態マシン(FSM)を採用した。
その理由を以下に記す。
ほとんどのチュートリアルは、リアルタイムゲームにおけるジェネレーターの2つの大きな問題、すなわち「メモリ」と「セーブ」を無視している。
1. メモリへの負荷
ジェネレーターに対して .next() を呼び出すたびに、JavaScriptエンジンは新しいオブジェクトを作成する。このオブジェクトには、現在の値とステータスが含まれている。
60 FPSで動作するゲームループにおいて、これは絶え間ないガベージコレクション(GC)の負荷を生む。私のエンジンは、パフォーマンスを安定させるために「ゼロアロケーション(zero-allocation)」の原則に従っている。ジェネレーターはこの原則を破ってしまう。毎フレーム、小さなオブジェクトのストリームを生成してしまうからだ。
2. シリアライズの問題
ゲームにはセーブとロードが必要だ。現在のゲーム状態をデータに変換し、再び元の状態に戻す必要がある。
一時停止中のジェネレーターをシリアライズすることはできない。その状態は、アクセス不可能なエンジン内部のスロットに保持されているからだ。保存しようとすると、空のオブジェクトが返されるか、エラーが発生する。
有限状態マシン(FSM)は異なる。状態はコンポーネント上の単なる文字列に過ぎない。
- 保存する場合: その文字列をディスクに書き込む。
- ロードする場合: 文字列を読み込み、FSMにそこから開始するよう指示する。
FSMが選ばれた理由は、プロフェッショナルなゲームにおいて極めて重要な2つの要素、すなわち「パフォーマンス」と「永続性(persistence)」を扱えるからだ。
選び方:
メインのフレームループ内のロジックか?
- いいえ: ジェネレーター・コルーチンを使用する。アセットの読み込みやオンボーディングのような、一度限りのフローには最適である。
- はい: 次の質問へ。
状態をセーブ時に保持する必要があるか?
- はい: 有限状態マシン(FSM)を使用する。
- いいえ: 次の質問へ。
多くのシーケンスを同時に実行しているか?
- はい: 有限状態マシン(FSM)を使用する。
- いいえ: ジェネレーター・コルーチンを使用する。
ジェネレーターは間違いではない。ただ、ホットパス(hot path)には不適切なツールなのだ。非同期処理の接着剤(async glue)として使うのは良いが、ゲームロジックにはFSMを使うべきだ。
Source: https://dev.to/grzott/i-built-generator-coroutines-for-my-game-engine-then-didnt-use-them-o3g
