ボタン操作によるグラフィックの描画に関しまして

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
mj(水無月)
記事: 22
登録日時: 12年前
住所: 東京

ボタン操作によるグラフィックの描画に関しまして

#1

投稿記事 by mj(水無月) » 12年前

いつもお世話になっております。

ボタン操作によるグラフィックの描画に関しましてご質問がございます。
[新・C言語プログラミングの館]や[龍神録プログラミングの館]にて上下左右キーを
押した際のキャラクター画像の切り替え方法は理解できました。
しかし、こちらで紹介されていたものは1つのモーションに要する画像(キャラチップ)の数が
4枚で表現され1枚の画像に全モーションが納められているものでした。
例えば前方移動:8枚、後方移動:5枚、ジャンプ:10枚などのように1つのモーションに
要する画像の数が違う場合はこちらで紹介されている1枚の画像に1列目は上方向移動
2列目は右方向移動というように1枚の画像に描画するモーションを列毎に分けて並べる
方法を使用することができるのでしょうか。
この方法をそのまま使用して、全モーションを1枚の画像に並べた場合、1列に並べられている
画像の数がそれぞれ異なってしまうためモーションによってちらついて描画されてしまいます。

上記のような1つのモーションに要する画像の枚数が異なる場合も[新・C言語プログラミングの館]や
[龍神録プログラミングの館]で紹介されている方法を利用して描画することが可能なのでしょうか。
それとも、1モーションを1つの画像として(移動画像、ダッシュ画像、ジャンプ画像など)それぞれを読み
込ませてこのキーが押されている場合はDrawGraph、DrawRotaGraphFで描画という方法をとるべき
でしょうか。

どなたか、こちらに関しましてよい方法のご教授をお願い致します。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#2

投稿記事 by softya(ソフト屋) » 12年前

配列に入れるのか変数に入れるのか、それは状況に応じて使いわけられるべきですね。
[新・C言語プログラミングの館]や[龍神録プログラミングの館]の方法は一例に過ぎませんし分かりやすくするため結構簡易版ですので、ちゃんと作る場合は状況に応じて最適の方法を選ぶ必要が有るわけです。
画像をどう並べて一枚に収めると最適か?とかはどうやって変数に入れてアニメーションして動かすのかと絡めて考えないといけません。

>例えば前方移動:8枚、後方移動:5枚、ジャンプ:10枚などのように1つのモーションに要する画像

これだと、どういう風に変数・配列に格納されていると扱いやすいと思いますか?
そこから考えていったほうが良い問題ですので、考えてみてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
雨ガエル
記事: 34
登録日時: 13年前
住所: 新潟
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#3

投稿記事 by 雨ガエル » 12年前

いったんLoadDivGraph関数でグラフィックをすべて読み込んで、
モーションごとに用意した配列に必要な部分だけコピーしてはどうでしょうか。

例:
モーション1では4枚のグラフィックを使用、
モーション2では2枚のグラフィックを使用、
モーション3では8枚のグラフィックを使用するとします。

〇〇〇〇◇◇◇◇
〇〇◇◇◇◇◇◇
〇〇〇〇〇〇〇〇

〇:使うグラフィックス
◇:使わない部分

コード:

int motion1[4];
int motion2[2];
int motion3[8];

int graphics[24];
LoadDivGraph("test.bmp",24,8,3,16,16,graphics);

int i;
for(i = 0; i<4; i++)motion1[i] = graphics[i];         // モーション1の4枚をコピー
for(i = 0; i<2; i++)motion2[i] = graphics[i + 8];    // モーション2の2枚をコピー
for(i = 0; i<8; i++)motion3[i] = graphics[i + 16];  // モーション3の8枚をコピー
これで各モーションの配列には必要なグラフィックスだけが入っているので、
それを扱えばいいと思います。

求めている回答とちがっていたらすみません。
最後に編集したユーザー 雨ガエル on 2013年5月11日(土) 13:02 [ 編集 1 回目 ]

アバター
せんちゃ
記事: 50
登録日時: 14年前
住所: 江別市東野幌町
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#4

投稿記事 by せんちゃ » 12年前

各アニメーションで利用する画像の数が違うのであれば龍神録の方法は難しいと思います。
mj(水無月) さんが書きました: 1モーションを1つの画像として(移動画像、ダッシュ画像、ジャンプ画像など)それぞれを読み
込ませてこのキーが押されている場合はDrawGraph、DrawRotaGraphFで描画という方法をとるべき
でしょうか。
そういった方法も一つの答えだと思います。
しかしいちいちそれ用に編集するのも大変ですよね。

・各アニメーションが何枚の画像からなるアニメーションなのか
・どの要素番号が必要なのか
をデータにまとめるとやりやすいのかもしれませんね。
ヽ(*゚д゚)ノ カイバー

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#5

投稿記事 by ISLe » 12年前

グラフィックハンドルをコピーすると解放したかどうか判断するのが面倒になるのでポインタを使ったほうが良いと思います。

コード:

int *motion1;
int *motion2;
int *motion3;
int graphics[24];
LoadDivGraph("test.bmp",24,8,3,16,16,graphics);
motion1 = &graphics[0];  // モーション1の4枚のハンドルの先頭を指す
motion2 = &graphics[8];  // モーション2の2枚のハンドルの先頭を指す
motion3 = &graphics[16]; // モーション3の8枚のハンドルの先頭を指す
画像中の配置だけでは限度がありますが、要素番号のテーブルを用意すれば柔軟に対応できます。
こちらも雨ガエルさんのコードを流用させていただくとこんな感じになります。

コード:

int motion1[] = { 0, 1, 2, 3 };
int motion2[] = { 8, 9 };
int motion3[] = { 16, 17, 18, 19, 20, 21, 22, 23 };
int graphics[24];
LoadDivGraph("test.bmp",24,8,3,16,16,graphics);
graphics[motion1[n]]のようにグラフィックハンドルを参照します。
画像が縦に並んでいても、離れていても、一部の画像を共用していても、要素番号のテーブル次第でどうとでもできます。

モーションごとにアニメーションのパターン数が異なるのをどのように(共通に)処理するかについては考えてみてください。

mj(水無月)
記事: 22
登録日時: 12年前
住所: 東京

Re: ボタン操作によるグラフィックの描画に関しまして

#6

投稿記事 by mj(水無月) » 12年前

>softya(ソフト屋)さん、雨ガエルさん、せんちゃさん、ISLeさんご意見ありがとうございます。

雨ガエルさんとISLeさんが参考コードを提示してくださったのでそれを元にプログラミングしてみました。
一応画像の読み込みからキーの押されている状態でのグラフィックの切り替えまでを行っているのですが
結構無理矢理なやり方であまりきれいなコードとはよべないかもしれません。すみません、私の実力不足です。

コード:

//画像読み込み
void load()
{
	LoadDivGraph( "mydat/dat/img/char/test.png" , 36 , 12 , 3 , 160 , 192 , graphics ) ;
}
//画像描画 
void graph_ch()
{
	int motion1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ,11 };//ニュートラル
	int motion2[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20 ,21 };//前進
	int motion3[] = { 24, 25, 26, 27, 28, 29 };//後退

	if(ch.img>=12 && ch.img<=21)
	{
		DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,graphics[motion2[ch.img]],TRUE);//前進
	}
	else if(ch.img>=24 && ch.img<=29)
	{
		DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,graphics[motion3[ch.img]],TRUE);//後退
	}
	else
	{
		DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,graphics[motion1[ch.img]],TRUE);//ニュートラル
	}
}

//キャラクタの移動制御
void ch_move()
{
	int i;
	int inputpad[2];
	inputpad[0]=CheckStatePad(configpad.left);
	inputpad[1]=CheckStatePad(configpad.right);

	if(CheckStatePad(configpad.left)>0 && mv.sayu_flag==0)
	{
		ch.img=22;//motion2の最初の番号をセット
	}
	else if(CheckStatePad(configpad.right)>0 && mv.sayu_flag==0)
	{
		ch.img=12;//motion3の最初の番号をセット
	}
	for(i=0;i<2;i++)//左右分
	{
		if(inputpad[i]>0)//左右どちらかの入力があれば
		{
			mv.sayu_flag=1;//左右入力フラグを立てる
			ch.img++;
			if(inputpad[0]>0 && ch.img>=30)
			{
				ch.img=24;
			}
			if(inputpad[1]>0 && ch.img>=21)
			{
				ch.img=12;
			}
		}
		else
		{
			mv.sayu_flag=0;
			ch.cnt++;
			ch.img=(ch.cnt%120)/10;
		}
	}
	for(i=0;i<2;i++)
	{
		if(inputpad[0]>0)
		{
			ch.x-=(int)2*mv.move;//前進
		}
		if(inputpad[1]>0)
		{
			ch.x+=(int)2*mv.move;//後退
		}

	}

}
あと、 せんちゃさんがそれもひとつのやり方だとおっしゃってくれたキーが押された状態によって描画する
画像を決定する方法も試してみました。こちらは割りと単純な構造で作成できましたがキー操作の種類が
多くなったり、キー同時押し処理が行われた場合も考えると確かにあまり効率的な方法ではない気がします。

コード:

 
//モーション毎に画像を読み込み
void load()
{
	LoadDivGraph( "mydat/dat/img/char/neutral.png" , 12 , 4 , 3 , 160 , 192 , neutral[0] ) ;
	LoadDivGraph( "mydat/dat/img/char/walk.png" , 10 , 10 , 1 , 160 , 192 , walk[0] ) ;
	LoadDivGraph( "mydat/dat/img/char/bwalk.png" , 6 , 6 , 1 , 160 , 192 , bwalk[0] ) ;
}
//グラフィック描画
void graph_ch()
{
	if(CheckStatePad(configpad.right)>0)
	{
		DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,walk[0][ch.img],TRUE);
	}
	else if(CheckStatePad(configpad.left)>0)
	{
		DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,bwalk[0][ch.img],TRUE);
	}
	else
	{
		DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,neutral[0][ch.img],TRUE);
	}
}
//キー操作による移動制御
void calc_ch()
{
	ch.cnt++;
	if(CheckStatePad(configpad.left)>0)
	{
		ch.img=(ch.cnt%36)/6;//後進
	}
	else if(CheckStatePad(configpad.right)>0)
	{
		ch.img=(ch.cnt%100)/10;//前進
	}
	else
	{
		ch.img=(ch.cnt%72)/6;//ニュートラル動作
	}
}

void ch_move()
{			
	if(CheckStatePad(configpad.left)>0)
	{
		ch.x-=(int)2*mv.move;
	}
	if(CheckStatePad(configpad.right)>0)
	{
		ch.x+=(int)2*mv.move;
	}
	if(CheckStatePad(configpad.left)>0 && CheckStatePad(configpad.right)>0 )
	{
		ch.x-=(int)2*mv.move;
	}
}


アバター
せんちゃ
記事: 50
登録日時: 14年前
住所: 江別市東野幌町
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#7

投稿記事 by せんちゃ » 12年前

効率的ではないように感じるのは画像アニメーション処理とキー入力制御と計算処理を
いっぺんにやろうとしているからだと思います。

ある一か所でキーの入力種類をすべて判別して
あとはその種類に応じた更新処理、描画処理を呼ぶような作りにした場合、
たぶんどちらの方法をとっても大差ない作りになるような気がします。

グラフィックアニメーションとキー操作はそれぞれ別々に制御する、と考えてみましょう。
グラフィックと移動はただの演出ですが、キー入力はグラフィックのアニメーションを制御する実行者です。

キーの入力を演出側にわかるデータにしてあげます。
右を入力したら前進
左を入力したら後進
何も押されなければニュートラル
という「状態」になります。

※この「状態」のことを人によってはステートと呼称したりします。


この各状態を持った変数を一つ用意して、
状態に応じて今するべき演技をさせてあげるのです。

と考えるととても簡単な作りになります。
入力が増えても演技「状態」が増えるだけなので変更も簡単です。

コード:

int state = 0;   // これが「状態」です

//	-------------------------------------------------------------
//	「状態」一覧
const int Neutral = 0;
const int Walk    = 1;
const int BWalk   = 2;
//	-------------------------------------------------------------



//グラフィック描画
void graph_ch(){
	switch( state ){
	case Neutral : 
        DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,neutral[0][ch.img],TRUE);
		break;
	case Walk    : 
        DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,walk[0][ch.img],TRUE);
		break;
	case BWalk   : 
        DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,bwalk[0][ch.img],TRUE);
		break;
	}
}


//キー操作による移動制御
void calc_ch(){
    state = Neutral;
    if(CheckStatePad( configpad.left ) > 0 ){
    	state = BWalk;
    }
    if(CheckStatePad( configpad.right ) > 0 ){
    	state = Walk;
    }
}
 
void ch_move(){
	switch( state ){
	case Neutral : 
		ch.img=(ch.cnt%72)/6;		//	ニュートラル動作
		break;
	case Walk : 
		ch.x+=(int)2*mv.move;
		ch.img=(ch.cnt%100)/10;		//	前進
		break;
	case BWalk : 
		ch.x-=(int)2*mv.move;
		ch.img=(ch.cnt%36)/6;		//	後進
		break;
	}
    ch.cnt++;
}
ヽ(*゚д゚)ノ カイバー

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#8

投稿記事 by ISLe » 12年前

mj(水無月) さんが書きました:雨ガエルさんとISLeさんが参考コードを提示してくださったのでそれを元にプログラミングしてみました。
要素番号のテーブルを使うのに、ch.imgの値で分岐させたら意味ないですよ。
というかch.img==12のときmotion2[ch.img]は範囲外の不正アクセスです。

コードは3分の1の量にできます。

mj(水無月)
記事: 22
登録日時: 12年前
住所: 東京

Re: ボタン操作によるグラフィックの描画に関しまして

#9

投稿記事 by mj(水無月) » 12年前

>ISLe さん、すみません要素番号のテーブルを利用して、DrawRotaGraphFで画像を描画させる方法がいまいち
理解できていなかったようです。
申し訳ないですがもう少し具体的な方法をお教えいただけないでしょうか。

mj(水無月)
記事: 22
登録日時: 12年前
住所: 東京

Re: ボタン操作によるグラフィックの描画に関しまして

#10

投稿記事 by mj(水無月) » 12年前

>せんちゃさん、確かに状態を表す変数を設定することによって分かりやすい構造になっていると思います。
ありがとうございます。下記のコードはせんちゃさんが提示してくださったコードにジャンプ処理を加えてみたものです。
一応ジャンプはキーを押した後、モーションが完了するまではキーを離してもモーションを続行するようにフラグを
立てるなどしていますがこの方法だと前方ジャンプや後方ジャンプなどジャンプキーと同時に別のキーを押している
ケースに対応させるためにif文に条件をつらつらと書くというあまりきれいではない処理になってしまいます。
ここら辺をうまくきれいに処理するような方法はないでしょうか。

コード:

	//  「状態」一覧
	const int Neutral = 0;
	const int Walk    = 1;
	const int BWalk   = 2;
	const int Jump    = 3;
	const int FJump   = 4;

void graph_ch()
{
	switch( graph_state )
	{
    case Neutral: 
        DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,tewi_neutral[0][ch.img],TRUE);
        break;
    case Walk: 
        DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,tewi_walk[0][ch.img],TRUE);
        break;
    case BWalk: 
        DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,tewi_bwalk[0][ch.img],TRUE);
        break;
	case Jump: 
        DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,tewi_jump[0][ch.img],TRUE);
        break;
	case FJump: 
        DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,tewi_jump[0][ch.img],TRUE);
        break;
    }
}
//キー操作による移動制御
void calc_ch()
{
    graph_state = Neutral;
	if(CheckStatePad( configpad.right ) > 0 )
	{
		if(mv.jflag==0)
        graph_state = Walk;
    }
    if(CheckStatePad( configpad.left ) > 0 )
	{
		if(mv.jflag==0)
        graph_state = BWalk;
    }
	if((CheckStatePad( configpad.jump ) > 0 && CheckStatePad( configpad.right )==0) || mv.jflag==1)
	{
        graph_state = Jump;
    }
	if((CheckStatePad( configpad.jump ) > 0 && CheckStatePad( configpad.right )>0) || mv.fjflag==1)
	{
        graph_state = FJump;
    }
}

void ch_move(){
    switch( graph_state )
	{
    case Neutral:
        ch.img=(ch.cnt%72)/6;       //  ニュートラル動作
        break;
    case Walk:
        ch.x+=(int)2*mv.move;
		//キャラの移動制御
		if(ch.x>640-MARGIN)
		{
			ch.x=640-MARGIN;
		}
		else if(ch.x<MARGIN)
		{
			ch.x=MARGIN;
		}
		ch.img=(ch.cnt%100)/10;     //  前進
        break;
    case BWalk:
        ch.x-=(int)2*mv.move;
        ch.img=(ch.cnt%36)/6;       //  後進
		//キャラの移動制御
		if(ch.x>640-MARGIN)
		{
			ch.x=640-MARGIN;
		}
		else if(ch.x<MARGIN)
		{
			ch.x=MARGIN;
		}
        break;
	case Jump:
		if(mv.fjflag==0 && mv.bjflag==0 && mv.jflag==1 )
		{//ノーマルジャンプ
			mv.y_temp = ch.y;
			ch.y +=(ch.y-mv.y_prev)+1;
			mv.y_prev = mv.y_temp;
			if(ch.y==325)
			{
				mv.jflag=0;
				if(CheckStatePad(configpad.jump)>0)
				{//着地後ジャンプボタンが押されている状態なら連続でジャンプ
					mv.jflag=1;
					mv.y_prev=ch.y;
					ch.y=ch.y-20;
				}
			}
		}
		if(CheckStatePad(configpad.jump)==1 && CheckStatePad(configpad.right)==0 && CheckStatePad(configpad.left)==0 && mv.jflag==0)
		{//ノーマルジャンプの最初の処理
 			mv.jflag=1;
			mv.y_prev=ch.y;
			ch.y=ch.y-20;
		}
		//キャラの移動制御
		if(ch.y>480-MARGIN)
		{
			ch.y=480-MARGIN;
		}
		else if(ch.y>324)
		{
			ch.y=324;//地面がある座標
		}
			ch.img=(ch.cnt%40)/4;       //  ジャンプ
		    break;
	case FJump:
		//前方ジャンプ
		if(mv.fjflag==1 && mv.jflag==1 )
		{
			mv.y_temp = ch.y;
			ch.y +=(ch.y-mv.y_prev)+1;
			mv.y_prev = mv.y_temp;
			ch.x+=(int)4*mv.move;
			if(ch.y==325)
			{
				mv.jflag=0;
				mv.fjflag=0;
				if(CheckStatePad(configpad.jump)>0 && CheckStatePad(configpad.right)==0)
				{//着地後ジャンプボタンが押されている状態なら連続でジャンプ
					mv.jflag=1;
					mv.y_prev=ch.y;
					ch.y=ch.y-20;
				}
				else if(CheckStatePad(configpad.jump)>0 && CheckStatePad(configpad.right)>0)
				{//着地後前方ジャンプボタンが押されている状態なら連続で前方ジャンプ
					mv.jflag=1;
					mv.fjflag=1;
					mv.y_prev=ch.y;
					ch.y=ch.y-20;
					ch.x+=(int)4*mv.move;
				}
			}
		}
		//前方ジャンプの最初の処理
		if(CheckStatePad(configpad.jump)>0 && CheckStatePad(configpad.right)>0 && mv.jflag==0)
		{
			mv.jflag=1;
			mv.fjflag=1;
			mv.y_prev=ch.y;
			ch.y=ch.y-20;
			ch.x+=(int)4*mv.move;
		}
		//キャラの移動制御
		if(ch.y>480-MARGIN)
		{
			ch.y=480-MARGIN;
		}
		else if(ch.y>324)
		{
			ch.y=324;//地面がある座標
		}
			ch.img=(ch.cnt%40)/4;       //  ジャンプ
	        break;
		}
		ch.cnt++;
}


ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#11

投稿記事 by ISLe » 12年前

mj(水無月) さんが書きました:すみません要素番号のテーブルを利用して、DrawRotaGraphFで画像を描画させる方法がいまいち
理解できていなかったようです。
申し訳ないですがもう少し具体的な方法をお教えいただけないでしょうか。
アニメーションコマ数の違いを吸収するためのテーブルも作りました。
pitchは1コマあたりのフレーム数で初期値は適当です。
関数はせんちゃさんの『状態』のサンプルをベースに書き換えました。
#コンパイル確認してないのでエラーがあるかもしれません。
#というか完全なコードではないです。

モーションテーブルを切り替えた後にch.imgを計算するようになっていないと不正アクセスする可能性があるので注意してください。
このコードではcalc_ch→ch_move→graph_chの順に呼ぶ必要があります。

コード:

static int motion1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ,11 };//ニュートラル
static int motion2[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20 ,21 };//前進
static int motion3[] = { 24, 25, 26, 27, 28, 29 };//後退

struct MotionTable {
	int *index;
	int num;
	int pitch;
};

#define ARRAYNUM(ary) (sizeof(ary)/sizeof(ary[0]))

static MotionTable motion_table[] = {
	{ motion1, ARRAYNUM(motion1), 10 },
	{ motion2, ARRAYNUM(motion2), 10 },
	{ motion3, ARRAYNUM(motion3), 10 },
};
static MotionTable *motion = &motion_table[0];

void graph_ch()
{
	DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,graphics[motion->index[ch.img]],TRUE);
}
void calc_ch(){
	state = Neutral;
	if(CheckStatePad( configpad.left ) > 0 ){
		state = BWalk;
	}
	if(CheckStatePad( configpad.right ) > 0 ){
		state = Walk;
	}
	motion = &motion_table[state];
}
void ch_move(){
	ch.img=(ch.cnt/motion->pitch)%motion->num;
	ch.cnt++;
}

mj(水無月)
記事: 22
登録日時: 12年前
住所: 東京

Re: ボタン操作によるグラフィックの描画に関しまして

#12

投稿記事 by mj(水無月) » 12年前

>ISLeさん、返信が大変遅くなってしまい申し訳ありません。
平日は忙しくて中々コードの検証がなかなかできないもので。

構造体とポインタをこのように 使用することでテーブルを作成するのですね。
提示して頂いたコードとても参考になりました。
下記はそのコードを元に実際にキーが押されたときに移動と描画の処理部分を
追加したものです。ジャンプモーションも新に追加しています。
完全なコードではないとありましたが普通にコンパイル実行もできモーション中にハングなども
起きず特に問題がないように思われますが・・・どこか不十分なところがあるのでしょうか。

また、現在void calc_ch()にてどのキーが押されていていたときに指定された座標に
指定したグラフィックを表示させるという方法を使用しています。
先にも書きましたがキーが同時押しされたときと単体で押されたときとを考慮してif文の中に
論理演算子とフラグを使用して処理を分けています。しかし、ボタン操作が増える度にif文の
中に条件を追加していくという方法はかなり非効率的に感じられます。
こちらに関しましても何か良いまとめ方等はございませんでしょうか。

コード:

	//  「状態」一覧
	const int Neutral = 0;
	const int Walk    = 1;
	const int BWalk   = 2;
	const int Jump    = 3;

	static int motion1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ,11 };//ニュートラル
	static int motion2[] = { 12, 13, 14, 15, 16, 17, 18, 19, 20 ,21 };//前進
	static int motion3[] = { 24, 25, 26, 27, 28, 29 };//後退
	static int motion4[] = { 36, 37, 38, 39, 40, 41, 42, 43, 44, 45 };//ジャンプ

	struct MotionTable 
	{
		int *index;
		int num;
		int pitch;
	}motion_table[];

	#define ARRAYNUM(ary) (sizeof(ary)/sizeof(ary[0]))

	static MotionTable motion_table[] = 
	{
		{ motion1, ARRAYNUM(motion1), 5 },
		{ motion2, ARRAYNUM(motion2), 7 },
		{ motion3, ARRAYNUM(motion3), 8 },
		{ motion4, ARRAYNUM(motion4), 4 },
	};

	static MotionTable *motion = &motion_table[0];

void graph_ch()
{
    DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,graphics[motion->index[ch.img]],TRUE);
}

void calc_ch(){
    graph_state = Neutral;
    if(CheckStatePad( configpad.right ) > 0 )
	{
        graph_state = Walk;
    }
    if(CheckStatePad( configpad.left ) > 0 )
	{
        graph_state = BWalk;
    }
	if((CheckStatePad( configpad.jump ) > 0 && CheckStatePad( configpad.right )==0) || (CheckStatePad( configpad.jump ) > 0 && CheckStatePad( configpad.right )>0) || mv.jflag==1)
	{
        graph_state = Jump;
    }
    motion = &motion_table[graph_state];
}

void ch_move()
{
    switch( graph_state )
	{
	    case Neutral:
		    ch.img=(ch.cnt/motion->pitch)%motion->num;	//  ニュートラル動作
			break;
	    case Walk:
		    ch.x+=(int)2*mv.move;
			ch_control();//移動範囲制御
			ch.img=(ch.cnt/motion->pitch)%motion->num;	//  前進
	        break;
	    case BWalk:
	        ch.x-=(int)2*mv.move;
	        ch.img=(ch.cnt/motion->pitch)%motion->num;	//  後進
			ch_control();
	        break;
		case Jump:
			if(mv.fjflag==0 && mv.bjflag==0 && mv.jflag==1)
			{//ノーマルジャンプ
				mv.y_temp = ch.y;
				ch.y +=(ch.y-mv.y_prev)+1;
				mv.y_prev = mv.y_temp;
				if(ch.y==325)
				{
					mv.jflag=0;
					if(CheckStatePad(configpad.jump)>0)
					{//着地後ジャンプボタンが押されている状態なら連続でジャンプ
						mv.jflag=1;
						mv.y_prev=ch.y;
						ch.y=ch.y-20;
					}
				}
			}
			if(CheckStatePad(configpad.jump)==1 && CheckStatePad(configpad.right)==0 && CheckStatePad(configpad.left)==0 && mv.jflag==0)
			{//ノーマルジャンプの最初の処理
 				mv.jflag=1;
				mv.y_prev=ch.y;
				ch.y=ch.y-20;
			}
			//前方ジャンプ
			if(mv.fjflag==1 && mv.jflag==1 )
			{
				mv.y_temp = ch.y;
				ch.y +=(ch.y-mv.y_prev)+1;
				mv.y_prev = mv.y_temp;
				ch.x+=(int)4*mv.move;
				if(ch.y==325)
				{
					mv.jflag=0;
					mv.fjflag=0;
					if(CheckStatePad(configpad.jump)>0 && CheckStatePad(configpad.right)==0)
					{//着地後ジャンプボタンが押されている状態なら連続でジャンプ
						mv.jflag=1;
						mv.y_prev=ch.y;
						ch.y=ch.y-20;
					}
					else if(CheckStatePad(configpad.jump)>0 && CheckStatePad(configpad.right)>0)
					{//着地後前方ジャンプボタンが押されている状態なら連続で前方ジャンプ
						mv.jflag=1;
						mv.fjflag=1;
						mv.y_prev=ch.y;
						ch.y=ch.y-20;
						ch.x+=(int)4*mv.move;
					}
				}
			}
			//前方ジャンプの最初の処理
			if(CheckStatePad(configpad.jump)>0 && CheckStatePad(configpad.right)>0 && mv.jflag==0)
			{
				mv.jflag=1;
				mv.fjflag=1;
				mv.y_prev=ch.y;
				ch.y=ch.y-20;
				ch.x+=(int)4*mv.move;
			}
			ch.img=(ch.cnt/motion->pitch)%motion->num;
			ch_control();
	}
		ch.cnt++;
}


ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#13

投稿記事 by ISLe » 12年前

完全なコードではないというのは、あのコード単体では解決できない識別子があってコンパイルエラーになるということです。

mj(水無月) さんが書きました:先にも書きましたがキーが同時押しされたときと単体で押されたときとを考慮してif文の中に
論理演算子とフラグを使用して処理を分けています。しかし、ボタン操作が増える度にif文の
中に条件を追加していくという方法はかなり非効率的に感じられます。
こちらに関しましても何か良いまとめ方等はございませんでしょうか。
各操作をフラグとして表現する値を定義して、状態ごとに受け付ける操作をテーブル化します。
最初に状態によって受け付けない操作を弾くようにすれば条件式をシンプルにできます。

ch_moveのジャンプ関連コードはとても無駄が多いように感じますし、操作系はcalc_chで処理すべきです。

calc_chでも『現在の状態』を活用してください。
「連続でジャンプ」とコメントされてるあたりの処理はわざわざ書かなくても連続ジャンプするようにできます。


共通化したコードをあちこちに複数配置してしまっているので共通化した意味がないです。

mj(水無月)
記事: 22
登録日時: 12年前
住所: 東京

Re: ボタン操作によるグラフィックの描画に関しまして

#14

投稿記事 by mj(水無月) » 12年前

> ISLeさん、ご返信ありがとうございます。
ISLe さんが書きました: 各操作をフラグとして表現する値を定義して、状態ごとに受け付ける操作をテーブル化します。
最初に状態によって受け付けない操作を弾くようにすれば条件式をシンプルにできます。
各操作をフラグ化として表現する値を定義するとありますが、こちらもグラフィック描画のときと同様に
前進(CheckStatePad(configpad.right)==1)が押されていたらフラグ1
後退(CheckStatePad(configpad.left)==1)が押されていたらフラグ2
ジャンプ(CheckStatePad(configpad.jump)==1)が押されていたらフラグ3
前方ジャンプ(CheckStatePad(configpad.jump)==1&&CheckStatePad(configpad.right)==1)が押されていたらフラグ4
というようにこのキーが押されていたらこのモーションのフラグを立てるということでしょうか。

あと、最近少々忙しいのでご返信に時間が掛かってしまうかもしれません。
できるだけ、早くご返信できるようにつとめますが何日かあいてしまったら申し訳ありません。

ym114

Re: ボタン操作によるグラフィックの描画に関しまして

#15

投稿記事 by ym114 » 12年前

入力,状態遷移,描画と分けて考えるべき問題がてんこもりです.一度整理してみましょう

どんなゲームでもプログラムでも,二大処理は更新(Update)と描画(Draw)です.
ですから,この二つは完全に独立させて考えます.まずは更新について考えてみましょう.

1フレームの更新の中で,プレーヤー操作対象は一度だけ入力を受け取り,必要ならば入力に応じた状態遷移をし,状態に応じた処理をします.
マリオの場合:
(1)Bと右が押されている
(2)地面にいればダッシュ,空中なら何もしない
(3)ダッシュ状態なら横方向に大きく移動,空中にいれば上昇なり落下なり.

これらを簡潔に記述するためには,これらを段階的に処理すべきです.
こうする場合,mjさんはch_move()を見直してみてください.

ここでは概念の説明だけをします.

1. 入力を受け取る
やりかたはいろいろありますが,基本は「フラグを立てる」考え方です.
ですが実はCheckStatePad関数が内部でフラグを立てているので既に解決済みです.

2. 状態を切り替える
現在の状態を保存する変数を用意して, 入力のフラグに応じて「状態」を切り替えます.
ISLeさんのいう
"状態ごとに受け付ける操作をテーブル化します。"
をswitch文で記述しています.(勘違いでなければ)

コード:

enum State { neutral, walk, jump };

State now_state = neutral

void SwitchState()
{
    switch(now_state){
    case neutral:
        if(CheckStatePad(configpad.right) > 0) 
            { now_state = walk; }
        if(CheckStatePad(configpad.A) > 0 )
            { now_state = jump;}
        break;
    case jump:
        // ジャンプ中は何も操作を受け付けない
        break;
  ...
    }
}
3. 状態に応じた移動をする

コード:

void Move()
{
    switch(state)
    {
    case neutral:
        break;
    case walk:
        ch.x += ch.move;
        break;
    //... 略
    }
    // 位置制御
    if(ch.x > MARGIN_X) { ch.x = MARGIN_X; }
}
と言った具合です.あとは
GetHitPadStateAll()
SwitchState()
Move()
の順でキャラを動かします.

ジャンプなど自動的に終了する状態は厄介です.ジャンプ終了後に何に切り替えるかを決めておく必要があります.
着地していたフレームで手動でnow_stateをneutralにするか,もう一度SwitchState()を呼び出してもいいでしょう.
後者の場合,着地後すぐに右に移動できます.
State::landing(着地中)といった専用の状態を経由してもいいでしょう.

あとは描画です.
上の例にはアニメーション用のカウントがありませんので,用意して1フレームごとに加算しておきます.
状態が変わるときにはアニメーションは1から再生するので,切り替える時にカウントをリセットします.
あとは既に完成しているますが,状態とカウントを画像に変換するテーブルを経由して取得するだけです.

状態遷移は入力に限らず発生します.(敵との衝突等)どうしても泥臭くなってしまいますが
とりあえずはこんな感じでいかがでしょうか

P.S. ちょうど入力とキャラクターの状態遷移についての質問を持っていたのですが,似た質問があるので私も一緒に考えました.

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#16

投稿記事 by ISLe » 12年前

例えばジャンプボタン押しっぱなしで連続ジャンプさせるだけなら以下のように書けます。

コード:

    if (graph_state != Jump && CheckStatePad( configpad.jump ) > 0)
    {
        graph_state = Jump;
        // その他初期設定
    }
「graph_state != Jump」の部分をビットマスクで判定すればシンプルになると思いましたけど、よく考えたら状態別に処理すれば良いだけですね。

わたしの提案は、『操作判定』>『状態判定』ですけど、条件式を伸ばしたくないだけであればym114さんの書かれたように『状態判定』>『操作判定』でもかまわないと思います。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#17

投稿記事 by ISLe » 12年前

もはやスレタイから完全に外れますが、アニメーションを除いたユーザー操作とキャラクターの挙動のサンプルコードを書いてみました。
mjさんの元のコードを詳しく見てないのですが、挙動としては同じかと思います。

構造体がchとmvに分かれている理由が不明なので勝手にまとめました。
メンバも要・不要で変更しました。

コード:

#include "DxLib.h"

enum {
	Neutral,
	Walk,
	BWalk,
	Jump
};

static int state = Neutral;

struct {
	int x;
	int y;
	int move;
	int vx;
	int y_prev;
	bool jumping;
} ch = { 320, 325, 1, 0, 0, false };

void graph_ch()
{
	DrawBox(ch.x,ch.y,ch.x+24,ch.y+32,GetColor(255,255,255),TRUE);
}

void calc_ch()
{
	if (CheckHitKey(KEY_INPUT_RIGHT) > 0) {
		switch (state) {
		case Jump:
			// 何もしない
			break;
		default:
			ch.vx = 2*ch.move;
			state = Walk;
		}
	}
	else {
		switch (state) {
		case Neutral:
		case Walk:
			ch.vx = 0;
			state = Neutral;
		}
	}
	if (CheckHitKey(KEY_INPUT_LEFT) > 0) {
		switch (state) {
		case Jump:
			// 何もしない
			break;
		default:
			ch.vx = -2*ch.move;
			state = BWalk;
		}
	}
	else {
		switch (state) {
		case Neutral:
		case BWalk:
			ch.vx = 0;
			state = Neutral;
		}
	}
	if (CheckHitKey(KEY_INPUT_UP) > 0) {
		switch (state) {
		case Walk:
			ch.vx = 4*ch.move;
			break;
		case BWalk:
			ch.vx = -4*ch.move;
			break;
		}
		if (state == Neutral || state == Walk || state == BWalk) {
			ch.jumping = false;
			state = Jump;
		}
	}
}

void ch_move()
{
	switch (state)
	{
	case Neutral:
	case Walk:
	case BWalk:
		ch.x += ch.vx;
		break;
	case Jump:
		if (!ch.jumping) {
			ch.y_prev=ch.y;
			ch.y=ch.y-20;
			ch.jumping = true;
		}
		else {
			int y_temp = ch.y;
			ch.y += ch.y-ch.y_prev+1;
			ch.y_prev = y_temp;
			if(ch.y >= 325) {
				ch.y = 325;
				state = Neutral;
			}
		}
		ch.x += ch.vx;
		break;
	}
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	ChangeWindowMode(TRUE);
	if (DxLib_Init() != 0) return 0;
	SetDrawScreen(DX_SCREEN_BACK);

	while (ProcessMessage() == 0 && ScreenFlip() == 0 && ClearDrawScreen() == 0) {
		calc_ch();
		ch_move();
		graph_ch();
	}

	DxLib_End();
	return 0;
}

mj(水無月)
記事: 22
登録日時: 12年前
住所: 東京

Re: ボタン操作によるグラフィックの描画に関しまして

#18

投稿記事 by mj(水無月) » 12年前

>またもや、ご返信のほうが遅くなってしまい申し訳ありません。

ym114さん、ご意見ありがとうございます。ISLe さんもわざわざサンプルコードまでご提供して頂き
ありがとうございます。
ISLe さんが提供してくださった今までのコードを元に実際にキャラクター描画処理を含めたものを
下記に記載致します。一応これで対応するキーを押下したときにニュートラル/前進/後退/ジャンプの
モーションを行うことができています。
唯一つ2段ジャンプを行う際の操作を、ジャンプ中にDキーを押したときとしていますがこちらは
ジャンプするときと同じ上キーに設定してしまうとどうやらうまく処理できないのでとりあえず別の
キーに設定してあります。

コード:

#include "DxLib.h"

	int img,cnt;

	static int motion1[] = 
	{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 };//ニュートラル
	static int motion2[] = 
	{ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37 };//前進
	static int motion3[] = 
	{ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 };//後退
	static int motion4[] = 
	{ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103 };//ジャンプ
	static int motion5[] = 
	{ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123 };//2段ジャンプ

	struct MotionTable 
	{
		int *index;
		int num;
		int pitch;
	}motion_table[];

	#define ARRAYNUM(ary) (sizeof(ary)/sizeof(ary[0]))
	static MotionTable motion_table[] = 
	{
		{ motion1, ARRAYNUM(motion1), 4 },
		{ motion2, ARRAYNUM(motion2), 7 },
		{ motion3, ARRAYNUM(motion3), 8 },
		{ motion4, ARRAYNUM(motion4), 2 },
		{ motion5, ARRAYNUM(motion5), 2 },
	};
	static MotionTable *motion = &motion_table[0];

enum 
{
    Neutral,
    Walk,
    BWalk,
    Jump,
	DJump
};
 
static int state = Neutral;
 
struct 
{
    int x;
    int y;
    int move;
    int vx;
    int y_prev;
    bool jumping;
} ch = { 165, 310, 1, 0, 0, false };
 
int graphics[150];
void load()
{
	LoadDivGraph( "mydat/dat/img/test.png" , 150 , 26 , 5 , 250 , 250 , graphics ) ;
}

void graph_ch()
{
    DrawRotaGraphF(ch.x,ch.y,1.0f,0.0f,graphics[motion->index[img]],TRUE);
}

void calc_ch()
{
    if (CheckHitKey(KEY_INPUT_RIGHT) > 0) 
	{
        switch (state) 
		{
			case Jump:
				// 何もしない
				break;
			default:
				ch.vx = 2*ch.move;
				state = Walk;
        }
    }
    else 
	{
        switch (state) 
		{
			case Neutral:
			case Walk:
				ch.vx = 0;
				state = Neutral;
        }
    }
    if (CheckHitKey(KEY_INPUT_LEFT) > 0) 
	{
        switch (state) 
		{
			case Jump:
				// 何もしない
				break;
			default:
				ch.vx = -2*ch.move;
				state = BWalk;
        }
    }
    else 
	{
        switch (state) 
		{
			case Neutral:
			case BWalk:
				ch.vx = 0;
				state = Neutral;
        }
    }
    if ((CheckHitKey(KEY_INPUT_UP)) == 1 ) 
	{
        switch (state) 
		{
			case Walk:
				ch.vx = 4*ch.move;
				break;
			case BWalk:
				ch.vx = -4*ch.move;
				break;
        }
        if (state == Neutral || state == Walk || state == BWalk) 
		{
			    ch.jumping = false;
				state = Jump;
        }
    }
	else if (state == Jump) 
	{
        if (CheckHitKey(KEY_INPUT_D) == 1) 
		{
			    ch.jumping = false;
				state = DJump;
        }
    }

	motion = &motion_table[state];
}
 
void ch_move()
{
    switch (state)
    {
		case Neutral:
		case Walk:
		case BWalk:
		    ch.x += ch.vx;
			img=(cnt/motion->pitch)%motion->num;
			break;
		case Jump:
			if (!ch.jumping) 
			{
				ch.y_prev=ch.y;
				ch.y=ch.y-17;
				ch.jumping = true;
			}
        else 
		{
			int y_temp = ch.y;
            ch.y += ch.y-ch.y_prev+1;
            ch.y_prev = y_temp;
            if(ch.y >= 310) 
			{
				ch.y = 310;
                state = Neutral;
            }
        }
        ch.x += ch.vx;
		img=(cnt/motion->pitch)%motion->num;
        break;
		case DJump:
			if (!ch.jumping) 
			{
				ch.y_prev=ch.y;
				ch.y=ch.y-17;
				ch.jumping = true;
			}
        else 
		{
			int y_temp = ch.y;
            ch.y += ch.y-ch.y_prev+1;
            ch.y_prev = y_temp;
            if(ch.y >= 310) 
			{
				ch.y = 310;
                state = Neutral;
            }
        }
        ch.x += ch.vx;
		img=(cnt/motion->pitch)%motion->num;
        break;
    }
	cnt++;
}
 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    ChangeWindowMode(TRUE);
    if (DxLib_Init() != 0) return 0;
    SetDrawScreen(DX_SCREEN_BACK);
	int func_state=0;
    while (ProcessMessage() == 0 && ScreenFlip() == 0 && ClearDrawScreen() == 0) 
	{
		switch(func_state)
		{
			case 0:
					load();
					func_state=10;
					break;
			case 10:
					calc_ch();
					ch_move();
					graph_ch();
					break;
			default:
					printfDx("不明なfunc_state\n");
					break;
		}
    }
 
    DxLib_End();
    return 0;
}

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#19

投稿記事 by ISLe » 12年前

mj(水無月) さんが書きました:唯一つ2段ジャンプを行う際の操作を、ジャンプ中にDキーを押したときとしていますがこちらは
ジャンプするときと同じ上キーに設定してしまうとどうやらうまく処理できないのでとりあえず別の
キーに設定してあります。
それはジャンプボタンを押しているかどうかで判定しているからです。
押した(瞬間)かどうかで判定すれば同じキーでいけます。

わたしの書いたコードは単体でコンパイルできるようにCheckHitKeyに変えましたが、
if (CheckStatePad(configpad.jump) > 0) { /* ジャンプボタンが押されている */ }
if (CheckStatePad(configpad.jump) == 1) { /* ジャンプボタンが押された */ }
と使い分けができるはずです。

mj(水無月)
記事: 22
登録日時: 12年前
住所: 東京

Re: ボタン操作によるグラフィックの描画に関しまして

#20

投稿記事 by mj(水無月) » 12年前

>ISLe さん、ご返信ありがとうございます。

コードを修正してみましたがやはり2段ジャンプキーが反応しません。2段ジャンプキーを別のキーに
設定したときはちゃんと2段ジャンプができることを確認しています。
下記のコードで他にどこか修正しなけらばならないところがあるのでしょうか。

コード:

    if (CheckStatePad(configpad.jump) > 0) 
	{
        switch (state) 
		{
			case Walk:
				ch.vx = 4*ch.move;
				break;
			case BWalk:
				ch.vx = -4*ch.move;
				break;
        }
        if (state == Neutral || state == Walk || state == BWalk) 
		{
			    ch.jumping = false;
				state = Jump;
        }
    }
	else if (state == Jump) 
	{
        if (CheckStatePad(configpad.jump) == 1) 
		{
			    ch.jumping = false;
				state = DJump;
        }
    }

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#21

投稿記事 by ISLe » 12年前

二段ジャンプ着地後、ジャンプボタン押しっぱなしで初段ジャンプするという現在の仕様に倣うなら以下のようになります。
ボタン判定→状態判定の流れで統一します。
流れは逆でもかまいませんが統一しないと論理バグを生みやすくなります。

コード:

    if (CheckStatePad(configpad.jump) == 1) // 0より大きいに含まれるので先に判定
    {
        if (state == Jump) 
        {
            ch.jumping = false;
            state = DJump;
        }
    } 
    else if (CheckStatePad(configpad.jump) > 0) 
    {
        switch (state) 
        {
        case Walk:
            ch.vx = 4*ch.move;
            break;
        case BWalk:
            ch.vx = -4*ch.move;
            break;
        }
        if (state == Neutral || state == Walk || state == BWalk) 
        {
            ch.jumping = false;
            state = Jump;
        }
    }
一般的には初段ジャンプも押しっぱなしではジャンプしないゲームがほとんどかと。
その場合はこちら。

コード:

    if (CheckStatePad(configpad.jump) == 1) 
    {
        switch (state) 
        {
        case Walk:
            ch.vx = 4*ch.move;
            break;
        case BWalk:
            ch.vx = -4*ch.move;
            break;
        }
        if (state == Neutral || state == Walk || state == BWalk) 
        {
            ch.jumping = false;
            state = Jump;
        }
        else if (state == Jump)
        {
            ch.jumping = false;
            state = DJump;
        }
    }
状態はswitchでまとめてしまったほうが良いかもです。

mj(水無月)
記事: 22
登録日時: 12年前
住所: 東京

Re: ボタン操作によるグラフィックの描画に関しまして

#22

投稿記事 by mj(水無月) » 12年前

>ISLe さん
最初に2段ジャンプの判定を行ってから最初のジャンプ判定を行うんですね。
if (CheckStatePad(configpad.jump) == 1)とelse if (CheckStatePad(configpad.jump) > 0)の
意味がようやく理解できました。ありがとうございます。
ISLe さんのこれまでの説明でキーを押されたときのグラフィックの描画方法も概ね理解することが
できました。
これまでにご提供頂いたコードでは設定されたキーが押されていている間(もしくは押されたとき)にモーション番号の
テーブルを呼び出し画像を順番に表示させ、端まで行ったらはじめに戻るといったようにループさせてアニメーションを
行うという方法はとても参考になりました。
しかし、今少し困っていることがありまして私が使用しているアニメーション画像は端から端まで画像をループさせて
アニメーションさせるだけではうまくアニメーションが成立しないということです。
私が使用している画像では1ループの中で最初のループにだけ描画させて次のループでは描画してはいけない
ものが含まれているため画像を同じようにループさせて描画したらアニメーションが破綻していまいます。

前進/しゃがむ/ジャンプを例にあげますと、

コード:

	static int motion1[] = 
	{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 };//ニュートラル
	static int motion2[] = 
	{ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37 };//前進
	static int motion3[] = 
	{ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 };//後退
	static int motion4[] = 
	{ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102 };//しゃがむ
	static int motion5[] = 
	{ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128 };//ジャンプ
	static int motion6[] = 
	{ 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149 };//2段ジャンプ
まず、これまでのようにmotion1~motion6の配列に画像が格納されているとします。
前進アニメーション:
26,27は振り向きモーション、28~37は歩きモーション。右キーを押されたとき最初のループは26~37が描画されて
次ループ以降からは歩きモーションの28~37の画像だけを描画させる。

しゃがむアニメーション:
78~82はしゃがむ過程、83~96はしゃがんでいる状態、97~102は立ち上がるまでの過程。
下キーを押しているときは最初のループで78~96を描画させて次ループからは下キーが押されている間は
83~96をループさせる。そして下キーが離されたら97~102の立ち上がりアニメーションが行われる。

ジャンプアニメーション:
上昇時は130~133、一番高い座標に到達したら134~140回転、下降時は141~143、着地時は144~147、
立ち上がり148、149。
ここらへんをうまく制御しないと2段ジャンプ時地面に着地するアニメーションがうまく成立してくれません。

このようにアニメーションを行う際、モーションテーブル内の描画させる画像を制御したい場合はどのような処理を
行えばよいのでしょうか。
Switch文の中に処理を追加するのも手だとは思いますがそうするとcase内の処理が複雑になってしまうので
別に制御用の関数等を作成するべきでしょうか。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#23

投稿記事 by ISLe » 12年前

複雑なアニメーションをするためにはアニメーションカウンタを増やしてアニメーション専用の処理を追加してください。
アニメーションのシーケンスを制御することになるので段階が増えます。

とりあえずテーブルデータとしてはこんな感じになると思います。
このとおりでなくてもかまいません。
#インデックスの中にシーケンス制御用のコマンドを埋め込む形にする方法もあります。
#それだと簡単ないわゆるスクリプトの実装をすることになります。
#テーブルの段階も増えませんし、終端をデータの中に埋め込めるので配列サイズを求める必要がなかったり、メリットはたくさんあります。

コード:

	static int motion1[] = 
	{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; // ニュートラル
	static int motion2_1[] = 
	{ 26, 27 }; // 前進 振り向き
	static int motion2_2[] = 
	{ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37 }; // 前進 歩き

	struct MotionSerialTable
	{
		int *index;
		int num;
		int pitch;
		bool loop;
	};
	struct MotionTable 
	{
		MotionSerialTable *motion;
	};

	#define ARRAYNUM(ary) (sizeof(ary)/sizeof(ary[0]))
	static MotionSerialTable motion_serial1[] = 
	{
		{ motion1, ARRAYNUM(motion1), 4, true  },
	};
	static MotionSerialTable motion_serial2[] = 
	{
		{ motion2_1, ARRAYNUM(motion2_1), 7, false },
		{ motion2_2, ARRAYNUM(motion2_2), 7, true  },
	};
	static MotionTable motion_table[] = 
	{
		motion_serial1,
		motion_serial2,
	};
キーを離したらしゃがみから復帰するといったところは『状態』を分ける必要があるかと思います。
アニメーション処理の中でキー入力を直接監視するようなことをしてはいけません。

ジャンプも同じく状態を細かく分ける必要があると思います。
『状態』の解像度が大きく異なってしまうので、挙動としての『状態』とアニメーションの『状態』を別個にすべきかと思います。
アニメーションのシーケンスを『状態』と見ることもできますが。

できればもっと明確に挙動とアニメーションを分離して、ch_moveとは別にアニメーション専用の関数を作ると良いでしょう。
calc_chではアニメーションの再生開始ポイントで再生指示を出す関数を呼ぶという形になるのが理想的です。

mj(水無月)
記事: 22
登録日時: 12年前
住所: 東京

Re: ボタン操作によるグラフィックの描画に関しまして

#24

投稿記事 by mj(水無月) » 12年前

ISLe>
ISLeさん、現在ご提供頂いたコードで色々と検証を行っています。グラフィックを描画するときなのですが
以前はMotionTable motion_table[]内に構造体配列がおさめられていたためmotion->index[ch.img]
のみでアクセスが可能でした。しかし今回はserial毎にアクセス先が相違するため描画時にmotion_serial1->、
motion_serial2->とモーション毎にアクセス先を指定しなければなりません。こちらに関しまして前回の
DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,graphics[motion->index[ch.img]],TRUE);
ようにうまく短いコードでアクセス先をまとめる方法はございませんでしょうか。

また、下記は前進/後退/しゃがむ/ジャンプをテーブルデータにしたものです。画像データをこの配列通りに
並べかえるのに少々手間取りました。こちらに関しましてモーションデータが増える度にmotion_serialが
増えてしまいますが ISLeさんがおっしゃっていたシーケンス制御用のコマンドを使用することでもっと完結に
表現することが可能でしょうか。

コード:

	static int motion1[] = 
	{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 };//ニュートラル
	static int motion2_1[] = 
	{ 26, 27 }; // 前進 振り向き
	static int motion2_2[] = 
	{ 28, 29, 30, 31, 32, 33, 34, 35, 37, 38 }; // 前進 歩き
	static int motion3[] = 
	{ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 };//後退
	static int motion4_1[] = 
	{ 78, 79, 80, 81, 82 };//しゃがむ 前傾
	static int motion4_2[] =
	{ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95 };//じゃがむ 座る
	static int motion4_3[] =
	{ 96, 97, 98, 99, 100, 101 };//じゃがむ 立ち上がり
	static int motion5_1[] = 
	{ 104, 105, 106, 107, 108, 109 };//ジャンプ 飛び
	static int motion5_2[] =
	{ 110, 111, 112, 113 };//ジャンプ 上昇
	static int motion5_3[] =
	{ 114, 115, 116, 117, 118, 119, 120 };//ジャンプ 回転
	static int motion5_4[] =
	{ 121, 122, 123 };//ジャンプ 下降
	static int motion5_5[] =
	{ 124, 125, 126, 127 };//ジャンプ 着地
	static int motion5_6[] =
	{ 128, 129 };//ジャンプ 立ち上がり
	static int motion6_1[] = 
	{ 130, 131, 132, 133 };//2段ジャンプ 上昇
	static int motion6_2[] = 
	{ 134, 135, 136, 137, 138, 139, 140 };//2段ジャンプ 回転
	static int motion6_3[] = 
	{ 141, 142, 143 };//2段ジャンプ 下降
	static int motion6_4[] = 
	{ 144, 145, 146, 147 };//2段ジャンプ 着地
	static int motion6_5[] = 
	{ 148, 149 };//2段ジャンプ 立ち上がり

	struct MotionSerialTable 
	{
		int *index;
		int num;
		int pitch;
		bool loop;
	};
	struct MotionSerialTable motion_serial1[];
	struct MotionSerialTable motion_serial2[];
	struct MotionSerialTable motion_serial3[];
	struct MotionSerialTable motion_serial4[];
	struct MotionSerialTable motion_serial5[];
	struct MotionSerialTable motion_serial6[];

	struct MotionTable
	{
		MotionSerialTable *motion;
	}motion_table[];

	#define ARRAYNUM(ary) (sizeof(ary)/sizeof(ary[0]))
	static MotionSerialTable motion_serial1[] = 
	{
		{ motion1, ARRAYNUM(motion1), 4, true  },		
	};
	static MotionSerialTable motion_serial2[] =
	{
		{ motion2_1, ARRAYNUM(motion2_1), 7, false },
        { motion2_2, ARRAYNUM(motion2_2), 7, true  },
	};
	static MotionSerialTable motion_serial3[] =
	{
		{ motion3, ARRAYNUM(motion3), 8, true  },
	};
	static MotionSerialTable motion_serial4[] =
	{
		{ motion4_1, ARRAYNUM(motion4_1), 4, false  },
		{ motion4_2, ARRAYNUM(motion4_2), 4, true  },
		{ motion4_3, ARRAYNUM(motion4_3), 4, true  },
	};
	static MotionSerialTable motion_serial5[] =
	{
		{ motion5_1, ARRAYNUM(motion5_1), 2, false  },
		{ motion5_2, ARRAYNUM(motion5_2), 2, true  },
		{ motion5_3, ARRAYNUM(motion5_3), 2, true  },
		{ motion5_4, ARRAYNUM(motion5_4), 2, true  },
		{ motion5_5, ARRAYNUM(motion5_5), 2, true  },
		{ motion5_6, ARRAYNUM(motion5_6), 2, true  },
	};
	static MotionSerialTable motion_serial6[] =
	{
		{ motion6_1, ARRAYNUM(motion6_1), 2, false  },
		{ motion6_2, ARRAYNUM(motion6_2), 2, true  },
		{ motion6_3, ARRAYNUM(motion6_3), 2, true  },
		{ motion6_4, ARRAYNUM(motion6_4), 2, true  }
	};
	static MotionTable motion_table[] = 
    {
        motion_serial1,
        motion_serial2,
		motion_serial3,
		motion_serial4,
		motion_serial5,
		motion_serial6,
    };

	static MotionTable *motion_serial = &motion_table[0];

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#25

投稿記事 by ISLe » 12年前

103行目を
static MotionSerialTable *motion_serial = motion_table[0].motion;
にして
motion_serial->index[ch.img]
のように使います。

ループしないテーブル項目で最終要素を超えたときは、motion_serial++でポインタを進め、ch.cntをリセットします。

ループするテーブル項目は、その先へ進むことがないので、最後にひとつしか置けません。


これほど複雑になると、制御コマンドを埋め込めるようにしても、コード変更の手間はループの開始位置で配列を分けなくても済む程度のものです。
アニメーションデータ作成用のツールプログラムを作ったほうが良いでしょう。

インデックスデータは連続したひとつの配列として扱い、どのモーションが何番目の要素からというふうに指定します。

これが

コード:

    static MotionSerialTable motion_serial1[] = 
    {
        { motion1, ARRAYNUM(motion1), 4, true  },       
    };
このような形になります。

コード:

    static MotionSerialTable motion_serial1[] = 
    {
        { &motion[N], 4, true  },
    };
実際にはMotionSerialTableのデータもツールで作成します。
ツールで出力するデータにポインタは使えないので、要素番号からアドレスを求めることになります。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#26

投稿記事 by ISLe » 12年前

アニメーションデータ作成用のツールプログラムというのは例えば…

このようなテキストファイルを読み込んで、

コード:

----
 4 true  0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // ニュートラル
----
 7 false 26 27 // 前進 振り向き
 7 true  28 29 30 31 32 33 34 35 37 38 // 前進 歩き
このようなテキストファイルに変換して出力するだけのプログラムでもコードを書き換える手間はずいぶん省けます。

コード:

static int motion1[] = 
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; // ニュートラル
static int motion2_1[] = 
{ 26, 27 }; // 前進 振り向き
static int motion2_2[] = 
{ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37 }; // 前進 歩き

static MotionSerialTable motion_serial1[] = 
{
    { motion1, ARRAYNUM(motion1), 4, true  },
};
static MotionSerialTable motion_serial2[] = 
{
    { motion2_1, ARRAYNUM(motion2_1), 7, false },
    { motion2_2, ARRAYNUM(motion2_2), 7, true  },
};
MotionTable motion_table[] = 
{
    motion_serial1,
    motion_serial2,
};

mj(水無月)
記事: 22
登録日時: 12年前
住所: 東京

Re: ボタン操作によるグラフィックの描画に関しまして

#27

投稿記事 by mj(水無月) » 12年前

ISLeさん、ありがとうございます。おかげでグラフィックのほうはうまく描画することができました。
しかし、アニメーションのほうが上手く制御できていないようでmotion2_1からmotion2_2へと
うまく繋がってくれません。おそらく前回ISLeさんが記載してくれた
ISLe さんが書きました: ループしないテーブル項目で最終要素を超えたときは、motion_serial++でポインタを進め、ch.cntをリセットします。
こちらをうまく利用できていないからだと思います。アニメーションを制御する際[motion_serial++]は
どのように活用すればよいのでしょうか。

アニメーションデータ作成用のツールプログラム方はとりあえず今のボタンを押したときの挙動を
うまく制御できてから実装を試みたいと思います。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#28

投稿記事 by ISLe » 12年前

現在のコードでどのようにイメージ番号を求めているか理解されているでしょうか。

現在はこのようになっていると思います。

コード:

ch.img = (ch.cnt/motion_serial->pitch)%motion_serial->num;
ch.cnt++;
ループしない場合は、イメージ番号テーブルの最終要素を超えようとした時点で次のテーブルに移行させます。

コード:

if (!motion_serial->loop) {
	// ループしない場合
	int idx = ch.cnt/motion_serial->pitch;
	if (idx >= motion_serial->num) {
		// イメージ番号の最終要素を超える
		motion_serial++; // テーブルを進める
		ch.cnt = 0; // カウンタをリセット
	}
}
ch.img = (ch.cnt/motion_serial->pitch)%motion_serial->num;
ch.cnt++;
分かりやすさ優先でループしない場合が目立つ形にしてみました。
個人的にはもっと整理したいところです。
割り算の重複を無くしたいというか割り算そのものを無くしたい。

mj(水無月)
記事: 22
登録日時: 12年前
住所: 東京

Re: ボタン操作によるグラフィックの描画に関しまして

#29

投稿記事 by mj(水無月) » 12年前

>ISLeさん
すみません、motion_serial++というよりは新しく追加したloopの使い方を間違えていたようです。
こちらISLeさんのコードを元に書いてみました。テーブルデータは以前書いたものを使用しています。
(割り算をなくしたいとありましたが割り算を使わずにコマの切り替わるスピードを制御することは
可能なのでしょうか?)
motion_serial++でmotion2_1[]からmotion2_2[]へと進んだ際、Walk2へと状態が遷移するように
したのですがどうもアニメーションがちらついてしまいます。motion2_2[]にテーブルが移動したときように
列挙型を増やしたのですがやり方的にはこのような形でよろしいでしょうか。
(Jumpに関してはとりあえず設定しているだけです。)

コード:

enum 
{
    Neutral,
    Walk,
	Walk2,
    BWalk,
    Jump,
};

static int state = Neutral;

void graph_ch()
{
    DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,graphics[motion_serial->index[ch.img]],TRUE);
}

void calc_ch()
{
    if (CheckStatePad(configpad.right) > 0) 
	{
        switch (state) 
		{
			case Jump:
				// 何もしない
				break;
			case Walk2:
				ch.vx = 2*ch.move;
				break;
			default:
				ch.vx = 2*ch.move;
				state = Walk;
        }
    }
    else 
	{
        switch (state) 
		{
			case Neutral:
			case Walk2:
				ch.vx = 0;
				state = Neutral;
			case Walk:
				ch.vx = 0;
				state = Neutral;
        }
    }
    if (CheckStatePad(configpad.left) > 0) 
	{
        switch (state) 
		{
			case Jump:
				// 何もしない
				break;
			default:
				ch.vx = -2*ch.move;
				state = BWalk;
        }
    }
    else 
	{
        switch (state) 
		{
			case Neutral:
			case BWalk:
				ch.vx = 0;
				state = Neutral;
        }
    }
	motion_serial = motion_table[state].motion;
}
void ch_move()
{
    switch (state)
    {
		case Neutral:
			ch.img = (ch.cnt/motion_serial->pitch)%motion_serial->num;
			break;
		case Walk:
			ch.x += ch.vx;
			if (!motion_serial->loop) 
			{
				// ループしない場合
				int idx = ch.cnt/motion_serial->pitch;
				if (idx >= motion_serial->num) 
				{
					// イメージ番号の最終要素を超える
					motion_serial++; // テーブルを進める
					state = Walk2;
					ch.cnt = 0; // カウンタをリセット
				}
			}
			ch.img = (ch.cnt/motion_serial->pitch)%motion_serial->num;
			break;
		case Walk2:
		case BWalk:
		    ch.x += ch.vx;
			ch.img = (ch.cnt/motion_serial->pitch)%motion_serial->num;
			break;
    }
	ch.cnt++;
}

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#30

投稿記事 by ISLe » 12年前

calc_ch関数で、毎フレーム、現在の状態のテーブルの先頭をmotion_serialにセットしているので進めても元に戻ります。
状態が変化するときセットされるようにしてください。


モーションテーブルの仕様を理解してその仕様に合わせてコードを変更してください。
現在のコードに合わせてモーションテーブル処理を無理矢理組み込もうとすれば、設計レベルで滅茶苦茶になってしまって手が付けられません。

ch_move関数で、ch.imgを求める処理は、状態別に分けないでください。
テーブルの形式を共通にした意味がありません。

ch_move関数で、状態を変化させてはいけません。
状態変化を管理するのはcalc_ch関数の仕事です。

状態とテーブルは一対一でなければいけません(仕様)。
motion_serialをインクリメントして状態を変化させるようなことをすれば後に大きなバグに繋がる可能性があります。
状態がWalk2に変化する場合も、他の状態と同じように改めてテーブルをリセットしてください。

余計なものが見えないようにソースファイルを分けたほうが良いかもしれません。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#31

投稿記事 by ISLe » 12年前

mj(水無月) さんが書きました:(割り算をなくしたいとありましたが割り算を使わずにコマの切り替わるスピードを制御することは
可能なのでしょうか?)
例えばこれを、

コード:

ch.img = (ch.cnt/motion_serial->pitch)%motion_serial->num;
ch.cnt++;
こういうふうにします。

コード:

if (ch.cnt >= motion_serial->pitch) {
    ch.cnt = 0;
    ch.img++;
    if (ch.img >= motion_serial->num) {
        ch.img = 0;
    }
}
ch.cnt++;
初期設定が必須ですがテーブルの切り替えにはどのみち必須ですからね。
やり方を変えないほうがバグ混入を防ぐことになりますし。

単純ループで除算と剰余算を使うと便利なのは、それしかできないから、です。
それ以外のことをやろうとすれば一から作り直すことになります。
そうなればとうぜんバグもたくさん生まれます。

先にしっかり方法を検討してそのやり方を通すようにすればバグは生まれにくくなります。

mj(水無月)
記事: 22
登録日時: 12年前
住所: 東京

Re: ボタン操作によるグラフィックの描画に関しまして

#32

投稿記事 by mj(水無月) » 12年前

>ISLeさん
お世話になってます。こちらプログラムを修正いたしました。これでとりあえずは前進/後退/しゃがむが
正常に行えています。ISLeさんにご教授頂いたテーブルを進めて画像を描画する部分は少し自分なりに
変更してみました。これで一応はうまく実行はできていますがいかがでしょうか。

コード:

	static int motion1[] = 
	{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 };//ニュートラル
	static int motion2_1[] = 
	{ 26, 27 }; // 前進 振り向き
	static int motion2_2[] = 
	{ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37 }; // 前進 歩き
	static int motion3[] = 
	{ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 };//後退
	static int motion4_1[] = 
	{ 78, 79, 80, 81, 82 };//しゃがむ 前傾
	static int motion4_2[] =
	{ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95 };//じゃがむ 座る
	static int motion4_3[] =
	{ 96, 97, 98, 99, 100, 101 };//しゃがむ 立ち上がり

	struct MotionSerialTable 
	{
		int *index;
		int num;
		int pitch;
		bool loop;
	};
	
	struct MotionSerialTable motion_serial1[];
	struct MotionSerialTable motion_serial2[];
	struct MotionSerialTable motion_serial3[];
	struct MotionSerialTable motion_serial4[];

	struct MotionTable
	{
		MotionSerialTable *motion;
	}motion_table[];

	#define ARRAYNUM(ary) (sizeof(ary)/sizeof(ary[0]))

	static MotionSerialTable motion_serial1[] = 
	{
		{ motion1, ARRAYNUM(motion1), 4, true  },		
	};

	static MotionSerialTable motion_serial2[] =
	{
		{ motion2_1, ARRAYNUM(motion2_1), 7, false },
        { motion2_2, ARRAYNUM(motion2_2), 7, true  },
	};
	static MotionSerialTable motion_serial3[] = 
	{
		{ motion3, ARRAYNUM(motion3), 7, true  },		
	};
	static MotionSerialTable motion_serial4[] =
	{
		{ motion4_1, ARRAYNUM(motion4_1), 3, false  },
		{ motion4_2, ARRAYNUM(motion4_2), 4, false  },
		{ motion4_3, ARRAYNUM(motion4_3), 3, true  },
	};
	static MotionTable motion_table[] = 
    {
        motion_serial1,
        motion_serial2,
		motion_serial3,
		motion_serial4,
    };

	static MotionSerialTable *motion_serial = motion_table[0].motion;

enum 
{
    Neutral,
    Walk,
    BWalk,
	Crouch,
    Jump,
};

//現在の状態
static int state = Neutral;
//1つ前の状態
static int prev_state = Neutral;

void graph_ch()
{
    DrawRotaGraphF(ch.x+FIELD_X,ch.y+FIELD_Y,1.0f,0.0f,graphics[motion_serial->index[ch.img]],TRUE);
}

void calc_ch()
{
    if (CheckStatePad(configpad.right) > 0) 
	{
        switch (state) 
		{
			case Jump:
				// 何もしない
				break;
			default:
				ch.vx = 3*ch.move;
				state = Walk;
        }
    }
    else 
	{
        switch (state) 
		{
			case Neutral:
			case Walk:
				ch.vx = 0;
				state = Neutral;
        }
    }
    if (CheckStatePad(configpad.left) > 0) 
	{
        switch (state) 
		{
			case Jump:
				// 何もしない
				break;
			default:
				ch.vx = -2*ch.move;
				state = BWalk;
        }
    }
    else 
	{
        switch (state) 
		{
			case Neutral:
			case BWalk:
				ch.vx = 0;
				state = Neutral;
        }
    }
	if (CheckStatePad(configpad.down) == 1)
	{
        switch (state) 
		{
			case Jump:
				// 何もしない
				break;
			default:
				ch.vx = 0*ch.move;
        }
        if (state == Neutral || state == Walk || state == BWalk) 
        {
            state = Crouch;
        }
    }
	else if (CheckStatePad(configpad.down) > 0)
	{
		if (state == Crouch)
		{	//下キーが押されている間、テーブルが進まないようにする
			motion_serial4[1].loop = true;
		}
		if (state == Neutral || state == Walk || state == BWalk) 
        {
            state = Crouch;
        }
	}
    else 
	{	//下キーが押されていなかったら、loopをセットしなおす
		motion_serial4[1].loop=false;
    }
	if(state!=prev_state)
	{
		prev_state=state; // 変更前の状態を保存
		ch.cnt = 0;	// カウンタをリセット
		motion_serial = motion_table[state].motion;
	}
}
void ch_move()
{
    switch (state)
    {
		case Neutral:
		case Walk:
		case BWalk:
			ch.x += ch.vx;
			break;
		case Crouch:
			if(!ch.crouch)
			{
				ch.x += ch.vx;
				ch.crouch = true;
			}
			else
			{
				if(motion_serial->index[ch.img] == 101)
				{
					ch.crouch = false;
					state = Neutral;
				}
			}
			break;
    }
}

void ch_table()
{
	if (!motion_serial->loop) 
	{
		// ループしない場合
		int idx = (ch.cnt/motion_serial->pitch)%motion_serial->num;
		if(ch.ms_flag)
		{
			// イメージ番号の最終要素を超える
			motion_serial++; // テーブルを進める
			ch.cnt = 0; // カウンタをリセット
			ch.ms_flag= 0;	//テーブルフラグをリセット
		}
	    else if (idx >= (motion_serial->num-1)) 
		{
			ch.ms_flag = 1;	//テーブルを進めるフラグセット
	    }
	}
	ch.img = (ch.cnt/motion_serial->pitch)%motion_serial->num;
	ch.cnt++;
}

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#33

投稿記事 by ISLe » 12年前

mj(水無月) さんが書きました:これで一応はうまく実行はできていますがいかがでしょうか。
謎のコードが紛れ込んでますが、期待通りに動作しているのであれば良いのではないでしょうか。
わたしの提示したコードをよく見れば、苦し紛れのテーブルを進めるフラグは必要なくなるのではないでしょうか。

個人的には、テーブルを書き換えたり状態遷移をテーブルの内容に依存させたりせずに、きっちりアニメーション関連を分離したいところです。
手作業での限界も近いのでこれ以上やるなら作業の自動化も含めて考える必要があるでしょう。

mj(水無月)
記事: 22
登録日時: 12年前
住所: 東京

Re: ボタン操作によるグラフィックの描画に関しまして

#34

投稿記事 by mj(水無月) » 12年前

>ISLeさん
ISLeさんのおっしゃった通り、コードを良く検証したところ下記のようにすればテーブルを
進めるフラグを立てることなく普通にテーブルを進めても正常に描画でるようになりました。

コード:

void ch_table()
{
	if (ch.cnt >= motion_serial->pitch) 
	{			
		ch.cnt = 0;
		ch.img++;
		if (!motion_serial->loop) 
		{
			if(ch.img >= motion_serial->num)
			{
				//イメージ番号の最終要素を超える
				motion_serial++; // テーブルを進める
				ch.cnt = 0; // カウンタをリセット
				ch.img = 0;	// 画像番号リセット
			}
		}
		if (ch.img >= motion_serial->num)
		{
			ch.img = 0;
		}
	}
	ch.cnt++;
}
これまでにISLeさんにお教え頂いたコードで大体のボタン操作によるキャラクターモーションの制御の
仕方は理解できてきました。(まだ、いろいろと改良しなければならない部分は多々ありますが)
今後、jump/dash/attack等のモーションを増やした場合void calc_ch()内に状態を制御する
コードを追加していこうと考えていたのですがISLeさんのおっしゃる作業の自動化とはここら辺の
処理をもっと効率的に行っていく方法があるということでしょうか。
手作業での限界も近いのでこれ以上やるなら作業の自動化も含めて考える必要があるでしょう。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#35

投稿記事 by ISLe » 12年前

mj(水無月) さんが書きました:ISLeさんのおっしゃった通り、コードを良く検証したところ下記のようにすればテーブルを
進めるフラグを立てることなく普通にテーブルを進めても正常に描画でるようになりました。
割り算を使わないコードにしなくても良かったのですが。

わたしの提示したコード

コード:

    // ループしない場合
    int idx = ch.cnt/motion_serial->pitch;
mj(水無月)さんのコード

コード:

        // ループしない場合
        int idx = (ch.cnt/motion_serial->pitch)%motion_serial->num;
いったん提示したとおり書いていたのに、書き換えるとこうなってしまうのはなぜか純粋に興味あります。

mj(水無月) さんが書きました:今後、jump/dash/attack等のモーションを増やした場合void calc_ch()内に状態を制御する
コードを追加していこうと考えていたのですがISLeさんのおっしゃる作業の自動化とはここら辺の
処理をもっと効率的に行っていく方法があるということでしょうか。
前に書いたツールプログラムの話のことです。

mj(水無月)
記事: 22
登録日時: 12年前
住所: 東京

Re: ボタン操作によるグラフィックの描画に関しまして

#36

投稿記事 by mj(水無月) » 12年前

いったん提示したとおり書いていたのに、書き換えるとこうなってしまうのはなぜか純粋に興味あります。
すみません。こちらに関しましてはISLeさんの提供してくださったコードを導入したところボタン操作時の
アニメーションが思うように描画されていなかったため、自分なりに原因を探るために書き換えたものです。
主にフラグを立てたりしていたのはテーブルの終端が読みこまれたときその画像を描画せずに即座に次の
テーブルへ移動してしまうという不具合が発生していたためです。
その辺りの不具合は私がvoid calc_ch()を編集時のコードの記述ミスによるものだったと思われます。
現在はvoid ch_table()を下記のように変更しても割り算を使用しないプログラムと同じ挙動となることを
確認致しました。

コード:

void ch_table()
{
	if (!motion_serial->loop) 
	{
	    // ループしない場合
	    int idx = ch.cnt/motion_serial->pitch;
		if (idx >= motion_serial->num) 
		{
		    // イメージ番号の最終要素を超える
		    motion_serial++; // テーブルを進める
			ch.cnt = 0; // カウンタをリセット
		}
	}
	ch.img = (ch.cnt/motion_serial->pitch)%motion_serial->num;
	ch.cnt++;
}
ISLeさんが以前お教えくださったアニメーションデータ作成用ツールプログラムはモーションテーブルの
データをプログラム内に記述せずに外部ファイルから読み込ませるという内容だと思われます。こちらは
テーブル以外のボタン操作のプログラムに関しましてもテーブルデータのように外部ファイルに記述された
ものを読み込ませるという方法が利用できるということでしょうか。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: ボタン操作によるグラフィックの描画に関しまして

#37

投稿記事 by ISLe » 12年前

mj(水無月) さんが書きました:ISLeさんが以前お教えくださったアニメーションデータ作成用ツールプログラムはモーションテーブルの
データをプログラム内に記述せずに外部ファイルから読み込ませるという内容だと思われます。こちらは
テーブル以外のボタン操作のプログラムに関しましてもテーブルデータのように外部ファイルに記述された
ものを読み込ませるという方法が利用できるということでしょうか。
現状の配列の初期値だけを記述したようなテキストファイルから、ソースファイルに変換するプログラムを作るだけでも効率が良い、ということも書きました。
PerlとかRubyが使えれば簡単なんですけど、C/C++で作ることもできます。

どういうフォーマットにすれば良いかは、どういうアニメーションを実装したいかによるので、mj(水無月)さんしか分かりませんが、いちいち配列に名前を付けて、付けた配列の名前を並べて、といった煩雑な作業からは解放されます。
MotionTable motion_table[]以外は直接参照しないのでどんな名前でもかまわいないですからツールで適当に生成させることができます。

mj(水無月)
記事: 22
登録日時: 12年前
住所: 東京

Re: ボタン操作によるグラフィックの描画に関しまして

#38

投稿記事 by mj(水無月) » 12年前

どういうフォーマットにすれば良いかは、どういうアニメーションを実装したいかによるので、mj(水無月)さんしか分かりませんが、いちいち配列に名前を付けて、付けた配列の名前を並べて、といった煩雑な作業からは解放されます。
MotionTable motion_table[]以外は直接参照しないのでどんな名前でもかまわいないですからツールで適当に生成させることができます。
なるほど、プログラムの効率化に関してはやはりツール作成の知識が必要となるのですね。
どうやらその辺りの知識はまだ、勉強不足のようなので改めて学習してみることに致します。
とりあえずは当初のボタン操作によるグラフィックの描画に関しましてはどうにか理解することができました。
あとは、自分なりに色々と試していこうと思います。

なので、本トピックはこれにて解決と致します。ISLeさん、本当に長くなってしまいました今日までお付き合い
頂きありがとうございました。
途中で質問にお答えしてくださった方々もどうもありがとうございます。

閉鎖

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