ページ 11

リプレイデータの記録について

Posted: 2010年8月22日(日) 08:58
by シエル
お世話になります。

現在リプレイデータの記録&再生を頑張って実装しようとしているのですが、
どうしても途中でずれてしまいます。(もしかしたら初めからずれているのかもしれません)

そこで、昔リプレイの保存で議論していたスレがあったので、ちょっと目を通してみました。
下記のスレです。
http://www.play21.jp/board/formz.cgi?ac ... &rln=54489

ここで、キューさんがサンプルとして書いていただいているコードと私が自分で書いてるコードは
大体やってることは同じだったのですが、一つだけ違いがありました。
それは、キューさんの方のコードは最初にキーの状態を全て保存しておいて、リプレイ再生時にまずそれを
読み込んで当時の最初のキー状態を再現しているところです。
私はこれは必要ないと思ったのでやっていません。

理由としては、
まず0フレーム目の記録の際に、現在のキー状態と過去のキー状態を比べて差異があれば、
ファイルに書き込みを行っています。
この処理を行っていれば、最初のキー状態を再現できると思ったからです。
例えば0フレーム目にAキーが押されていたとします。
この時点では、過去キー用変数には0フレーム目なので何も値は入っていません。

なので最初の差異をチェックする部分で必ず、ファイルに書き込みが行われます。
これを後から再生すると、0フレーム目でAキーという情報が読み込まれ、
Aキーが押されたいたことになります。
ですので、最初にキー状態を全て保存しておく必要はないと思っています。

私とキューさんのコードの違いはこの部分だけです。
この違いが原因で私のリプレイ再生がずれていることに関係しているのかわかりませんが、
この最初にキーを全て保存しておくことが本当に必要なのかどうかをはっきりさせたいので、
必要であれば必要な理由をお答え願えないでしょうか?

それと、もう一つありまして、
このスレで議論されている差分フレームの話は非常に興味がありまして、どうせならこの理論を
取り入れたいと思っています。
しかし、まだC言語暦が3ヵ月半程度で経験不足な為、完全に理解することができませんでした。

特に理解できなかったのが、対象のキーのビットを立てる?ことです。
ビットとバイトの関係は理解しているつもりなのですが、次のキューさんの発言がいまいち分かりません。

========================================================================================
いえ、キーの入力状態を保存する必要はなく、「何番のキーか」を保存すればいいと思います。
上に書いたとおりですが例えば
No. ボタン
0 … 下キー
1 … 上キー
2 … 左キー
3 … 右キー
4 … ショットキー
5 … ボムキー
6 … 低速移動キー
7 … ストップキー
8 … ボス会話スキップキー
みたいに予め決めておきます。
そうすれば、16種類もキーがあれば十分でしょうから、2^4=16で4ビットあればいい事になります。
=========================================================================================
この記述を踏まえて改めて質問致しますと、
もし0~3番までの4ビットを使った場合、右キーを押した場合は変数にどのように
値を入れればいいのでしょうか?
unsigned short test=3
でいいのでしょうか?
もし良かった場合、11~14番の4ビットを使った場合はどうなるのでしょうか?
左に8ビットシフトするので、3×2^8をすればいいのでしょうか?
これが理解できれば、他の部分も全て理解できると思います。

以上、長文になってしまいましたが、分かる方ご教授願います。

Re:リプレイデータの記録について

Posted: 2010年8月22日(日) 08:59
by シエル
追記しますが、
キューさんが提示されているコードは二つあるうちの二つ目の方です。
一応貼っておきます。

#include "DxLib.h"

/*********************** リプレイデータ保存 ****************************/

//キーの入力状態の変化を計算して変化があると表示する
void SaveKeyChangeState( unsigned short FrameNum, FILE *fp ){
char nowKeyState[256]; //今現在の入力状態
static char oldKeyState[256]; //過去の入力状態

GetHitKeyStateAll( nowKeyState ); //入力状態を取得

if( FrameNum == 0 ){ //最初ならキーの入力状態全てを保存
fwrite( nowKeyState, 256, 1, fp );
}

for( int i=0; i<256; i++ ){
unsigned char KeyNum = (unsigned char)i;
if( nowKeyState != oldKeyState ){ //過去の入力状態と違えば
fwrite( &FrameNum, 2, 1, fp ); //フレーム数記録
fwrite( &KeyNum, 1, 1, fp ); //キー番号録
printfDx("Frame = %4d, KeyNo.=%d\n", FrameNum, KeyNum); //表示
}
}
memcpy( oldKeyState, nowKeyState, 256 ); //現在の入力状態を過去のものに
}

/*********************** リプレイデータ読み込み ****************************/

//変化のあったフレーム数とキー番号を読み込み
int ReadData( unsigned short *FrameRem, unsigned char *KeyNum, FILE *fp ){
if( !fread( FrameRem, 2, 1, fp ) ) return -1;
if( !fread( KeyNum, 1, 1, fp ) ) return -1;
return 0;
}

//リプレイデータからキーの状態を再現
int ReadKeyChangeState( unsigned short FrameNum, FILE *fp ){
static char KeyState[256]; //過去の入力状態
static unsigned short FrameRem; //変化のあったフレーム番号
static unsigned char KeyNum; //変化のあったキー番号

if( FrameNum == 0 ){ //最初なら
if( !fread( KeyState, 256, 1, fp ) ) //キーの入力状態をセット
return -1;
if( ReadData( &FrameRem, &KeyNum, fp ) ) //次にキーの状態が変わる情報を読み込み
return -1;
}

if( FrameRem > FrameNum ){ //キーの状態が変わるのがまだ先ならもう帰る
return 0;
}
if( FrameRem < FrameNum ){ //こんなことはあってはいけないのでエラー
return -1;
}

for( int i=0; i<256; i++ ){ //ここに来た時点で0-255のどれかが変わるはず
if( i == KeyNum ){ //変わるのがこれなら
KeyState = KeyState ? 0 : 1; //1なら0を、0なら1を代入
printfDx("Frame = %4d, KeyNo.=%d\n", FrameNum, KeyNum); //表示
if( ReadData( &FrameRem, &KeyNum, fp ) ) //次にキーの状態が変わる情報を読み込み
return -1;
if( FrameRem > FrameNum ){ //キーの状態が変わるのがまだ先ならもう帰る
return 0;
}
}
}
return 0;
}

/*********************** メイン ****************************/

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
int FrameCount=0, ReplayFlag=0;//リプレイフラグ
FILE *fp;
if(ChangeWindowMode(TRUE)||DxLib_Init()||SetDrawScreen(DX_SCREEN_BACK))return -1;

if( ReplayFlag == 0 ){ //リプレイ中じゃなければ書き込み
if( (fp=fopen("000.rpy","w")) == NULL )
goto LAB_EX;
} else { //リプレイ中なら読み込み
if( (fp=fopen("000.rpy","r")) == NULL )
goto LAB_EX;
}

while(!ScreenFlip()&&!ProcessMessage()&&!ClearDrawScreen()&&!CheckHitKey(KEY_INPUT_ESCAPE)){

if( ReplayFlag == 0 ){ //リプレイ中じゃないなら
SaveKeyChangeState( FrameCount, fp ); //キーの変化状態を保存
} else {
if( ReadKeyChangeState( FrameCount, fp ) ) //キーの変化状態を読み込み・適用
break;
}

DrawFormatString(500,0,GetColor(255,255,255),"Frame=%d",FrameCount);//フレーム数表示
FrameCount++;
}

fclose( fp );

LAB_EX:
DxLib_End();return 0;
}

Re:リプレイデータの記録について

Posted: 2010年8月22日(日) 12:40
by Justy
>この最初にキーを全て保存しておくことが本当に必要なのかどうか
 必要です。
 Dixqさんの方法は「変化があったときにビットが立つ」からです。
 どういうことかというと、ボタンが押されているときに1、押されてないときに0という
ビットが押下状態を表すものではなく、ボタンを押そうが離そうが変化があったその瞬間に
対応するビットが1になる方式だからです。

 つまり最初の段階で押下状態がどうだったかが判らないと、ビットが1になっていたとき
押されたのか離されたのか判別が付かないので、ボタンの初期状態を保存しておく必要があります。

 同じ変化があったときのみ記録する方式でも逆にこれがボタンが押されている時に1、
押されてないときに0の方法だった場合、ビットを見れば押下状態が判るのでボタンの初期状態は
保存しておく必要はありません。



>もし0~3番までの4ビットを使った場合、右キーを押した場合は変数にどのように
>値を入れればいいのでしょうか?
 右キーは3になっていますので、計算は 1 << 3となり、値は 8です。
 ついでに低速移動キーも一緒に押されたら (1 << 3) | (1 << 6)で 0x48となります。


>11~14番の4ビットを使った場合はどうなるのでしょうか?
 んーと、これは下~右の本来 0から 3ビットが割り振られているのを 11~14ビットとした
場合、ということですか?

 であれば右ボタンが 13ビット目に対応するので、 1 << 13で 0x2000となります。


 参考までにリプレイにおけるビットの話とかがあるこちらのスレにも目を通すといいかも。

C言語何でも質問掲示板
http://www.play21.jp/board/formz.cgi?ac ... &rln=22441

C言語何でも質問掲示板
http://www.play21.jp/board/formz.cgi?ac ... &rln=33526

Re:リプレイデータの記録について

Posted: 2010年8月22日(日) 13:03
by シエル
Justyさん!お忙しいところありがとうございます!

過去スレの内容を見てみたところ
私も過去スレのサウスさん同様、あまりにも分かりやすくて凄い回答だったので
驚いたというか感動しました!

ちょっと一回作ってやってみます!
本当にありがとうございます!

Re:リプレイデータの記録について

Posted: 2010年8月22日(日) 15:59
by シエル
うおおお!出来ました!動作もずれなく完璧に動くようになりました!

しかもサイズも今までの半分!(約3分で4KB)

自分で何回やっても少しずれてしまうから、毎回少数の計算で誤差が出てるのかと
思ったら、やっぱり自分のリプレイのコードがまずかったようですね。
バイトとビットの関係、各ビットの立て方や倒し方、立ってるかどうかの調べる方なども
理解しました。

余裕があればフレーム数保存用の変数も差分フレームの理論を使ってみて、
さらにサイズが減らせるか頑張ってみます!

それでは次は3D背景に挑戦したいと思います!

Justyさん!本当にいつもありがとうございます!