ページ 11

リンゴ落ちの向きについて

Posted: 2008年9月17日(水) 22:27
by mi
はじめまして。
管理人さんのリンゴ落ちゲー(?)を真似させてもらい、コードを書いていったのですが、
向きを右と左で変えてみようと思い、少しコードを変えてみたのですが変わらなく、私の力では
どこが間違ってるのかわからないので、質問させていただきます。

私は1ヶ月前からこのサイトを見つけ、勉強を始めたの初心者です。
C言語もぜんぜんやったこともなかったので、全く見当違い、また、理解が足りないからできない
と言うこともあると思います。その辺りはごめんなさい。

コンパイラはVC++でやってます。ゲームプログラミングの館。龍神録プログラミングの館の途中まで、読み進めた程度です(30章)

質問なのですが、管理人さんのリンゴの落ちゲー(?)
(http://www.play21.jp/board/formz.cgi?ac ... &lognum=64)
を見ながらコードを打っていって、その際に上でも記しましたが向きの反転をさせようと思い、
キャラクタの移動に
void char_ido(){
	if(Key[KEY_INPUT_RIGHT]>0){//右向きが押されていたら
		ch.muki=0;//向きヲセットする
		ch.x+=4;}
	if(Key[KEY_INPUT_LEFT]>0){//左
		ch.muki=1;
		ch.x-=4;}
}
とし、キャラクタの描画で
void char_graph(){
	if(ch.muki==0){
		DrawRotaGraph(ch.x,ch.y,1.0f,0,img[1],TRUE,TRUE);
		DrawRotaGraph(ch.x+50,ch.y,1.0f,0,img[2],TRUE);
	}
	if(ch.muki==1){
		DrawRotaGraph(ch.x,ch.y,1.0f,0,img[1],TRUE,FALSE);
		DrawRotaGraph(ch.x-50,ch.y,1.0f,0,img[2],TRUE);
	}
}
とし、実行すると常にどっちかしか向かない状態になって、反転してくれません。
DrawRotaGraph関数の反転が間違ってるのかと思い、画像を新たに反転しているのを用意し、
muki==0とmiku==1の画像ハンドルを新しいのにそれぞれ変え、実行しましたが、変わりませんでした。

コードは全部記した方がいいのでしょうか?
これは、どうしたら反転してくれるようになるのでしょうか?
やり方がそもそも間違っているとしたら、どのようにしてみるのがいいか、アドバイスを頂けたらと
思い質問させていただきます。

Re:リンゴ落ちの向きについて

Posted: 2008年9月18日(木) 02:54
by 管理人
初めまして。
ターンフラグなどリファレンスに無い使い方も利用されてるとはすごいですね。

とりあえず足らないところは想像で補完してコンパイルは出来ました。
お書きになっているものとあっているかどうかはわかりませんが・・。

>常にどっちかしか向かない状態になって、反転してくれません

ということなんですが、どっちかしか向かないとは・・?
右か左かどっちかに向けばいいのではないでしょうか?
ごめんなさい、理解力低いせいでわかりません(_ _||)

実行したところ、添付ファイルのようになりましたが、
向きが逆では・・?という印象を受けますが、そのことでしょうか?
う~ん、これは単にターンフラグを反対にすればいいだけだと思うので違うでしょうかね。
この辺少し補足していただけると助かります。

違う話ですが、カゴの表示位置と実際のカゴの位置が違うので、リンゴが見た目の位置に入らないことがありますね。
キャラの位置を基準に、現在の向きでカゴの位置を決めてみましょう。
新たに作ったサンプル以下に示しておきますね。

Re:リンゴ落ちの向きについて

Posted: 2008年9月18日(木) 03:14
by 管理人
青い部分が変更点です。
今回、「カゴ」という概念を別にした方がよさそうだったので
kago_t型の構造体を用意し、kagoという変数を用いてカゴを一つの物体として計算しました。
処理はいたって簡単で、キャラの位置と向きを中心に、カゴの位置を決めているだけです。
#include "../lib/DxLib.h"
#include <math.h>

#define BULLET_MAX 1000
#define PI 3.141592653589793

//キャラクタデータの構造体
typedef struct{
	int muki;
	double x,y;
}ch_t;

typedef struct{
	int flag,cnt;
	double x,y,angle,spd;
}bullet_t;

//カゴの構造体☆1
typedef struct{
	int x,y;
}kago_t;

int count;
int Key[256];//キーデータ
int img[10],img_bullet[3];//画像データ
int White;//白色データ
ch_t ch;//キャラデータ
bullet_t bullet[BULLET_MAX];

//カゴの変数宣言☆2
kago_t kago;

//キーがどれ位押されたかを判定する関数
//この関数は覚えなくていい。
//渡されたKeyStateBufにはキーの押されたカウンタ数が入るものだと知っているだけでOK
int GetHitKeyStateAll_2(int KeyStateBuf[/url]){
	char GetHitKeyStateAll_Key[256];
	GetHitKeyStateAll( GetHitKeyStateAll_Key );
	for(int i=0;i<256;i++){
		if(GetHitKeyStateAll_Key==1)KeyStateBuf++;
		else KeyStateBuf=0;
	}
	return 0;
}

//データロード
void load(){
	img[0]=LoadGraph( "img/haikei.jpg" );
	img[1]=LoadGraph( "img/char.png" );
	img[2]=LoadGraph( "img/kago.png" );
	LoadDivGraph( "img/bullet.png" , 1 , 1 , 1 , 30 , 29 , img_bullet ) ;
	White=GetColor(255,255,255);
}

//初期化
void ini(){
	ch.x=320;
	ch.y=360;
	count=0;
}

void calc_kago(){//カゴの計算☆3
	kago.x=ch.x;
	kago.y=ch.y;
	if(ch.muki==0)//右なら
		kago.x += 30;//キャラより30右へ
	if(ch.muki==1)//左なら
		kago.x -= 30;//キャラより30左へ
}

//キャラクタ移動
void operate_char(){
	if(Key[KEY_INPUT_RIGHT]>0){//右向きが押されていたら
		ch.muki=0;//向きヲセットする
		ch.x+=4;
	}
	if(Key[KEY_INPUT_LEFT]>0){//左
		ch.muki=1;
		ch.x-=4;
	}
	calc_kago();//カゴの計算関数の呼び出し☆4
}

//空いている弾情報格納要素番号を返す
int bullet_serch(){
	int i;
	for(i=0;i<BULLET_MAX;i++){
		if(bullet.flag==0)
			return i;
	}
	return -1;
}

//弾を登録
void bullet_enter(){
	int n;
	n=bullet_serch();//空いている弾情報格納要素番号
	if(n!=-1){
		bullet[n].flag =1;				//弾の存在フラグを立てる
		bullet[n].cnt	=0;				//カウンタ初期化
		bullet[n].spd	=2;				//発射スピードを2に
		bullet[n].angle=PI/2;			//角度は真下である90°
		bullet[n].x		=GetRand(640);	//x座標は0~640のランダムな値
		bullet[n].y		=0;				//y座標は0
	}
}

//i番の弾幕がかごの中にあるかどうかを判定する関数。
//1:ある 0:ない
int bullet_kago_exit(int i){
	//もしカゴの中にあれば
	//カゴに入るかどうかの判定文変更☆5
	if(kago.x-25<bullet.x && bullet.x<kago.x+25 
		&& kago.y-25<bullet.y && bullet.y<kago.y+25)
		return 1;
	return 0;
}

//i番の弾幕が画面内にあるかどうかを判定する関数。
//1:画面外 0:画面内
int bullet_feeld_exit(int i){
	//もし画面の中になければ
	if(!(0<=bullet.x && bullet.x<=640 && 0<=bullet[i].y && bullet[i].y<=480))
		return 1;
	return 0;
}

//弾計算
void bullet_calc(){
	int i;
	if(count%60==0)//60カウントに一回(モニタが60Hzの場合、60カウントは1秒)
		bullet_enter();//発射
	//軌道計算
	for(i=0;i<BULLET_MAX;i++){
		if(bullet[i].flag==1){
			bullet[i].x += cos(bullet[i].angle) * bullet[i].spd;//x成分計算
			bullet[i].y += sin(bullet[i].angle) * bullet[i].spd;//y成分計算
			if(bullet_kago_exit(i)==1)//かごの中にあるか?
				bullet[i].flag=0;//弾を消す
			if(bullet_feeld_exit(i)==1)//画面の中にあるか?
				bullet[i].flag=0;//弾を消す
		}
	}
}

//弾メイン
void bullet_main(){
	bullet_calc();
}

//背景描画
void graph_background(){
	DrawGraph(0,0,img[0],FALSE);
}

//キャラ描画
void graph_char(){
	//カゴの位置による描画に変更☆6
	DrawRotaGraph(kago.x, kago.y, 1.0f, 0, img[2],TRUE);
	if(ch.muki==0){
		DrawRotaGraph(ch.x,ch.y,1.0f,0,img[1],TRUE,FALSE);
	}
	if(ch.muki==1){
		DrawRotaGraph(ch.x,ch.y,1.0f,0,img[1],TRUE,TRUE);
	}
}

//弾の描画
void graph_bullet(){
	int i;
	for(i=0;i<BULLET_MAX;i++){
		if(bullet[i].flag>0){
			DrawRotaGraphF( bullet[i].x, bullet[i].y, 1.0f, 
			bullet[i].angle-PI/2, img_bullet[0], TRUE );
			//+PI/2は弾の向きを合わせるため
		}
	}
}

//描画処理メイン
void graph_main(){
	graph_background();//背景描画
	graph_char();//キャラ描画
	graph_bullet();//弾描画
}

int WINAPI WinMain( HINSTANCE hInstance, 
	HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
		
	ChangeWindowMode(TRUE);//ウィンドウモード
	if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0)
		return -1;//初期化と裏画面化

	load();//データロード
	ini();//初期化

	while(ProcessMessage()==0 && ClearDrawScreen()==0 &&
		GetHitKeyStateAll_2(Key)==0 && Key[KEY_INPUT_ESCAPE]==0){

		operate_char();//キャラ操作
		bullet_main();//弾メイン
		graph_main();//描画処理メイン

		count++;

		ScreenFlip();//裏画面反映関数
	}
	DxLib_End();//DXライブラリ終了処理
	return 0;
}
 

Re:リンゴ落ちの向きについて

Posted: 2008年9月18日(木) 17:43
by mi
返答ありがとうございます。

>常にどっちかしか向かない状態になって、反転してくれません

言葉不足で申し訳ありません。右入力で右向きに、左入力で左向きにさせたいのに
最初に画面に表示された向きのままで、逆を押してもそっちに向きになってくれないということです。
(いろいろいじっていたら最初の向きが変わることもあったので
 こういう表現にさせてもらいました)
自分のイメージでは、ch.muki==0で右向きの画像を、ch.muki==1で左向きの画像を表示させたかった
ので、ifを使い場合分けをし、左向き時は右向きの画像を反転させて左を表示させようとしましたが、
(上記のコード間違ってました。TRUEとFALSEが逆でした。)
できませんでした。ここからあれこれいじったのですが、どうして向いてくれないのかわからないので
質問させていただきました。

管理人さんのコード通りでやりたかったことは出来ました。ありがとうございます。
しかし、何が原因だったのか私なりに検討しましたが分かりませんでした。
変更点はカゴの構造体を作ってカゴを別に計算させたこと。、
ここら辺を見てみましたが、何がいけなかったのか。これがいまいちわかりません。

大元から私が変更した点は、上の記したキャラの移動と描画と
カゴの中にりんごがあるかどうかの判定関数くらいです。
//i番の弾幕がかごの中にあるかどうかを判定する関数
//1:ある 2:ない
int bullet_kago_exit(int i){
	//もしカゴにあれば
	if(ch.muki=0)
		if(ch.x+25<bullet.x && bullet.x<ch.x+75 && ch.y-22<bullet.y && bullet.y<ch.y+22)
			return 1;
	if(ch.muki=1)
		if(ch.x-75<bullet.x&&bullet.x<ch.x-25&&ch.y-22<bullet.y&&bullet.y<ch.y+22)
			return 1;
	return 0;
}


となっているところくらいしか変更をしてないのですが、
これらのせいで左右の方向転換をしてくれないというのはどうしてなのか、
どこを間違ったのかがわからないので、教えていただきたく質問させていただきます。

Re:リンゴ落ちの向きについて

Posted: 2008年9月18日(木) 19:38
by 管理人
>最初に画面に表示された向きのままで、逆を押してもそっちに向かない

と言う事ですが、私がコンパイルした時には、押したキーと逆向きに向いていました。
添付ファイルのような感じです。
もしかしたら私が予測して補完した部分がそちらと違うのかもしれません。
もしよければこちらにコードを添付してもらえるでしょうか。

Re:リンゴ落ちの向きについて

Posted: 2008年9月18日(木) 19:57
by mi
添付というのは、コードだけ記せばいいのでしょうか?
それとも、zipなどで上げるということでしょうか?
理解力不足ですみません。

わからないので、とりあえずコードを下に記したいと思います。
#include "DxLib.h"
#include <math.h>

#define BULLET_MAX 1000
#define PI 3.141592653589793

//キャラックタの構造体
typedef struct{
	double x,y;//座標
	int muki;
}ch_t;

typedef struct{
	int cnt,flag;
	double x,y,angle,spd;
}bullet_t;

int count;
int Key[256];//キーデータ
int img[10],img_bullet[3];//画像データ
int White;//白色データ
ch_t ch;
bullet_t bullet[BULLET_MAX];

int GetHitKeyStateAll_2(int KeyStateBuf[/url]){
        char GetHitKeyStateAll_Key[256];
        GetHitKeyStateAll( GetHitKeyStateAll_Key );
        for(int i=0;i<256;i++){
                if(GetHitKeyStateAll_Key==1) KeyStateBuf++;
                else                            KeyStateBuf=0;
        }
        return 0;
}
//データロード
void load(){
	img[0]=LoadGraph("img/haikei.jpg");
	img[1]=LoadGraph("img/char_r.png");//右向き画像
	img[2]=LoadGraph("img/char_l.png");//左向き画像
	img[3]=LoadGraph( "img/kago.png" );
	LoadDivGraph( "img/bullet.png" , 1 , 1 , 1 , 30 , 29 , img_bullet ) ;
	White=GetColor(255,255,255);
}

//初期化
void ini(){
	ch.x=320;
	ch.y=360;
	count=0;
	ch.muki=0;
}

//キャラクタ移動
void char_ido(){
	if(Key[KEY_INPUT_RIGHT]>0){//右向きが押されていたら
		ch.muki=0;//向きヲセットする
		ch.x+=4;}
	if(Key[KEY_INPUT_LEFT]>0){//左
		ch.muki=1;
		ch.x-=4;}
}

//キャラの描画
void char_graph(){
	if(ch.muki==0){
		DrawRotaGraph(ch.x,ch.y,1.0f,0,img[1],TRUE,FALSE);
		DrawRotaGraph(ch.x+50,ch.y,1.0f,0,img[3],TRUE);
	}
	if(ch.muki==1){
		DrawRotaGraph(ch.x,ch.y,1.0f,0,img[1],TRUE,TRUE);
		DrawRotaGraph(ch.x-50,ch.y,1.0f,0,img[3],TRUE);
	}
}

//空いている弾情報格納要素番号を返す
int bullet_serch(){
	for(int i=0;i<BULLET_MAX;i++){
		if(bullet.flag==0)
			return i;
	}
	return -1;
}

//弾を登録
void bullet_enter(){
	int n;
	n=bullet_serch();//空いている弾幕情報格納要素番号
	if(n!=-1){
		bullet[n].flag=1;//弾の存在フラグをたてる
		bullet[n].cnt=0;//カウンタの初期化
		bullet[n].spd=2;//発射スピードを2に
		bullet[n].angle=PI/2;//角度は真下
		bullet[n].x=GetRand(640);
		bullet[n].y=0;
	}
}

//i番の弾幕がかごの中にあるかどうかを判定する関数
//1:ある 2:ない
int bullet_kago_exit(int i){
	//もしカゴにあれば
	if(ch.muki=0)
		if(ch.x+25<bullet.x && bullet.x<ch.x+75 && ch.y-22<bullet.y && bullet.y<ch.y+22)
			return 1;
	if(ch.muki=1)
		if(ch.x-75<bullet.x&&bullet.x<ch.x-25&&ch.y-22<bullet[i].y&&bullet[i].y<ch.y+22)
			return 1;
	return 0;
}

//i番の弾幕が画面内にあるかどうかを判定する関数。
//1:外 2:内
int bullet_feeld_exit(int i){
	//もし画面内なら
	if(!(0<=bullet[i].x &&bullet[i].x<=640&&0<=bullet[i].y&&bullet[i].y<=480))
		return 1;
	return 0;
}


//弾計算
void bullet_calc(){
	int i;
	if(count%60==0)//60カウントに一回(モニタが60Hzの場合、60カウントは1秒)
		bullet_enter();//発射
	//軌道計算
	for(i=0;i<BULLET_MAX;i++){
		if(bullet[i].flag==1){
			bullet[i].x+=cos(bullet[i].angle)*bullet[i].spd;
			bullet[i].y+=sin(bullet[i].angle)*bullet[i].spd;
			if(bullet_kago_exit(i)==1)//かごに入ってれば
				bullet[i].flag=0;
			if(bullet_feeld_exit(i)==1)//画面の中にあるか
				bullet[i].flag=0;
		}
	}
}

//弾メイン
void bullet_main(){
	bullet_calc();
}

//背景描画
void graph_background(){
	DrawGraph(0,0,img[0],FALSE);
}

//弾の描画
void graph_bullet(){
	int i;
	for(i=0;i<BULLET_MAX;i++){
		if(bullet[i].flag>0){
			DrawRotaGraphF(bullet[i].x,bullet[i].y, 1.0f, bullet[i].angle-PI/2, img_bullet[0], TRUE );
			//+PI/2は弾の向きを合わせるため
		}
	}
}

//描画メイン
void graph_main(){
	graph_background();
	char_graph();
	graph_bullet();
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
        
    
    ChangeWindowMode(TRUE);//ウィンドウモード
    if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) return -1;//初期化と裏画面化

	load();//データロード
	ini();//初期化

    while(ProcessMessage()==0 && ClearDrawScreen()==0 && GetHitKeyStateAll_2(Key)==0 && Key[KEY_INPUT_ESCAPE]==0){
          //↑メッセージ処理         ↑画面をクリア            ↑入力状態を保存       ↑ESCが押されていない

		char_ido();//キャラ操作
		bullet_main();//弾メイン
		graph_main();//描画処理メイン

		count++;

        ScreenFlip();//裏画面反映関数
    }

    DxLib_End();//DXライブラリ終了処理
    return 0;
}


です。

Re:リンゴ落ちの向きについて

Posted: 2008年9月18日(木) 21:14
by 管理人
あ~これは見落としやすいかもしれませんね。
//キャラの描画

int bullet_kago_exit(int i){
    //もしカゴにあれば
    if(ch.muki=0)
        if(ch.x+25<bullet.x && bullet.x<ch.x+75 && ch.y-22<bullet.y && bullet.y<ch.y+22)
            return 1;
    if(ch.muki=1)
        if(ch.x-75<bullet.x&&bullet.x<ch.x-25&&ch.y-22<bullet.y&&bullet.y<ch.y+22)
            return 1;
    return 0;
}

ここは比較を行いたいのかもしれませんが、代入になっています。

a == 1 

は比較ですが

a = 1

は代入です。aに1を代入します。
ここで、条件文の仕組みについて理解してみましょう。

int a=1;
if( a==1 )

みたいにかかれたとき、このa==1は具体的になんなのでしょうか。
条件文が「真」か「偽」かということはご存知でしょうか。
この時はaが1ですから「真」となります。「真」とは0以外を意味します。
試しにprintfでa==1を表示してみたら「1」を表示されました。
今度は

int a=2;
if( a==1 )

としたとき、a==1は偽となり、a==1は0と同じことになります。
つまり、条件文の中身が0なら処理されませんし、0以外なら処理されます。

だから

if( a=1 )

でも条件文としてはaが1だから0以外と解釈し、真として処理されるわけです。
ということで、

if( a=0 )
と
if( a=1 )

は比較ではないものの、ちゃんと条件式は評価されて期待しているのとは違えども処理されているのです。

まぁとりあえずイコール二つにすれば直ります。

Re:リンゴ落ちの向きについて

Posted: 2008年9月18日(木) 21:39
by mi
ありがとうございます。
自分で見てて気づけなかったですが確かにあれじゃおかしいですね。
エラーの出てくれないミスは恐ろしいです。
とても解りやすく丁寧に説明していただき、本当にありがとうございます。

これからこのゲームに少しずついろいろ足していけたらと思って現在四苦八苦中です。
以後解らないところがあったら、またお願いしたいと思います。
どうもありがとうございました。