ゲームのリプレイデータをURLへ埋め込む

URLには2000文字を詰め込むことができるので、頑張ればここにいろんなデータを埋め込むことができる。例えば、ゲームのリプレイデータ。この前作った例。

URLにリプレイデータが埋め込められればそのURLをTwitterとかで共有することができる。サーバレスで。いやTwitterのサーバには頼っているんだけど、自前のサーバはいらない。

でもまあたかだか2000文字分の情報量しか入らないので、入れるリプレイをどういう物にするのかは考えないといけない。

  1. ゲームオーバー直前の数秒だけをリプレイ
  2. プレイヤーからの入力を限定してデータ量を削減しつつゲーム時間を短く

1.はゲームのタイプに限らずリプレイができるけどリプレイ時間は短くなるし、作る側から見て非常に面倒なことにゲーム各フレームのスナップショットを記録しなければならない。ゲームを最初からリプレイするならゲーム初期状態としてランダムシードくらいを記録しておけばいいんだけど、1.みたいにゲーム中のどのタイミングからリプレイが始まるか分からない場合、毎フレームのスナップショットとして画面上のオブジェクト状態を正確に保存する必要がある。しかも今回はそのスナップショットも小さく最低限のデータ量にシリアライズしないといけない。何か記録漏れしたらすぐにリプレイは壊れる。とても面倒。

2.は初期状態記録+プレイヤー入力を全部記録だけで済むので開発側から見ると楽。でもゲーム中のプレイヤー全入力を2000文字に入れ込まなければいけないので、ゲームのタイプはだいぶ限られる。上記の例はワンボタンゲームで1分以内にまずプレイヤーがやられるという前提だから成り立っていた。

上記条件を満たしていれば、ゲームの初期状態やプレイヤーからの入力イベント、毎フレームのスナップショットを記録し、それをlz-stringとか使ってURL文字列にすればOk。実装の一例

リプレイ対応ゲーム作成の注意点としては以下がある。

  • JavaScriptのMath.randomはシードが与えられないのでシードが与えられる乱数を使う。Xorshiftとか
  • Array.sortを使わない。この実装が同じ比較値を持つ要素の順序が変わらない安定ソートかどうかはブラウザに依存するのでブラウザ間のリプレイデータの整合性が取れなくなる。lodash.sortByなどで代替すること

まあでもリプレイの実装はいろいろと面倒なのでサーバ使えるならgifなりムービーなりで記録してサーバに叩き送るのが昨今だと正解じゃないですかね。