« 去年のenSPA読んでた(FX関連) | メイン | <警告!>まだあなたのゲームにリプレイ機能を実装してはいけません!なぜなら……(後編) »

[開発] <警告!>まだあなたのゲームにリプレイ機能を実装してはいけません!なぜなら……(前編)

この記事を読まずに、あなたのゲームにリプレイ機能を実装するのはあまりに危険です……。

なぜなら……。

gamedev01.jpg

さて、ちょっと前の話題ですが、
リプレイに関する話題が流行っていました。

そのことについてうちでの実装方法を書いてみようと思います。

うちの場合は、基本的に入力情報を全部記録して、
再生時に与えてやるという感じです。
いわゆるキー入力記録タイプ

面倒くさい気がしますが、入力情報を取得するAPIやライブラリにラッパーをかませてやれば簡単です。

大概、WireBattlerとか GuNMeNのときに悩みまくったので、
それ以降、このタイプの記録方式ではそんなに苦労していないですね。

自分のリプレイ実装の実績

  • WireBattler:2D格闘ゲーム
    リプレイの他に、もっとも連続技をつなげたところから再生する機能もあり
    固定フレームレート
  • GuNMeN:2D固定画面アクション
    インターネットランキング対応で、他人のリプレイをダウンロードして再生することもできる。
    固定フレームレート。
  • Gan Gan Gan:2Dシューティングゲーム
    今のところ体験版のみのリリース。
    ステージごとに再生が可能。
    可変フレームレート(但しベースは60FPS)

入力情報記録タイプで気をつけることをいくつか

実績を見てわかるように、この記事では、基本ベースフレームレートが固定のものに使えるリプレイのテクニック及び注意点を解説しています。

ぜひゲーム作りにお役立てください。

途中でずれたらオワリ\(^o^)/

この入力情報を記録するタイプでは、
最初から再生して、途中でずれたらそこで失敗になります。
一度ずれたら、その後ずーっとずれっぱなしになるためです。

なので、かなり厳密に作る必要があります。

キー入力の記録

まず、この方法の基本はAPIから取得したキー入力を全部記録しておくことです。

かなりメモリーを食うのでは?とか思った人います?
大丈夫です、安心してください。
まあ計算すればすぐにわかりますがたいした容量ではありません。

カーソル4方向 + 4ボタンなら 8bits=1byteです。
60FPSとしたら、30分記録するのに、

1 * 60 * 60 * 30 = 105 kbytes

2人プレイなら2倍、ボタンが2倍ならさらに2倍になるだけです。

全然大丈夫ですよね?

というか、10年以上前から オンメモリーで全部記録していましたが問題はなかったです。
もし精神的に嫌なら、zlibのストリームにでも突っ込んでいけばよいのではないでしょうか?
(WireBattlerのころは一応、自前でリアルタイムにランレングス圧縮をかけてました。今は素でオンメモリーストリームに突っ込んでいるだけです)

記録と再現の仕方ですが、ラッパーを作ってやるとよいです。
つまり、記録時に実際に入力したキー情報をラッパーを通した段階で記録し、再現時には実際のキーは無視してそのラッパーから記録したものを出力してやるだけです。

そうすれば、キー入力を処理する外側からは、
まったく同じように見える(=同じようにプログラムを組める)よね?

全部初期化すること

基本的にプレイ開始時に全部初期化することです。
初期化を忘れて、前のプレイの状態変数なんかが残っているとずれます。

一番簡単なのは、オブジェクトのインスタンス確保で、データがすべて初期化されるように最初から組んでしまうこと。

そうすれば、プレイが始まったらインスタンス確保、
プレイが終わったらインスタンス開放。

つまり、メモリも一旦全部開放してすっきりさわやかにしてしまうのです。

浮動小数点がらみ

これが実はあまり再現ができてなくてよくわかってないのですが、
浮動小数点の丸めモードの違いがあると、計算結果がずれるんです。

それがどうも他のアプリケーションの影響などで、突然変わることがあるらしいとか。
(不確定すぎてすいません)

で、かなーり昔から(10年くらい前から)なんですが、
浮動小数の算術関数に FINIT でコントロールワードのリセットを入れまくるっていう方法を使ってました。

例えばこんな感じです。

で、Sinは直接呼ばずにこれを通すと。

ただ、この現象については不確実なので責任が持てませんw

CPUにより演算結果が変わるライブラリがある

Direct3DのサポートライブラリのD3DXとか使うと、場合わけして勝手に最適化したルーチンが呼ばれるためにCPUによって演算結果が変わるようです。
(主に浮動小数点。またか)

けっこういろいろなゲームの作者さんが「リプレイがずれる」という報告を受けているようなのですが、ほとんどが、これか、前述の浮動小数の誤差だと思います

いろいろ解決策はあるようですが、
自分の場合は、自前実装で回避でした。

というか、Direct3Dまともに使ったことねえwww
(DirectDraw派→CPU描画派→OpenGL派(いまここ))

コラム:WireBattler の途中再生機能

ちなみに、対戦格闘ゲーム「WireBattler」の最も多くつなげた連続技を見られる機能、つまり、途中から再生する機能は、記録時にある地点(地面にダウンしたとき)にフラグを立てておいて、リプレイ時にそこまで描画を全部すっとばして早送りしているだけなのです。

なのでCPUが遅いとけっこう時間がかかってしまいます。

次回に続く

長くなりすぎたので続きは次回に!

後編書きました。

参考リンク

カテゴリ: [ 開発 ]

コメント (2)

Ko-Ta:

全部整数で!
って対策も3Dになると3次元コリジョン計算に使わざるを得ないので浮動小数誤差の問題は頭が痛い問題ですよね。

随分昔にDelphiだとDirect3D命令の前後にFInitを入れないとバグる事がありましたが、それもコレト関係があるのでしょうかね?

エクセルでも話題としてあるようですね。
http://pc.nikkeibp.co.jp/pc21/special/gosa/eg4.shtml

TOBY:

すいません。コメント承認が遅れました。(URLあると許可制にしてたの忘れてた)

「全部固定小数でやれば関係ない!!」そう思っていた時期が私にもありました(ry

エクセルの件のURL、わかりやすくて参考になりましたww
てか、エクセルでもあるんだなあ。当たり前かあ

コメントを投稿

プロフィール

あわせて読みたい

あわせて読みたいブログパーツ

人気エントリー

About

2008年07月13日 07:39に投稿されたエントリーのページです。

ひとつ前の投稿は「[投機] 去年のenSPA読んでた(FX関連)」です。

次の投稿は「[開発] <警告!>まだあなたのゲームにリプレイ機能を実装してはいけません!なぜなら……(後編)」です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。