ブロック崩しの弾の跳ね返りが上手くいかない

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

ブロック崩しの弾の跳ね返りが上手くいかない

#1

投稿記事 by dast » 13年前

こんにちは。ブロック崩しを作ろうと思い、ひとまず画面内を球が跳ねながら移動し、左右キーを押すと自機が左右に動くプログラムを作ってみたのですが、弾がうまく跳ねかえらず、自機が左方向に動きません。
考え方としては弾の座標がx<=0,y<=0,x>=640,y>=480のどれかを満たしたとき、座標を画面内に戻し角度を変えるというものですが、弾がy>=480を満たすと角度が絶え間なく変わってしまい、跳ね返る軌道を描いてくれません。それと最初に中央にある自機が何故か左側に動きません。一体どうしたら弾は跳ね返ってくれるのでしょうか?
プログラムソースは次の通りです。

コード:


#include "DxLib.h"
#include <math.h>

#define tsx 640//弾の初期x座標
#define tsy 240//弾の初期y座標
#define tsa 1.25//弾の初期角度
#define tss 5//弾の初期速度
#define maxx 640//弾の移動領域の右端
#define minx 0//弾の移動領域の左端
#define miny 0//弾の移動領域の上端
#define maxy 480//弾の移動領域の右端
#define zsx 320//自機の初期x座標
#define zsy 480//自機の初期y座標
#define zss 1//自機の初期スピード
#define PI 3.1415926f//円周率

int Key[256]; // キーが押されているフレーム数を格納する

// キーの入力状態を更新する
int gpUpdateKey(){
        char tmpKey[256]; // 現在のキーの入力状態を格納する
        GetHitKeyStateAll( tmpKey ); // 全てのキーの入力状態を得る
        for( int i=0; i<256; i++ ){ 
                if( tmpKey[i] != 0 ){ // i番のキーコードに対応するキーが押されていたら
                        Key[i]++;     // 加算
                } else {              // 押されていなければ
                        Key[i] = 0;   // 0にする
                }
        }
        return 0;
}

typedef struct{
	float x,y,ang,sp;//x,y座標,角度,スピード
	int img;//画像
}TamaPara_t;
TamaPara_t TP;

typedef struct{
	float x,y,sp;//x,y座標,スピード
	int img;//画像
}ZikiPara_t;
ZikiPara_t ZP;

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
        ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定

	int cnt;//カウンター

	TP.img = LoadGraph("img/tama.png");//画像をロード
	ZP.img = LoadGraph("img/ziki.png");//画像をロード
	cnt=0;
	TP.x = tsx;
	TP.y = tsy;
	TP.ang = tsa;
	TP.sp = tss;
	ZP.x = zsx;
	ZP.y = zsy;
	ZP.sp = zss;

        // while(裏画面を表画面に反映, メッセージ処理, 画面クリア, キーの状態更新)
	while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 && gpUpdateKey()==0 ){
		//計算フェイズ
		if( Key[ KEY_INPUT_RIGHT ] >= 1 && ZP.x<maxx ){ // 移動領域の中で右キーが押されていたら
			ZP.x+=zss;
		}
		if( Key[ KEY_INPUT_LEFT ] >= 1 && ZP.x<minx ){ // 移動領域の中で左キーが押されていたら
			ZP.x-=zss;
		}
		TP.x += cos(TP.ang)*TP.sp;//x座標を更新
		TP.y += sin(TP.ang)*TP.sp;//y座標を更新
		if(TP.x>=maxx || TP.x<=minx || TP.y>=maxy || TP.y<=miny || cnt!=0 ){
			if(TP.x>=maxx){
			   TP.x-=(TP.x-maxx)*2;
			   if(TP.ang >=1.0 || TP.ang <2.0){
				TP.ang=3-TP.ang;
			   }
			   else{
				TP.ang=1-TP.ang;
			   }
			}
			else if(TP.x<=minx){
			   TP.x+=(minx-TP.x)*2;
			   if(TP.ang >=1.0 || TP.ang <2.0){
				   TP.ang=3-TP.ang;
			   }
			   else{
				   TP.ang=1-TP.ang;
			   }
			}
			else if(TP.y>=maxy){//異常
			   TP.y-=(TP.y-maxy)*2;
			   TP.ang=2-TP.ang;
			}
			else if(TP.y<=miny){//異常
			   TP.y+=(miny-TP.y)*2;
			   TP.ang=2-TP.ang;
			}
			//TP.ang -= 0.50;
			if(TP.ang>=2.00){
				TP.ang -= 2.00;
			}
			else if(TP.ang<0){
				TP.ang += 2.00;
			}
		}
		//自機の画像は横
		//作画フェイズ
		DrawRotaGraph(TP.x,TP.y,1.0,0.0,TP.img,TRUE);
		DrawRotaGraph(ZP.x,ZP.y,1.0,0.0,ZP.img,TRUE);
		DrawFormatString(0,0,GetColor(255,255,255),"%7.2f",TP.ang);
		cnt+=1;
	}

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

jay
記事: 314
登録日時: 14年前
住所: 大阪市
連絡を取る:

Re: ブロック崩しの弾の跳ね返りが上手くいかない

#2

投稿記事 by jay » 13年前

自機が左に行かないのはここが原因でしょうね

コード:

if( Key[ KEY_INPUT_LEFT ] >= 1 && ZP.x<minx ){ // 移動領域の中で左キーが押されていたら
            ZP.x-=zss;
        }
minxが移動可能領域の左端ならば
ZP.x<minx
ではなく
ZP.x>minx
と書くのが正解ですね(左端でZP.xが最も小さくなるため)

反射についてですが、この場合恐らくy>=480を満たすと無条件で反射処理を行っているのだと思います。
結果反射を行った次のフレームでもまたy>=480を満たしてしまい、その場で意味不明な行動を起こしているのだと思います。

どうすればいいのかと聞かれれば、これにはいくつか方法がありますね
フラグを使う方法、ベクトルから判定する方法、反射時に位置を調整する方法などがあります。

例として反射時に位置を調整する方法を説明しますね
簡単にいうと、反射した(角度を更新した)時にyを479にするんです。
そうすれば角度が変わった(反射した)ままy>=480を満たすことはなくなります。
これが恐らくもっとも簡単な方法だと思います。 但しボールのスピードによっては見た目に違和感を覚えるので注意してくださいね。
♪僕たちは まだ森の中 抜け出そう 陽のあたる場所へ

dast
記事: 41
登録日時: 13年前

Re: ブロック崩しの弾の跳ね返りが上手くいかない

#3

投稿記事 by dast » 13年前

ご指摘いただいて直したところ自機を左に動かせるようにはなりました。ありがとうございます。
ただ「反射処理をした後にボールが移動領域にいた場合強制的に移動領域に入れる」という処理を組み込んでも反射ができません。この場合は何が問題になっていると思いますか?
以下プログラムソース

コード:


#include "DxLib.h"
#include <math.h>

#define tsx 640//弾の初期x座標
#define tsy 240//弾の初期y座標
#define tsa 1.25//弾の初期角度
#define tss 5//弾の初期速度
#define maxx 640//弾の移動領域の右端
#define minx 0//弾の移動領域の左端
#define miny 0//弾の移動領域の上端
#define maxy 480//弾の移動領域の右端
#define zsx 320//自機の初期x座標
#define zsy 480//自機の初期y座標
#define zss 1//自機の初期スピード
#define PI 3.1415926f//円周率

int Key[256]; // キーが押されているフレーム数を格納する

// キーの入力状態を更新する
int gpUpdateKey(){
        char tmpKey[256]; // 現在のキーの入力状態を格納する
        GetHitKeyStateAll( tmpKey ); // 全てのキーの入力状態を得る
        for( int i=0; i<256; i++ ){ 
                if( tmpKey[i] != 0 ){ // i番のキーコードに対応するキーが押されていたら
                        Key[i]++;     // 加算
                } else {              // 押されていなければ
                        Key[i] = 0;   // 0にする
                }
        }
        return 0;
}

typedef struct{
	float x,y,ang,sp;//x,y座標,角度,スピード
	int img;//画像
}TamaPara_t;
TamaPara_t TP;

typedef struct{
	float x,y,sp;//x,y座標,スピード
	int img;//画像
}ZikiPara_t;
ZikiPara_t ZP;

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
        ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定

	int cnt;//カウンター

	TP.img = LoadGraph("img/tama.png");//画像をロード
	ZP.img = LoadGraph("img/ziki.png");//画像をロード
	cnt=0;
	TP.x = tsx;
	TP.y = tsy;
	TP.ang = tsa;
	TP.sp = tss;
	ZP.x = zsx;
	ZP.y = zsy;
	ZP.sp = zss;

        // while(裏画面を表画面に反映, メッセージ処理, 画面クリア, キーの状態更新)
	while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 && gpUpdateKey()==0 ){
		//計算フェイズ
		if( Key[ KEY_INPUT_RIGHT ] >= 1 && ZP.x<maxx ){ // 移動領域の中で右キーが押されていたら
			ZP.x+=zss;
		}
		if( Key[ KEY_INPUT_LEFT ] >= 1 && ZP.x>minx ){ // 移動領域の中で左キーが押されていたら
			ZP.x-=zss;
		}
		TP.x += cos(TP.ang)*TP.sp;//x座標を更新
		TP.y += sin(TP.ang)*TP.sp;//y座標を更新
		if(TP.x>=maxx || TP.x<=minx || TP.y>=maxy || TP.y<=miny || cnt!=0 ){
			if(TP.x>=maxx){
			   TP.x-=(TP.x-maxx)*2;
			   if(TP.ang >=1.0 || TP.ang <2.0){
				TP.ang=3-TP.ang;
			   }
			   else{
				TP.ang=1-TP.ang;
			   }
			}
			else if(TP.x<=minx){
			   TP.x+=(minx-TP.x)*2;
			   if(TP.ang >=1.0 || TP.ang <2.0){
				   TP.ang=3-TP.ang;
			   }
			   else{
				   TP.ang=1-TP.ang;
			   }
			}
			else if(TP.y>=maxy){//異常
			   TP.y-=(TP.y-maxy)*2;
			   TP.ang=2-TP.ang;
			   if(TP.y>=maxy){
				   TP.y=maxy-1;
			   }
			}
			else if(TP.y<=miny){//異常
			   TP.y+=(miny-TP.y)*2;
			   TP.ang=2-TP.ang;
			   if(TP.y<=miny){
				   TP.y=miny+1;
			   }
			}
			//TP.ang -= 0.50;
			if(TP.ang>=2.00){
				TP.ang -= 2.00;
			}
			else if(TP.ang<0){
				TP.ang += 2.00;
			}
		}
		//自機の画像は横
		//作画フェイズ
		DrawRotaGraph(TP.x,TP.y,1.0,0.0,TP.img,TRUE);
		DrawRotaGraph(ZP.x,ZP.y,1.0,0.0,ZP.img,TRUE);
		DrawFormatString(0,0,GetColor(255,255,255),"%7.2f",TP.ang);
		cnt+=1;
	}

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

jay
記事: 314
登録日時: 14年前
住所: 大阪市
連絡を取る:

Re: ブロック崩しの弾の跳ね返りが上手くいかない

#4

投稿記事 by jay » 13年前

う~ん・・・、何が原因でしょうね(汗)

恐らくですが角度の扱いに問題があるのではないでしょうか?
定数で PI 3.1415...と宣言しているにも関わらず使っている場所が見受けられないですしね

そればかりかangの変数に+=2.0とかやってますし
これって弧度法ですよね? だとしたらやっぱりPIを使った方がいいですよ~
だって何より分かりやすくなりますし(苦笑)
ちなみに弧度法で2.0だと約114.591度というよくわからない数値に・・・w

弧度法PI(π)は180度、PI/2は90度を表します
つまり壁に当たった時方向に応じて角度(ang)に+PI/2か-PI/2してやればいいでしょうね

あともうひとつおかしいと思ったのはこれらの文ですね

コード:

else if(TP.y<=miny){//異常
               TP.y+=(miny-TP.y)*2;
               TP.ang=2-TP.ang;
               if(TP.y<=miny){
                   TP.y=miny+1;
               }
            }
TP.y<=minyと判定したif文の中でもう一回TP.y<=minyと判定しているif文が入っていますし
TP.y+=(miny-TP.y)*2;の部分も何の為にあるのかよく分かりません。
角度を変えて位置を調整するだけならこれでいいのではないでしょうか?

コード:

else if(TP.y<=miny){//異常
               TP.ang=2-TP.ang;
               TP.y=miny+1;
               }
            }
これ以外の部分についても修正してあげてくださいね
最後に編集したユーザー jay on 2011年12月18日(日) 00:51 [ 編集 1 回目 ]
♪僕たちは まだ森の中 抜け出そう 陽のあたる場所へ

dast
記事: 41
登録日時: 13年前

Re: ブロック崩しの弾の跳ね返りが上手くいかない

#5

投稿記事 by dast » 13年前

ご指摘を受けて改めて見たところ、71、72行目のPIを使うべきところで使われていないというミスを発見したのでそれを直し、さらに跳ね返りの条件とその後の処理を少し見直したら跳ね返りが上手くいくようになりました。ありがとうございます。
ところで跳ね返りとはまた別の質問になってしまうのですが、このプログラムの弾の角度を1.25πにしている筈なのに何故か最初に右上の方向に飛んで行ってしまうのですが何故でしょうか?御教授お願いします。
ちなみに現在のソースは次のとおりです。

コード:

#include "DxLib.h"
#include <math.h>

#define tsx 640//弾の初期x座標
#define tsy 240//弾の初期y座標
#define tsa 1.25//弾の初期角度
#define tss 5//弾の初期速度
#define maxx 640//弾の移動領域の右端
#define minx 0//弾の移動領域の左端
#define miny 0//弾の移動領域の上端
#define maxy 480//弾の移動領域の右端
#define zsx 320//自機の初期x座標
#define zsy 480//自機の初期y座標
#define zss 3//自機の初期スピード
#define PI 3.1415926f//円周率

int Key[256]; // キーが押されているフレーム数を格納する

// キーの入力状態を更新する
int gpUpdateKey(){
        char tmpKey[256]; // 現在のキーの入力状態を格納する
        GetHitKeyStateAll( tmpKey ); // 全てのキーの入力状態を得る
        for( int i=0; i<256; i++ ){ 
                if( tmpKey[i] != 0 ){ // i番のキーコードに対応するキーが押されていたら
                        Key[i]++;     // 加算
                } else {              // 押されていなければ
                        Key[i] = 0;   // 0にする
                }
        }
        return 0;
}

typedef struct{
	float x,y,ang,sp;//x,y座標,角度,スピード
	int img;//画像
}TamaPara_t;
TamaPara_t TP;

typedef struct{
	float x,y,sp;//x,y座標,スピード
	int img;//画像
}ZikiPara_t;
ZikiPara_t ZP;

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
        ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定

	int cnt;//カウンター

	TP.img = LoadGraph("img/tama.png");//画像をロード
	ZP.img = LoadGraph("img/ziki.png");//画像をロード
	cnt=0;
	TP.x = tsx;
	TP.y = tsy;
	TP.ang = tsa;
	TP.sp = tss;
	ZP.x = zsx;
	ZP.y = zsy;
	ZP.sp = zss;

        // while(裏画面を表画面に反映, メッセージ処理, 画面クリア, キーの状態更新)
	while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 && gpUpdateKey()==0 ){
		//計算フェイズ
		if( Key[ KEY_INPUT_RIGHT ] >= 1 && ZP.x<maxx ){ // 移動領域の中で右キーが押されていたら
			ZP.x+=zss;
		}
		if( Key[ KEY_INPUT_LEFT ] >= 1 && ZP.x>minx ){ // 移動領域の中で左キーが押されていたら
			ZP.x-=zss;
		}
		TP.x += cos(TP.ang*PI)*TP.sp;//x座標を更新
		TP.y += sin(TP.ang*PI)*TP.sp;//y座標を更新
		if(TP.x>=maxx || TP.x<=minx || TP.y>=maxy || TP.y<=miny || cnt!=0 ){
			if(TP.x>maxx){
			   TP.x-=(TP.x-maxx)*2;
			   if(TP.ang >=1.0 || TP.ang <2.0){
				TP.ang=3-TP.ang;
			   }
			   else{
				TP.ang=1-TP.ang;
			   }
			}
			else if(TP.x<minx){
			   TP.x+=(minx-TP.x)*2;
			   if(TP.ang >=1.0 || TP.ang <2.0){
				   TP.ang=3-TP.ang;
			   }
			   else{
				   TP.ang=1-TP.ang;
			   }
			}
			else if(TP.y>maxy){
			   TP.y-=(TP.y-maxy)*2;
			   TP.ang=2-TP.ang; 
			}
			else if(TP.y<miny){
			   TP.y+=(miny-TP.y)*2;
			   TP.ang=2-TP.ang;
			}
			if(TP.ang>=2.00){
				TP.ang -= 2.00;
			}
			else if(TP.ang<0){
				TP.ang += 2.00;
			}
		}
		//作画フェイズ
		DrawRotaGraph(TP.x,TP.y,1.0,0.0,TP.img,TRUE);
		DrawRotaGraph(ZP.x,ZP.y,1.0,0.0,ZP.img,TRUE);
		DrawFormatString(0,0,GetColor(255,255,255),"%7.2f",TP.ang);
		DrawFormatString(0,50,GetColor(255,255,255),"%7.2f",TP.x);
		DrawFormatString(0,70,GetColor(255,255,255),"%7.2f",TP.y);
		cnt+=1;
	}

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


jay
記事: 314
登録日時: 14年前
住所: 大阪市
連絡を取る:

Re: ブロック崩しの弾の跳ね返りが上手くいかない

#6

投稿記事 by jay » 13年前

さぁ? 何故でしょう?

僕の方でプログラムを実行してみたのですが、弾は最初左上に移動しましたよ。
1.25πなのでこれで問題はないハズなんですけどね。
実際プログラムに殆ど手を加えず、張り付けてあったコードをそのまま実行したので何も問題はない・・・ハズですね(汗)

こういう時はとりあえずソリューションをリビルドしてみてくださいな
中間ファイルが余計なことをしているというのも稀にあることです。 ホントに稀ですけど(苦笑)
♪僕たちは まだ森の中 抜け出そう 陽のあたる場所へ

dast
記事: 41
登録日時: 13年前

Re: ブロック崩しの弾の跳ね返りが上手くいかない

#7

投稿記事 by dast » 13年前

かなり遅い返信ですみません。角度の件は解決しました。プログラムでの角度指定が時計回りだったのに対し私の頭では反時計回りだったというだけでした。左右の違いは単に右と左を間違えてレスしてしまっただけでした。本当につまらないことでお騒がせしてすみませんでした。

閉鎖

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