ステージ間ワープを作りましたが、自信がないので見てください。

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
usao
記事: 1564
登録日時: 6年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#31

投稿記事 by usao » 4年前

オフトピック
>走り状態に切り替える
という処理を行う関数の中で
「え?いやです.遷移しません」とかいう話も不思議.

この関数を呼ぶときというのは,
既に何らかの判断の結果として状態遷移先がR_RUNだということが決定しているのではないのだろうか…?

走るべきかどうかわからないけど とりあえず「走り中という状態に遷移しろ」と言ってみる みたいな…?

ISLe()

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#32

投稿記事 by ISLe() » 4年前

usaoさんのおっしゃっている「表」というのは、データテーブル(あるいは単にテーブル)といってとても多用されます。
ゲームプログラムの内部的な動作はテーブルの参照と状態の更新だけと言っても過言ではないです。
テーブルもこの掲示板で話題になったことがあるので過去ログを探せば参考になる情報が見付かるかもしれません。

No.24に書いたように、キャラに与える命令を「ルール」、実際の変化を「振る舞い」として分離させます。
この概念はあらゆる場所に適用できます。
基本的に、命令と状態のインデックスでテーブルを参照し、そこから新しい状態を持ってくるだけです。

キャラオブジェクトに命令を与える(アクションを要求する)メソッドはひとつあれば良い。
「ルール」と「振る舞い」をきちんと整理すればプログラムには基本的にswitchは必要ありませんし、ifすらも必要ありません。

ISLe()

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#33

投稿記事 by ISLe() » 4年前

ステートパターンに関して。

ステートパターンは、継承によりオブジェクト自身がステートとなるパターンです。

現在のキャラのインスタンスに対して「ジャンプしろ」と要求したら、『ジャンプした状態のオブジェクト』が生成されて返ってきて、前のオブジェクトは捨てて次からは新しいオブジェクトを使う、という感じの使い方をします。

ゲームキャラでそんな実装したらプログラマは死ねます。
使いどころとしてはフレームワークの一部とかですかね。

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#34

投稿記事 by イマダニ » 4年前

基本的に、命令と状態のインデックスでテーブルを参照し、そこから新しい状態を持ってくるだけです。
大体の考え方が分かってきました。
テーブルの概念は、昔ISLeさんにクォーターの八方向の移動を作る際に教わったので大体わかります。
あの時はありがとうございました。

コード:

void Player::InputStateManage(){
	const int keyinput_muki_table[] = {
		Neutral, // nothing
		Left, // L
		Right, // R
		Neutral, // L+R
		Up, // U
		L_Up, // U+L
		R_Up, // U+R
		Up, // U+L+R
		Down, // D
		L_Down, // D+L
		R_Down, // D+R
		Down, // D+L+R
		Neutral, // D+U
		Left, // D+U+L
		Right, // D+U+R
		Neutral, // D+U+L+R
	};

	input_way = 0;

	if (Input::Inst()->CheckPad(Input::Right))
		input_way += 2;
	if (Input::Inst()->CheckPad(Input::Left))
		input_way += 1;
	if (Input::Inst()->CheckPad(Input::Up))
		input_way += 4;
	if (Input::Inst()->CheckPad(Input::Down))
		input_way += 8;

	input_state = keyinput_muki_table[input_way];
}
上記のを参考に書き換えると

コード:

void Player::InputStateManage(){
	int PlayerState;

	const int Player_Input_Table[/*状態*/][/*命令*/4] = {
		//立ち時
		R_N, R_RUN, L_RUN, R_JUMP,	  /*左から*/ /*入力なし、右キー、左キー、上キー*/
		L_N, R_RUN, L_RUN, L_JUMP,
		//走り時
		R_N, R_RUN, L_RUN, R_SIDEJUMP,/*左から*/ /*入力なし、右キー、左キー、上キー*/
		L_N, R_RUN, L_RUN, L_SIDEJUMP,
		//ジャンプ時
		R_JUMP, R_JUMP, L_JUMP, R_JUMP,  /*左から*/ /*入力なし、右キー、左キー、上キー*/
		L_JUMP, R_JUMP, L_JUMP, L_JUMP,
	};

	input_way = 0;

	/*入力に合わせて命令を*/
	if (Input::Inst()->CheckPad(Input::Right))
		input_way += 1;		/*ここで『キャラオブジェクトに命令を与える(アクションを要求する)メソッド』を使う。*/
							/*例:Set_Order(命令);といった感じの命令を引数にとるメソッドをプレイヤー側に用意?*/
	if (Input::Inst()->CheckPad(Input::Left))
		input_way += 2;
	if (Input::Inst()->CheckPad(Input::Up))
		input_way += 3;
	if (Input::Inst()->CheckPad(Input::Down))
		input_way += 0;

	/*現在の状態と、上記のif構文で更新される命令、二つのインデックスをもとに、ここで状態を放り込む*/
	PlayerState = Player_Input_Table[PlayerState][input_way];
}
こんな感じでしょうか?

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#35

投稿記事 by イマダニ » 4年前

ゲームキャラでそんな実装したらプログラマは死ねます。
使いどころとしてはフレームワークの一部とかですかね。
ひえっ
使いどころをよく考えて使います……
解説ありがとうございます

Rittai_3D
記事: 522
登録日時: 6年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#36

投稿記事 by Rittai_3D » 4年前

イマダニ さんが書きました:
実際に数値を出力しておかしな所があるか確認しましたか?
「思います」との発言から実際にデバッグしていないものと判断しましたが、もしそうならフォーラムルールにある丸投げに当たるのではないでしょうか。
ほんとうに丸投げにはしたくないのですが、会話の流れでつい甘えが出るのか、ついアホなことを言ってしまいます。
3Dさん、ごめんなさい。

なのでこの質問がいわゆる『丸投げ』になるようでしたら、悩み自体には全く返答しなくても構いません。
返答がなかったら丸投げになってるんだなと判断して、自分でちゃんと考えます。

何でこんなこと言うかというと、今回の悩み自体が難しすぎて、どう質問したらいいのかぶっちゃけよくわからない状態だからです。
どの段階で聞いていいかわからないというか。

『答えてやってもいいけど仕様とか現段階のコードとかいろいろ情報足らんわ』

ということでしたら情報を答えます。
一応現状のソースコードを貼っていただきたいです。時間が作れましたら動作の確認をします。
ちなみに、イマダニさん自身はデバッグしましたか?デバッグを行ったなら、その結果も教えてくだい。
オフトピック

コード:

std::unique_ptr<StageData[]>  Data;
前にも書きましたが、ここはstd::vector<>やstd::list<>などを使うべきでしょう。
それとも、std::unique_ptr<>で持たなければならない理由でもあるのでしょうか。
オフトピック
今回の悩み自体が難しすぎて、どう質問したらいいのかぶっちゃけよくわからない状態だからです。
「ファイル読み込み処理をfopen()からifstreamに変更したら、自分の意図していない挙動になってしまいました」
「デバッグをした結果、○○行目で入るべき数値と違った数値が代入されているようです」
「なぜ違った数値が入っているのかが分かりません。原因を教えてください」
と聞けばよいのでは・・・?
それとも、わたしが読み違えている・・・?
初心者です

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#37

投稿記事 by イマダニ » 4年前

一応現状のソースコードを貼っていただきたいです。時間が作れましたら動作の確認をします。
3Dさん。忙しい中、ありがとうございます。
周りにプログラマーの友人もいないので見ていただけるのはほんとうに助かります。
ちなみに、イマダニさん自身はデバッグしましたか?デバッグを行ったなら、その結果も教えてくだい。
今はISLeさんやusaoさんのアドバイスをもとにコードをガンガン書きなおしているので、いったんファイル読み込みのほうは後回しにしている状態です。
それが終わり次第デバッグを行い、結果報告の際に、出来上がったコードをzipファイル形式で貼ろうと思います。

Rittai_3D
記事: 522
登録日時: 6年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#38

投稿記事 by Rittai_3D » 4年前

今はISLeさんやusaoさんのアドバイスをもとにコードをガンガン書きなおしているので、いったんファイル読み込みのほうは後回しにしている状態です。
それが終わり次第デバッグを行い、結果報告の際に、出来上がったコードをzipファイル形式で貼ろうと思います。
No18
イマダニ さんが書きました:

コード:

int Ground::Load(const char *dataname){
    char buf[256];  // データ一時保管用配列
    char comma;                     //カンマ用変数
    int data = 0;
 
    std::ifstream ifs(dataname);
 
    ifs.getline(buf, sizeof(buf));  // 一行読み込んで
    std::istringstream iss(buf);    //それを文字列として読み込む
 
    iss >> ChipY_Num >> comma >> ChipX_Num;
 
    //==========================
    //ステージデータ動的確保
    //==========================
    Data.reset(new StageData[ChipY_Num*ChipX_Num]);
 
    //要素数分だけデータ変数にハンドル内容を代入
    for (int y = 0; y<ChipY_Num; y++){
        ifs.getline(buf, sizeof(buf));  // 一行読み込んで
        std::istringstream iss(buf);    //それを文字列として読み込む
        for (int x = 0; x<ChipX_Num; x++){
            iss >> data >> comma;
 
            //マップデータにファイルデータを渡す
            Data[y*ChipX_Num + x].BackGround = data;
        }
    }
    /*これをレイヤーごとに*/
    for (int y = 0; y<ChipY_Num; y++){
        ifs.getline(buf, sizeof(buf));  // 一行読み込んで
        std::istringstream iss(buf);    //それを文字列として読み込む
        for (int x = 0; x<ChipX_Num; x++){
            iss >> data >> comma;
 
            //マップデータにファイルデータを渡す
            Data[y*ChipX_Num + x].レイヤー = data;
        }
    }
    return 0;
}
このコードのまま変わっていないのでしたら、9行目の iss と32行目の iss が被っていて、34行目の

コード:

iss >> data >> comma;
の部分の iss が前者の iss と判断されているのではないでしょうか。
実際に動かしていないので勘です。

・・・と前の投稿で書けばよかったですね、すいません。
初心者です

ISLe()

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#39

投稿記事 by ISLe() » 4年前

イマダニ さんが書きました:こんな感じでしょうか?
それはユーザー入力の変換テーブルですね。

1. 周辺機器からの現実の入力を、アプリケーションで定義する仮想の入力に変換
2. アプリケーションで定義する仮想の入力を、ゲーム内のオブジェクトで定義する仮想のアクションに変換
3. ゲーム内のオブジェクトで定義する仮想のアクションを元に、振る舞いを変化

No.34でやってるのは1です。
やりたいことは1から3の一連の流れ(特に3)なのでは。
わたしは3のつもりでテーブルの話をしてました。

ひとつのテーブルですべて処理できるわけでなく、個々にテーブルを経ていきます。

何度も書いている「ルール」と「振る舞い」の分離で設計や実装に柔軟性が生まれます。
キャラの振る舞いをユーザーの入力(の仕様)に直結すれば、ユーザー入力がないとテストもできなくなります。
開発効率が激しく低下します。


No.29でイマダニさんが書いている「立ち状態」。
文字通り「状態」です。
それはキャラの振る舞いのひとつ。
であれば、「立ち状態」になる外部要因は何か。
それを整理して、アクションをひとつだけ定義してください。

先回りしておくと「地面に触れる」というのは要因であってアクションではありません。
要因がなくてもアクションできるようにするのがゲームプログラムの効率の良い作り方のひとつです。

イマダニ
記事: 145
登録日時: 7年前

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#40

投稿記事 by イマダニ » 4年前

ISLeさん。何度も何度も詳しい説明ありがとうございます。
説明された以上、ちゃんと理解するのが礼儀だと思うので、もう一度、理解できてるかの確認お願いします。
No.29でイマダニさんが書いている「立ち状態」。
文字通り「状態」です。
それはキャラの振る舞いのひとつ。
振る舞い:立ち状態
であれば、「立ち状態」になる外部要因は何か。
先回りしておくと「地面に触れる」というのは要因であってアクションではありません。
外部要因:地面に触れている。入力がない。
それを整理して、アクションをひとつだけ定義してください。
振る舞い:立ち状態
外部要因:地面に触れている。入力がない。

アクション:立つ。


こういうことですか?

振る舞い:右走り
外部要因:地面に触れている。右キー入力がある。

アクション:右へ走る


こんな感じですかね?

コードだと、「地面に触れる」などといった、ステージ要因を抜いてますが(僕の悪い頭だとちょっと混乱しそうなので……

コード:


	/*仮想入力テーブル[現実の入力]*/
	const int VirtualInput[] = {
		Neutral, // nothing
		Left, // L
		Right, // R
		Neutral, // L+R
		Up, // U
		L_Up, // U+L
		R_Up, // U+R
		Up, // U+L+R
		Down, // D
		L_Down, // D+L
		R_Down, // D+R
		Down, // D+L+R
		Neutral, // D+U
		Left, // D+U+L
		Right, // D+U+R
		Neutral, // D+U+L+R
	};

	m_VirtualInputWay = 0;
	
	/*現実の入力*/
	if (Input::Inst()->CheckPad(Input::Inst()->Get_PadHandle(Input::Left)))
		m_VirtualInputWay += 1;
	if (Input::Inst()->CheckPad(Input::Inst()->Get_PadHandle(Input::Right)))
		m_VirtualInputWay += 2;
	if (Input::Inst()->CheckPad(Input::Inst()->Get_PadHandle(Input::Up)))
		m_VirtualInputWay += 4;
	if (Input::Inst()->CheckPad(Input::Inst()->Get_PadHandle(Input::Down)))
		m_VirtualInputWay += 8;

	//仮想入力
	m_VirtualInput_State = VirtualInput[m_VirtualInputWay];


	/*仮想アクションテーブル[仮想入力(外部要因)]*/		/*[ここにステージ要因(地面に触れるとか、触れてないとか)?]*/
	const int VirtualAction[] = {
		STAND,		//インデックスが『入力なし』なら『立ちアクション』
		RUN,		//走り
		RUN,
		JUMP,		//ジャンプ
		SIDEJUMP,	//横ジャンプ
		SIDEJUMP,
		DOWN,		//しゃがみ
		DOWN,
		DOWN,
	};

	//仮想アクション
	m_VirtualAction_State = VirtualAction[m_VirtualInput_State];


	/*振る舞いテーブル*/
	const int PlayerState[/*プレイヤーの状態*/][/*仮想アクション*/] = {
		R_N, R_RUN, L_RUN, R_JUMP, R_SIDEJUMP, L_SIDEJUMP, R_DOWN,		//[右立ち状態の時の][各アクションでの状態]

		R_N, R_RUN, L_RUN, R_SIDEJUMP, R_SIDEJUMP, L_SIDEJUMP, R_DOWN,  //[右走り状態の時の][各アクションでの状態]

		R_JUMP, R_JUMP, L_JUMP, R_JUMP, R_JUMP, L_JUMP, R_JUMP,			//[右ジャンプ状態の時の][各アクションでの状態]
	};

	//プレイヤーの振る舞い(状態)
	m_PlayerState = PlayerState[PlayerState/*プレイヤーの状態*/][m_VirtualAction_State/*仮想アクション*/]

てな感じでしょうか?
何度も何度もすいません……

ISLe()

Re: ステージ間ワープを作りましたが、自信がないので見てください。

#41

投稿記事 by ISLe() » 4年前

イマダニ さんが書きました:振る舞い:立ち状態
外部要因:地面に触れている。入力がない。

アクション:立つ。


こういうことですか?
そういうことです。
まだ外部要因が少ない(あるいは今後増えていく)ですが。

キャラの中で状況判断して、その場で振る舞いを変える、というプログラムが多いですよね。
むしろネットとか書籍とかどこでもそういうコードしか見掛けないというか。

おっしゃるとおり外的要因は、ユーザー操作やステージギミックなど多岐に渡るので、事前にアクションを必要最小限にまとめておくことが重要なのです。
外部要因はいろんなところで使うのでインデックスにも使えるIDとして用意すればよいです。

それぞれ整理してから、外部要因から変化する振る舞いをテーブルにしていきます。
十分整理してからでないと指数関数的に無駄に時間がかかってしまうので注意してください。

閉鎖

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