リプレイデータでキー入力の変化を記録する方法

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
レイ

リプレイデータでキー入力の変化を記録する方法

#1

投稿記事 by レイ » 14年前

はじめまして、レイと申します。

龍神録でリプレイを保存して再生するのはできているのですが、
サイズがとても大きくて困っております。

圧縮しようと思ったのですが、zlibとかランレングス圧縮のプログラムは
自分にとって難解だと感じたため、

過去ログの方法の

「全てのキーの入力状態を記憶せず、キーの入力に変化があったフレーム番号とその変化を記憶させれば、リプレイデータが軽量化出来る」

という方法を行ってみたいと思ったのですが、まず何を行えばいいのか分からないという現状です。

どんな感じの手順を踏めばいいのかだけでも
教えて頂けないでしょうか。

未熟者ですみません。
どうかよろしくお願いします。

Dixq (管理人)

Re:リプレイデータでキー入力の変化を記録する方法

#2

投稿記事 by Dixq (管理人) » 14年前

特に難しくはないと思います。
書いてある通りですが、キー入力の状態が変わった時に、どのボタンが何フレーム目で変わったのかを記録すればいいと思います。

まず、ゲームが始まった瞬間のキーの入力状態を記録しておきます。
そして、リプレイの瞬間それを再現します。

後は「キーの入力状態が変わった」情報を元に変更して行けばいいのです。
例えば、Zキーが押されているかどうか、ではなく、
最初のキーの入力状態がわかれば、「○○フレーム目でXXキー」という情報さえあれば再現出来るのです。

例えばですが、

No. ボタン
0 … 下キー
1 … 上キー
2 … 左キー
3 … 右キー
4 … ショットキー
5 … ボムキー
6 … 低速移動キー
7 … ストップキー
8 … ボス会話スキップキー

とか事前に決めておいて、キーの入力状態に変化があった時、

・何フレーム目か(unsigned shot int(2バイト))
・どのボタンか(unsigned char(1バイト))

を記録すれば良いです。
キーの入力状態は必要ありません。
データがあるということは、この時入力状態が変わった事がわかるのですから。

とりあえず全部一気に作ろうとすると難しいのでまずは

「キーの入力状態が変わった時、そのキー番号とフレーム数を記録するプログラム」

を作ってみてはどうでしょう。
読み込み、再現はそれが出来てからということで。

スキマ妖怪

Re:リプレイデータでキー入力の変化を記録する方法

#3

投稿記事 by スキマ妖怪 » 14年前

圧縮とかの前に記録する情報量を減らすことを考えてみてはいかがですか?
もしかして、全ての変数をintとかでファイルに書き込んでないですか。


以下的外れかもしれませんが例えば、

キー入力の場合、上下左右+4ボタンしか使わないのなら1バイト分ですみます。(各キーにbitを割り当てる)
フレーム情報の場合も、2バイト分など必要な分だけ書き込めば、その分容量を減らせます。
1Byte(キー) + 3Byte(フレーム) = 4Byteの方がキリがよさそうですかね。


その上で、

>「全てのキーの入力状態を記憶せず、キーの入力に変化があったフレーム番号とその変化を記憶させれば、
> リプレイデータが軽量化出来る」

をすればさらに減らせるんじゃないですか。(試してないのでどの程度かは分かりませんけど…;)
手順については、…書いてある通りなんですけどね。

ゲームプレイ時は、
(前のフレームと比べて)キーの変化がある毎にそのときのフレーム(ゲーム内時間)を記録
これで変化があったモノだけになるはず。

リプレイ時は、
読み込んだリプレイデータを元に記録されたフレームがきたら記録された値にキーを変更
これでキー入力に関しては、再現できるはず。

という感じですね。


…ところで、サイズが大きいって具体的にどのくらいですか?
100KByte程度だったら許容範囲だと思いますよ(^^




投稿迷ってたらDixqさんが…
まぁ、せっかくなんで書き込みます(かぶっちゃってますけどorz)

Dixq (管理人)

Re:リプレイデータでキー入力の変化を記録する方法

#4

投稿記事 by Dixq (管理人) » 14年前

なんか被ったようですみません^^;

[color=#FF0000>[修正:23:06]
(すみません、秒に分を掛けていませんでした・・。
1時間のフレーム数は2バイトでは計算しきれませんので、普通のintが必要ですね。
以下2バイトの話は読み飛ばして下さい・・)[/color]

おさらいすると、、

  ・何番のボタンか(1バイト)
  ・何フレーム目か(2バイト)

さえあれば良いということですね。

上の通りならば3バイトなのですが、むちゃをして詰め込もうとすれば、2バイトでもいけるかもしれませんね。

  ・リプレイは2^12フレーム(1時間ちょっと)までしか保存しない。
  ・ボタンは16種類しか使用しない

とすれば、
例えば16ビットの内、上位4ビットはボタンの種類を表し、下位12ビットはフレーム数を表すことで、
容量が2/3になると思います。

まぁここまでして軽量化したければですが^^;

※ボタンが8種類でいいのなら、2.2時間位記録できます。


ちょっとサンプル作ってみました。
実行中に適当にキーボードを押してみて下さい。
変化のあったキー番号とフレーム数が表示されます。
重ねて言いますが、押したか離したかは記録する必要ありませんので、
変化のあったキー番号とフレーム数のみ表示しています。
#include "DxLib.h"

//キーの入力状態の変化を計算して変化があると表示する
void CalcKeyState( int FrameNum ){
            char nowKeyState[256];    //今現在の入力状態
    static    char oldKeyState[256];    //過去の入力状態
    GetHitKeyStateAll( nowKeyState );    //入力状態を取得

    for( int i=0; i<256; i++ ){
        if( nowKeyState != oldKeyState ){    //過去の入力状態と違えば
            printfDx("Frame = %4d, KeyNo.=%d\n", FrameNum, i);    //表示
        }
    }
    memcpy( oldKeyState, nowKeyState, 256 );    //現在の入力状態を過去のものに
}
 
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
    int FrameCount=0;
    if(ChangeWindowMode(TRUE)||DxLib_Init()||SetDrawScreen(DX_SCREEN_BACK))return -1;
    while(!ScreenFlip()&&!ProcessMessage()&&!ClearDrawScreen()&&!CheckHitKey(KEY_INPUT_ESCAPE)){

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

シエル

Re:リプレイデータでキー入力の変化を記録する方法

#5

投稿記事 by シエル » 14年前

ゲーム等のリプレイデータって、動画形式でそのまま保存してると思ってました…
キー情報を保存して、後からその情報を読み込んで動かせば、
確かにリプレイになりますね。
勉強になりました。ド素人のつぶやきですので、スルーして下さい。

Dixq (管理人)

Re:リプレイデータでキー入力の変化を記録する方法

#6

投稿記事 by Dixq (管理人) » 14年前

私も最初はどうやって保存すればいいのかな~と思っていました。
動画なんてキャプチャしようものなら、圧縮しないと何十GBなんてことになってしまいますからね;

キーの入力状態を再現すればいいっていう方法は
一般的な方法かどうかはわかりませんので、参考程度にお聞きください。

ただ、一般的にもきっとこんな方法でプレイを再現しているのだとは思います。

Dixq (管理人)

Re:リプレイデータでキー入力の変化を記録する方法

#7

投稿記事 by Dixq (管理人) » 14年前

なんかどれ位圧縮できるのか興味あったので、作ってしまいました。
エラー処理云々は改善しないといけないでしょうけど、試作ということで。
本当にゲームに組み込む時はstatic変数とかを綺麗にやり変えて下さい。

デフォルトのままだとプレイ状態です。キーの入力状態を随時記録していきます。
最初に動作させる時はこの状態でまずデータを作って下さい。

作ったら、

メイン関数の最初にある
ReplayFlag=0;//リプレイフラグ



ReplayFlag=1;//リプレイフラグ

にして下さい。プレイを再現する処理に変わります。
1にすると、ファイルからキーの変化のあった情報を読み込み、リアルタイムでキーの入力状態を再現します。
サンプルということで、何か参考になればどうぞ。

#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;
}
 

Dixq (管理人)

Re:リプレイデータでキー入力の変化を記録する方法

#8

投稿記事 by Dixq (管理人) » 14年前

連投になりますが・・。

ちょっと気になったので、龍神録の1面でリプレイデータ取ってみました。

1.4KBでした。
先ほどの理論で圧縮すれば900バイトになりますね。

プレイ時間が4分でしたので、普通なら60フレーム×240秒で14400フレーム。

今まで単純に、龍神録の館で紹介しているキーの状態

typedef struct{
int left,up,right,down,shot,bom,slow,start,change;
}configpad_t;

をそのまま記録していたとすると

14400フレーム*4バイト*9個=514[KB]

ですから、[color=#FF0000>1/500位に圧縮できたことになりますね。[/color]


[color=#FF0000>[修正:23:12]
1時間は60*60*60フレームで、unsigned short intで扱える数を超えるので、unsigned intが必要ですね。
詰め込んだとしてもキー番号と合わせて4バイト必要ですから、圧縮率は1/250ですね。[/color]

スキマ妖怪

Re:リプレイデータでキー入力の変化を記録する方法

#9

投稿記事 by スキマ妖怪 » 14年前

時間記録用の変数が2バイトって微妙じゃないですか?
サンプルだからいいんですが、

2バイトだと65535

65535 / 60(秒) / 60(フレーム) = 約18分

東方風のSTGだとして(竜神録等)

6ステージだとすると

18分 / 6ステージ = 3分
道中1分、各弾幕平均1分として(クリア時間でなくタイムリミット)


道中、ボス戦込みで1ステージあたり3分しか使えないことになります。
遊ぶ側は、最短・平均クリア時間を目指すと思いますが(稼ぎプレイとかはとりあえず省いて)
作る側は、最長クリア時間を考慮しないとまずいと思います。(3、4バイト分くらい必要だと思います。)


実際はこうでないかもしれませんが、結構シビアだと思います。




…と思ったんですが記録方法を工夫すればいけますかねえ。
(例えば、直前の時間との差分とか)


> ですから、1/500位に圧縮できたことになりますね。これはやらなきゃですねw

それでも2バイト計算で1/500ってすごいですねw(すっごいコンパクトw)



以下追記--------------------
またカブッテシマッタ∑( ̄Д ̄;)
(ハズカシイ…)

> (例えば、直前の時間との差分とか)

ものすごいケチって1バイトで記録とかw

画像

Dixq (管理人)

Re:リプレイデータでキー入力の変化を記録する方法

#10

投稿記事 by Dixq (管理人) » 14年前

えぇ、、、秒に分を掛けるの忘れていましたよorz
記事を修正したのですが、私が修正すると修正マークが付かないんですよね・・。
赤字で修正してあるので、よければもう一度ご覧ください;

前回変化があったフレーム番号からの差分って考えとてもいいですね。
完全なシステムを作りたいならそれはちょっと危ない設計かもしれませんが仰る方法は良い発想だと思うので、
なんとか2バイトに抑えてみたいものですw

如何にデータを小さくするか・・・。
なんかテーマ的に燃えますねw

Dixq (管理人)

Re:リプレイデータでキー入力の変化を記録する方法

#11

投稿記事 by Dixq (管理人) » 14年前

最上位ビットは桁が溢れたかどうかを表すフラグにしてみてはどうでしょう。
溢れていたらもう1バイトフレーム数を表す領域だと判定して1バイト余分に読みこむのです。

2^12フレームとは4096フレーム。
その最上位ビットを使うので、

2*11=2048フレーム
これは30秒に相当しますね。
30秒間何のキー入力も無い事などほとんどないと思います。

もしあれば最上位ビットを1にして、もう1バイト余分に領域を確保し、フレーム数の保存に使います。
こうすれば「普段2バイト、時々必要なら3バイト」みたいな事が可能にならないでしょうか?

図にしてみました。

画像

恐らく30秒以上何もキーを押さないなんてことはないでしょうから、ほとんど2バイトで計算出来るのではないかと思いまがどうでしょうね?


追記:
東方には
「音楽」「弾幕」「システム」「計算速度」「美しさ」「魅力」・・・∞
何一つ勝てないので、せめて「リプレイデータのサイズ」位は勝てないかと躍起になっています(笑

スキマ妖怪

Re:リプレイデータでキー入力の変化を記録する方法

#12

投稿記事 by スキマ妖怪 » 14年前

自分なら節約もいいですが、(製作者として)使いやすい構造にしますね。
どうせサムネ画像とか、記録日時とか、飾りが入ると思うので(となると努力が…)


#pragma pack(push,1) // ←入れた方がいいんですかね?(すごい適当)
typedef struct {
int keys : 8;
int frame : 24;
} SAV_DAT;
#pragma pack(pop)

とでもしておいて、出し入れのしやすさをとっちゃうんですけどね。
(バグになりそうな要素を入れたくない、シンプルにしたい)


> 30秒間何のキー入力も無い事などほとんどないと思います。

事実上、成立すると思いますよ。
(自機狙いなどの)弾幕の中で無傷(に近い状態)はゲームとしてアレですからね。


> もしあれば最上位ビットを1にして、もう1バイト余分に領域を確保し、フレーム数の保存に使います。

フラグを使って、切り替えをする仕組み。
面白そうですね。ただ、バグらせる自信がありますw(そして飽きるorz)


ん?
図おかしくないですか

キー番号用のビット最低5ビットは必要じゃないですか?(上下左右+ショット、これ以上はゲーム仕様による)
だとするとデフォで15秒…
可変式なんで関係ないですけどねw


すみません。上レスで2バイトといっているのは、フレーム時間のみのことで、
実際は2(フレーム)+1(キー)=3バイトです。
紛らわしくてすいません。


> 東方には
> 「音楽」「弾幕」「システム」「計算速度」「美しさ」「魅力」・・・∞
> 何一つ勝てないので、せめて「リプレイデータのサイズ」位は勝てないかと躍起になっています(笑

そんなことないですよ。
細部に作りこまれていて面白かったです。(ここに来たのも竜神録経由だったと思うし。いいサイトです(^^


では、対抗してシリーズ展k(ry

Dixq (管理人)

Re:リプレイデータでキー入力の変化を記録する方法

#13

投稿記事 by Dixq (管理人) » 14年前

今のリプレイってサムネ画像とか入ってるんですか?・・・知りませんでした。

>キー番号用のビット最低5ビットは必要じゃないですか?

いえ、キーの入力状態を保存する必要はなく、「何番のキーか」を保存すればいいと思います。

上に書いたとおりですが例えば

No. ボタン
0 … 下キー
1 … 上キー
2 … 左キー
3 … 右キー
4 … ショットキー
5 … ボムキー
6 … 低速移動キー
7 … ストップキー
8 … ボス会話スキップキー

みたいに予め決めておきます。
そうすれば、16種類もキーがあれば十分でしょうから、2^4=16で4ビットあればいい事になります。

後は差分フレームというわけです。


これらのデータの他のリプレイデータの最初に入れるものとしては

・名前(16)
・乱数の初期値(4)
・日付(年・月・日・時・分=10)
・プレイキャラ(1)
・ステージ(1)
・処理落ち率など?(4)

位ですかね。まぁ100あれば何でも保存できそうな気がします。
画像とかがあれば別ですが・・。


所で、この圧縮が東方と比較してどの程度な物か調べてみました。

常に左右に移動し、ショットを90秒撃ち続けるというプレイを東方風神録1面で行うとリプレイデータは4.7[KB]でした。
上の理論を龍神録に実装して、同じ条件でショットを90秒撃ち続けると、リプレイデータは0.7[KB]でした。ヘッダ情報を0.1[KB]追加したとしても0.8[KB]。

しかし東方のリプレイデータに実質的なリプレイデータ以外に何がくっついているのか解らないので何とも言えません・・。
何か暗号化的な事がしてあるとか改竄検出出来るようにしてあるとか何かあるかもしれませんね。

短時間のリプレイと長時間のリプレイで差分を取ればヘッダにどれ位の容量があるのか推測できますが、
長時間龍神録と風神録で全く同じプレイをするってことが出来ないので難しいですね・・。


>細部に作りこまれていて面白かったです。

ありがとうございます^^
いや~龍神録をやって東方をやると公開をやめたくなるような気持ちに今でもなります^^;
時間があればまた作りたいんですが・・・。
2を作りかけたものの、社会人になると作ってる時間はあまりないですねぇ;
こうしている間にもZUNさんは毎年着々と新作を出して離れていく(最初から近づいてないけど)
どういう時間の使い方をしていらっしゃるのでしょう^^;

スキマ妖怪

Re:リプレイデータでキー入力の変化を記録する方法

#14

投稿記事 by スキマ妖怪 » 14年前

> 今のリプレイってサムネ画像とか入ってるんですか?・・・知りませんでした。

いえ、本家には入っていません。(本当、分かりづらくてすいません)
自分が作るなら…と深く考えずに書いたんですがよく考えたらいらないですね。
(使う方向で考えるなら、ボムやハイスコア弾幕等の映える場面を撮影…面倒ですね)


> 短時間のリプレイと長時間のリプレイで差分を取ればヘッダにどれ位の容量があるのか推測できますが、
> 長時間龍神録と風神録で全く同じプレイをするってことが出来ないので難しいですね・・。

処理落ちを考慮した厳密な記録をしているのでは?

Justy

Re:リプレイデータでキー入力の変化を記録する方法

#15

投稿記事 by Justy » 14年前

>こうすれば「普段2バイト、時々必要なら3バイト」みたいな事が可能にならないでしょうか
 可能にはなりますけど、整合性の検証とかデバッグとか面倒になりそうですし、
理論的には3バイトを越えたことも考えて3バイト目の最終フラグを桁あふれフラグにして
4バイト目も見にいくようにしないとだめかと。

 シンプルに考えるなら、キー番号 0か何かを無効扱い(何も押されていない)にして
前とのフレーム差が 2^X-1を超えそうになったら無効扱いで書き込んでしまえばいいかと。

Dixq (管理人)

Re:リプレイデータでキー入力の変化を記録する方法

#16

投稿記事 by Dixq (管理人) » 14年前

>処理落ちを考慮した厳密な記録をしているのでは?

そうえいば「処理落ちを再現してリプレイ」みたいな機能もありましたね・・。
FPS制御ツールでいかさましたリプレイでも解ってしまう仕様・・確かに必要ですね。

後は「ボスまで高速再生」とか。
単なるキーの入力状態以外にも色々付いてそうですね~。
やっぱリプレイデータすら勝てないかも・・。
まぁ神様と勝負しようとするのがそもそもの間ちg(ry

======

今回の件せっかくなので、ゲームプログラミングの館にソース載せたいなと思うのですが、
スキマ妖怪さんに頂いた「差分を保存」理論紹介させて頂いても良いでしょうか?


後、上の5bit無いといけないのでは?の件はどうでした?
何か私に勘違いがあれば教えて頂けると幸いです。

Dixq (管理人)

Re:リプレイデータでキー入力の変化を記録する方法

#17

投稿記事 by Dixq (管理人) » 14年前

> Justyさん

確かに理論上可能な圧縮をすればいいというものではないですね。
メンテナンスやデバッグのしやすさを考慮した圧縮も必要ですね。

む~どの辺で妥協すべきか。
東方もその辺良い辺りで手を打ってあるのですかねぇ・・。


> 4バイト目も確かめる必要性

確かに。今のままでは145分放置すると3バイト目も溢れますね。
まぁ2時間半近くリプレイ取る人はいないだろうと言う事で、2時間しか記録できないように決め打ちしてもいいかもしれませんが
拡張性や汎用性を考えると4バイト目を見に行くようにするべきですね。

スキマ妖怪

Re:リプレイデータでキー入力の変化を記録する方法

#18

投稿記事 by スキマ妖怪 » 14年前

> 今回の件せっかくなので、ゲームプログラミングの館にソース載せたいなと思うのですが、
> スキマ妖怪さんに頂いた「差分を保存」理論紹介させて頂いても良いでしょうか?

書いてる途中でぱっと思いついただけのものなんでどうぞ自由にしちゃってくださいw


> 後、上の5bit無いといけないのでは?の件はどうでした?

書き忘れていましたw
アレは、同時押しを考慮したら必要かなと思ったので。(可能性は0じゃないので。ずれますよね)


慣れてないので、削除キーをよく忘れます(ついでに打つのが遅い)

Justy

Re:リプレイデータでキー入力の変化を記録する方法

#19

投稿記事 by Justy » 14年前

>リプレイデータの最初に入れるものとしては
 データの破損チェック用のハッシュはどこかに入れておいた方がいいかも。



 ところで会話シーンにおけるリプレイってどうするんでしょ?
 会話のところも忠実にリプレイするのでしょうか?

Dixq (管理人)

Re:リプレイデータでキー入力の変化を記録する方法

#20

投稿記事 by Dixq (管理人) » 14年前

>> スキマ妖怪さん
>アレは、同時押しを考慮したら必要かなと思ったので。

一応私の上のプログラムは同時押しも考慮してあります。
同時に押しても、キーの番号順にループするので、同じフレーム内で同時押しが起こっても大丈夫なはずです。
再現する時もキーの番号順にチェックしますから。

>書いてる途中でぱっと思いついただけのものなんでどうぞ自由にしちゃってくださいw

ありがとうございます。
このご恩は必ず・・・・!・・・・何で返そう?;


>> Justyさん
>データの破損チェック用のハッシュはどこかに入れておいた方がいいかも。

なるほど、上の理論で実装すると、実装が間違うと
フラグミスで読み取るバイトがずれたりする危険性もありますしね・・。

>会話のところも忠実にリプレイするのでしょうか?

必要は無いのでしょうが、場合分けせずに全部同じ要領でリプレイした方が簡単そうかなとは思います^^;

Justy

Re:リプレイデータでキー入力の変化を記録する方法

#21

投稿記事 by Justy » 14年前

>実装が間違うとフラグミスで読み取るバイトがずれたりする危険性もありますしね・
 そうですね。
 データを書き込んだ後、読み出されるまでの間に壊れてしまう可能性は少なからずありますから、
一応読み出した段階で何らかのチェックは必要になるかと。
 このあたりはセーブデータと同じですね。


>場合分けせずに全部同じ要領でリプレイ
 なるほど。
 だとすると、会話シーンで2、3時間以上放置される可能性もあり、それも忠実に再現すると
そのままでは3バイトでは足らなくなることもあるわけですね。

スキマ妖怪

Re:リプレイデータでキー入力の変化を記録する方法

#22

投稿記事 by スキマ妖怪 » 14年前

> データの破損チェック用のハッシュはどこかに入れておいた方がいいかも。
こういうのは、分かりませんorz(使ってこなかった…いや、避けてきたのかな)


> 一応私の上のプログラムは同時押しも考慮してあります。
> 同時に押しても、キーの番号順にループするので、同じフレーム内で同時押しが起こっても大丈夫なはずです。
> 再現する時もキーの番号順にチェックしますから。

という事は、

(キー+差分0)+(キー+差分フレーム数)+追加分
=2バイト+2バイト+追加分

て事ですよね。(違ったらすいません;)
これどのくらいの頻度で出るんでしょうねえ。(多いと努力の甲斐が…)
多くはないと思いますが…


> このご恩は必ず・・・・!・・・・何で返そう?;

ソンナオンナンテシッピツチュウノホンガホシイナンテ
冗談ですw真に受けないでくださいね。
ついでにいつもなら忘れてそのままになるので、役立つのならそれだけで十分です。


>>会話のところも忠実にリプレイするのでしょうか?
>必要は無いのでしょうが、場合分けせずに全部同じ要領でリプレイした方が簡単そうかなとは思います^^;

東方だと、分けてなかったと思います。(うろ覚えですが)

Dixq (管理人)

Re:リプレイデータでキー入力の変化を記録する方法

#23

投稿記事 by Dixq (管理人) » 14年前

>> Justyさん

> なるほど。だとすると、会話シーンで2、3時間以上放置される可能性もあり、それも忠実に再現すると
> そのままでは3バイトでは足らなくなることもあるわけですね。

そういう事になりますが、リプレイデータの軽量化は、友達同士で交換する、
もしくは自分の自慢のプレイを見て欲しくてHPなどにアップする事を容易にする為に必要であり、
自分のPCにおいておくだけなら別に容量が1.5倍程度になる事は十分許容範囲だと思います。

そして、会話で2~3時間放置するようなリプレイデータを友達同士で見せ合うだろうかと考えれば否なので、
こんなリプレイデータにおいて少々データサイズが増えることは考慮しなくても大丈夫かなと思います。

スキップが必要なら、リプレイ再生中に押せる「高速処理ボタン(描画スキップボタン)」を用意すればいいと思いますし。


>> スキマ妖怪さん
> (キー+差分0)+(キー+差分フレーム数)+追加分

う~んと、仰っている意味が良く解らないです、ごめんなさい;
データは、キー番号と差分フレーム数が連続して記録されているわけですから例えば

2,30,(キー番号:2、差分フレーム:30)
1,40,
3,30,

みたいに入ってるわけですよね、2,3,4キーの同時押しだと

2,0,
3,0,
4,0,

のようになるので、バイト数は2バイトから増えないと考えています。

> 本

なるほど、たかぎさんがされたようなイベント検討させて頂きます^^;

スキマ妖怪

Re:リプレイデータでキー入力の変化を記録する方法

#24

投稿記事 by スキマ妖怪 » 14年前

> (キー+差分0)+(キー+差分フレーム数)+追加分

すいません;
明らかに説明足らないですね…(我ながらヒドイ;)

自分はよく、書いたつもり、言ったつもりで肝心な所が抜けたりしますorz



> 2,3,4キーの同時押しだと
> 2,0,
> 3,0,
> 4,0,
> のようになるので、バイト数は2バイトから増えないと考えています。

仮にこういうのが頻繁に発生するなら、
1バイトで上下左右+各種ボタンにする方が(つまり最初のヤツにもどr)
トータルで容量が少なくて済むんじゃないかと
チラッと思ったんですがそんなこと微塵も書いてなかったですねorz



>> 本
>なるほど、たかぎさんがされたようなイベント検討させて頂きます^^;

書いてみたかっただけですのでw
自分のことは気になさらず。

レイ

Re:リプレイデータでキー入力の変化を記録する方法

#25

投稿記事 by レイ » 14年前

うおぉ…私のいない間にすごいレス数…
みなさん、こんなに真剣にレスして下さって頂いて本当に感謝いたします…

>全ての変数をint
自機情報や、ステージ情報などはintで保存しています。
キー情報はunsigned charで保存しています。
重くなっている原因はたぶん次のことが原因だと思います。

東方だと「パッドのあそび」というゲームパッドのアナログスティックの感度を
敏感にしたり鈍感にしたりする項目がありますよね。
私もコンフィグでそれを行っており、DXライブラリの関数で
GetJoypadAnalogInput( int *XBuf , int *YBuf , int InputType) ;
を使って、パッドのスティックの敏感度を再現しています。
しかし、そのままだと-1000~1000の範囲なので
これを10で割って-100~100の範囲にして、
signed charで1フレームずつ、リプレイのキー情報と同じようにして保存しています。
これがあるためサイズが大きいのです。


そこで質問なのですが

管理人さんの描かれた有難い図の
「桁あふれフラグ」は、みなさんが仰っているリプレイの放置時間が
2ビットで納まらなかったら3ビットにするためのフラグでしょうか。
もしくは、2ビットから溢れた場合にフラグを立てるためのものでしょうか。



管理人さんのソースについてなんですが、
龍神録だとパッドとキーボード両方を監視しているので
それを試みようと思いましたが、メモリエラーを起こしました。

まずパッド版を作成しようと考えたのが
添付したソースです。

両方監視はパッド版ができてからにしようと思いまして…。
パッドのみなのでキーボード部分をコメントアウトしています。

最後にもう一つです。すみません…。

2,30,(キー番号:2、差分フレーム:30)
という方法で保存していくということですが
同時押しすると

2,30,(キー番号:2、差分フレーム:30)
3,30,(キー番号:3、差分フレーム:30)
4,30,(キー番号:4、差分フレーム:30)

この3つが一気に保存されるということで合ってますでしょうか?
そして、再生中はこれらが一気に30フレーム中で再現される。
という感じで解釈しています。
もし間違っていたらご指摘頂けるとありがたいです。 画像

Dixq (管理人)

Re:リプレイデータでキー入力の変化を記録する方法

#26

投稿記事 by Dixq (管理人) » 14年前

なんか読むのもダルくなるほど書き込んじゃいましたね、ごめんなさい;

====

ビットとバイトという言葉がごっちゃになってしまっているので、ここで一度整理してみましょう。

情報量の最小単位はビットと言います。
これは0か1かという2種類の信号しか識別出来ない最小単位です。
これが一つで1ビットです。
二つで2ビットになると、

00
01
10
11

の4種類の信号が識別出来るようになります。
3ビットなら8、8ビットなら256です。
全ての情報を1ビット単位で計算すると無駄があるので、8ビットずつ計算しましょうという決まりがあり、
この単位が1バイトです。

つまり1バイトは8ビットであり、256種類の信号が識別出来ます。
私が描いた図は00~15までビットがありますが、これはunsigned short int(2バイト)で扱う16ビットを示しています。
65536種類の信号が識別出来ます。

この最上位ビットをフラグに、その下の4ビットをキー番号に、残りのビットを差分フレーム番号に使おうというわけです。
しかし、ビット単位でデータを詰め込むような事はややこしく、バグの元なので、よっぽどやりたいということで無ければやめておいた方が良いと思います。

>「桁あふれフラグ」は、みなさんが仰っているリプレイの放置時間が
>2ビットで納まらなかったら3ビットにするためのフラグでしょうか。

そうです。何かのフラグが無いと、3ビット目がどういうデータなのか解らないので、可変長にする為にフラグを用意しています。
最上位ビットに「次の1バイトも同じくくりのフレームのデータなので、連続で読み込んでね」という信号をいれておくのです。

>龍神録だとパッドとキーボード両方を監視しているので

キーボードの入力状態もパッドの入力状態も分ける必要はありません。
キーボードのZも、パッドの○も「ショットボタン」を表すのであれば、ゲーム側からしてみたら、
それがZキーであろうが、○ボタンであろうがどうでもいいのです。
必要なのは、どういう役割のボタンかということです。
龍神録だと9種類しか使っていないので、4ビットで十分ですね。

>同時押しすると
>2,30,(キー番号:2、差分フレーム:30)
>3,30,(キー番号:3、差分フレーム:30)
>4,30,(キー番号:4、差分フレーム:30)
>この3つが一気に保存されるということで合ってますでしょうか?

いえ、差分なので、同時だと差分フレームは0になるはずです。
私が最初に書いたプログラムは、ゲームが始まってからのフレーム数なので、次第に増えますが、
差分フレームは、前に変化があったフレーム数から何フレームかということなので、
同時なら0です。

ですから、フレーム数を数える処理が必要ですね。
まずは、データを小さくするより解りやすい方法で一度実装してみてはどうでしょう。

差分フレームの話はひとまずおいておいて、
まず私が最初に書いたようにフレーム番号で管理してみて下さい。

添付ファイルはまだザッとしか見ていませんが、
nowKeyStateは押している間加算される仕様ですか?
それならchar型だと2秒で溢れてしまいますよ。(128までしか数えられない)
unsigned intが必要です。

保存したデータの情報は「押下状態が変わった」だけですから、どれ位押されているかは解りません。
ここでいうならば
GetJoypadInputStateでとってくるPadInputの状態を変化させるための情報であり、
どれ位入力され続けているかはまた別に計算する必要があります。

読み込み・適用部と入力カウント計算部は関数で分けた方がわかりやすいかもしれませんね。

レイ

Re:リプレイデータでキー入力の変化を記録する方法

#27

投稿記事 by レイ » 14年前

>私が最初に書いたようにフレーム番号で管理してみて下さい。

管理人さんの詳しい説明でビットの知識がかなり身に付きましたので
管理人さんの作って下さったサンプルを使って龍神録のkey.cppを変えてみました。

添付ファイルがそれです。
メイン関数の部分は管理人さんのサンプルと同じです。

とりあえず分かりやすい実装とアドヴァイスを頂いたので
このソースでは差分フレームはまだ行ってないです。
フレーム番号と、ボタン番号のみ保存して、読み込むというものです。

nowKeyStateは、加算される仕様は溢れてしまうと教えて頂いたので取りやめました。
nowKeyState[configpad.up]=1;とすると、メモリーエラーがなくなって大丈夫でした。

しかし、リプレイの録画した自機の動きが
再生したものと異なる動きをしてしまいます。

すみません…ご指摘お願いいたします…。 画像

スキマ妖怪

Re:リプレイデータでキー入力の変化を記録する方法

#28

投稿記事 by スキマ妖怪 » 14年前

すいません…、多分私が元凶です;


> しかし、リプレイの録画した自機の動きが
> 再生したものと異なる動きをしてしまいます。

ReadKeyChangeState関数内の

pad_key[KeyNum]++;

が原因ではないでしょうか。
押されていないキーに対しては、

pad_key[KeyNum] = 0;

とかしないと足しっ放しになってまずくないですか。
間違っていたらすいません

レイ

Re:リプレイデータでキー入力の変化を記録する方法

#29

投稿記事 by レイ » 14年前

できました!
差分フレームでのリプレイ記録、及び再生まできっちり完成しました。
これはかなりいいですね。

ありがとうございました!

閉鎖

“C言語何でも質問掲示板” へ戻る