ブロック崩しの自機とボールの当たり判定がうまくいかない

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

ブロック崩しの自機とボールの当たり判定がうまくいかない

#1

投稿記事 by dast » 13年前

今ブロック崩しを作っているのですが、自機とボールの当たり判定が上手くいきません。自機とボールの距離が丁度(自機の当たり判定+ボールの当たり判定)になった時に一時停止をするというプログラムにしたいのですが、実際に一時停止した時の距離がそれよりも短くなってしまいます。この場合どうしたら丁度の位置で一時停止してくれるのでしょうか?
以下プログラムコードと一応自機とボールに使っている画像です。
当たり判定を管理している関数は93行目~120行目ZikiTamaAtariJudge()関数と122行目~130行目のZikiTamaAtariSyori()関数で、132行目~147行目のTamaMove()関数の中で実行しています。
自機の画像の赤い円形が当たり判定の範囲、青い点が当たり判定の中心、緑の点が画像の中心です。

コード:

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

#define startx 0//玉の初期値x座標の右壁からの距離(0~max_x+TP.at-1)
#define starty 0//玉の初期値y座標の床からの距離(0~max_y+TP.at-1)
#define max_y 340//ゲーム部分下端
#define min_y 40  //ゲーム部分上端
#define max_x 620//ゲーム部分右端
#define min_x 20  //ゲーム部分左端

#define EnemyMax 30//同時に出現する敵の最大数
#define EnemyOrderMax 200//敵の出現情報の数
//定数
#define PMAX 500.0//最大高さ到達位置(単位m)
#define g 9.8//重力加速度(単位m/s^2)
#define fps 60.0//フレーム/秒
#define PI 3.141592//円周率

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

int GameCnt=0;//ゲーム全体のフレームカウンター
int StageCnt=0;//ステージのカウント
int GameStop=0;//ゲーム全体の停止スイッチ

int waku_img,enemy_img;//枠の画像格納用,敵の画像格納用

//定数
double TM_gpf;//(使用関数:TamaMove()):1フレームあたりの重力加速度
//座標と速度
double TM_y=0,TM_x=0,TM_v0pf=0,TM_vypf=0,TM_vxpf=0;//(使用関数:TamaMove()):ボールの位置(単位m),初速(単位m/フレーム),ボールのyベクトル速度(単位m/フレーム),ボールのxベクトル速度(単位m/フレーム)
double bektolx,bektoly,high,kyori;//(使用関数:TamaMove(),ZikiTamaAtariJudge()):ボールのxベクトル,yベクトル(単位ピクセル/フレーム),ボールの高さ,距離(単位 ピクセル)
//座標系
double TM_sy,TM_PMAXf;//(使用関数:TamaMove()):発射地点の高さ,最高点到達時間(単位フレーム)

typedef struct{//玉のパラメータ
	int x,y;//x座標,y座標
	double at,ymax,xmax,ang;//当たり判定、現軌道の最高点、投射角
}TamaPara_t;TamaPara_t TP;
int TP_img;
 
typedef struct{//自機のパラメータ
	int x,y,img,zanki,muteki;//座標、画像の番号,残機の数,無敵時間
	double sp,at;//自機の動く速さ,当たり判定の大きさ
}ZikiPara_t;ZikiPara_t ZP;
int ZP_img[4];

typedef struct{
	int img,hp,hp_max,flag,nolma,cnt,pattern,item,knd;//表示画像,HP,最大HP,生存フラグ,敵判定,カウンター,行動パターン,所持アイテム,敵の種類
	int at,attime,stage;//攻撃するかしないか,弾発射の間隔,出現ステージ
	double x,y,vx,vy;//座標,xベクトル,yベクトル
}EnemyPara_t;EnemyPara_t EP[EnemyMax];
int EP_img;

typedef struct{
	int hp_max,nolma,pattern,item,knd;//最大HP,敵判定,行動パターン,所持アイテム,敵の種類
	int at,attime,stage,timing;//攻撃の可否,攻撃間隔,出現ステージ,出現タイミング
	double x,y;//座標
}EnemyOrder_t;EnemyOrder_t EO[EnemyOrderMax];

void GamenAtariSyori(){
	if( TP.y > max_y-TP.at ) {//  床の下に来たら無理やり反射。反発係数80%
		TM_y = (-1)*PMAX*starty/((TP.ymax*sin(TP.ang*PI))-(max_y-TP.at-TM_sy));
		TM_vypf = -TM_vypf * 0.8;
		TM_sy = TP.y;
	}
	else if( TP.y <min_y+TP.at){
		TM_y = (PMAX*(-1)*(min_y-max_y+2.00*TP.at+starty))/((TP.ymax*sin(TP.ang*PI))-(max_y-TP.at-TM_sy));
		TM_vypf = -TM_vypf;
	}
	if( TP.x<min_x+TP.at){//左壁の当たり判定
		TM_x = (min_x-max_x+2.00*TP.at+startx)*PMAX/(TP.xmax*cos(TP.ang*PI));
		TM_vxpf = -TM_vxpf;
	}
	else if( TP.x > max_x-TP.at ){//右壁の当たり判定
		TM_x = (startx*PMAX)/(TP.xmax*cos(TP.ang*PI));
		TM_vxpf = -TM_vxpf;
	}
}

int ZikiTamaAtariJudge(){//自機と玉の当たり判定
	int i;
	double x=(double)(ZP.x)-(double)(TP.x);
	double y=(double)(ZP.y+9)-(double)(TP.y);
	double r=(ZP.at)+(TP.at);
	if(sqrt(bektolx*bektolx+bektoly*bektoly)>sqrt(r*r)){
		double prey = max_y - (high-bektoly) - TP.at-starty;                //1フレーム前の表示y座標
		double prex = max_x - TP.at - startx + (kyori+bektolx);//1フレーム前の表示x座標
		double imay = max_y - high - TP.at-starty;//現在の表示y座標
		double imax = max_x - TP.at - startx + kyori;//現在の表示x座標
		double px,py;
		for(i=0;i<sqrt(bektolx*bektolx+bektoly*bektoly)/*/sqrt(r*r)*/;i++){//進んだ分÷当たり判定分ループ
			px=imax-ZP.x;
			py=imay-ZP.y+9;
			if(px*px+py*py<r*r){
				TP.x=(int)prex;
				TP.y=(int)prey;
				return 1;
			}
			prex-=/*sqrt(r*r)*/1*sqrt(bektolx*bektolx+bektoly*bektoly)/bektolx;
			prey+=/*sqrt(r*r)*/1*sqrt(bektolx*bektolx+bektoly*bektoly)/bektoly;
		}
	}
	if(x*x+y*y<r*r){//当たり判定内なら
		return 1;//当たり
	}
	return 0;
}

void ZikiTamaAtariSyori(){//自機と玉の当たり判定処理
	if(ZP.muteki==0){
		if(ZikiTamaAtariJudge()){
			ZP.zanki--;
			ZP.muteki=100;
			GameStop=10;
		}
	}
}

void TamaMove(){
	//  ボールを表示する(メーター単位からピクセル単位への換算と+/-の逆転)。
	high = ( (double)((TP.ymax*sin(TP.ang*PI))-(max_y-TP.at-TM_sy)) / PMAX ) * TM_y;       //単位メーターから座標系への変換
	kyori = ((double)(TP.xmax*cos(TP.ang*PI))/PMAX)*TM_x;       //単位メーターから座標系への変換
	bektoly = ( (double)((TP.ymax*sin(TP.ang*PI))-(max_y-TP.at-TM_sy)) / PMAX ) * TM_vypf;
	bektolx = ((double)(TP.xmax*cos(TP.ang*PI))/PMAX)*TM_vxpf;
	TP.y = max_y - (int)high - (int)TP.at-starty;                //表示座標
	TP.x = max_x - (int)TP.at - startx + (int)kyori;//表示座標
	//  次のボールの位置の計算
	TM_vypf = TM_vypf - TM_gpf;
	TM_y = TM_y + TM_vypf;
	TM_x = TM_x - TM_vxpf;

	ZikiTamaAtariSyori();
	GamenAtariSyori();
}

void ZikiMove(){
	if( Key[ KEY_INPUT_RIGHT ] >= 1 && ZP.x<(max_x-12)){//左移動
		ZP.x+=(int)ZP.sp;
	}
	if( Key[ KEY_INPUT_LEFT ] >= 1 && ZP.x>(min_x+12)){//右移動
		ZP.x-=(int)ZP.sp;
	}
	if(Key[KEY_INPUT_A]>=1 && Key[ KEY_INPUT_RIGHT ] >= 1 && Key[ KEY_INPUT_LEFT ] <= 0){
		ZP.img=3;
	}
	else if(Key[KEY_INPUT_A]>=1 && Key[ KEY_INPUT_RIGHT ] <= 0 && Key[ KEY_INPUT_LEFT ] >= 1){
		ZP.img=2;
	}
	else if(Key[KEY_INPUT_A]>=1){
		ZP.img=1;
	}
	else{
		ZP.img=0;
	}
}

void EnemyOrderload(){
	int n,num,i,fp;
	char fname[]={"Estate.csv"};
	int input[27];
	char inputc[27];
	
	fp = FileRead_open(fname);//ファイル読み込み
	if(fp == NULL){
		printfDx("read error\n");
		return;
	}
	for(i=0;i<2;i++){//最初の2行読み飛ばす
		while(FileRead_getc(fp)!='\n');
	}

	n=0 , num=0;
	while(1){
		for(i=0;i<64;i++){
			inputc[i]=input[i]=FileRead_getc(fp);//1文字取得する
			if(inputc[i]=='/'){//スラッシュがあれば
				while(FileRead_getc(fp)!='\n');//改行までループ
				i=-1;//カウンタを最初に戻して
				continue;
			}
			if(input[i]==',' || input[i]=='\n'){//カンマか改行なら
				inputc[i]='\0';//そこまでを文字列とし
				break;
			}
			if(input[i]==EOF){//ファイルの終わりなら
				goto EXFILE;//終了
			}
		}
		switch(num){
		case 0: EO[n].hp_max      =atoi(inputc);break;
		case 1: EO[n].nolma       =atoi(inputc);break;
		case 2: EO[n].pattern     =atoi(inputc);break;
		case 3: EO[n].item        =atoi(inputc);break;
		case 4: EO[n].knd         =atoi(inputc);break;
		case 5: EO[n].at          =atoi(inputc);break;
		case 6: EO[n].attime      =atoi(inputc);break;
		case 7: EO[n].stage       =atoi(inputc);break;
		case 8: EO[n].timing      =atoi(inputc);break;
		case 9: EO[n].x           =atoi(inputc);break;
		case 10: EO[n].y          =atoi(inputc);break;
		}
		num++;
		if(num==11){
			num=0;
			n++;
		}
	}
EXFILE:
	FileRead_close(fp);
}

int EPnumsearch(){
	int i;
	for(i=0;i<EnemyMax;i++){
		if(EP[i].flag==0){
			return i;
		}
	}
	return -1;
}

void EnemyEnter(){
	int i,j;
	for(i=0;i<EnemyOrderMax;i++){
		if(StageCnt==EO[i].stage){
			if(GameCnt==EO[i].timing){
				if((j=EPnumsearch())!=-1){
					EP[j].flag=1;
					EP[j].cnt=0;
					EP[j].nolma=EO[i].nolma;
					EP[j].hp_max=EO[i].hp_max;
					EP[j].hp=EP[0].hp_max;
					EP[j].pattern=EO[i].pattern;
					EP[j].item=EO[i].item;
					EP[j].knd=EO[i].knd;
					EP[j].at=EO[i].at;
					EP[j].attime=EO[i].attime;
					EP[j].x=EO[i].x;
					EP[j].y=EO[i].y;
					EP[j].vx=0;
					EP[j].vy=0;
				}
			}
		}
	}
}

void EnemyPattern0(int z){
}

void Enemyact(){
	int i;
	for(i=0;i<EnemyMax;i++){
		if(EP[i].flag==1){
			EnemyPattern0(i);
			EP[i].cnt++;
		}
	}
}

void DrawEnemy(){
	int i;
	for(i=0;i<EnemyMax;i++){
		if(EP[i].flag==1){
			DrawRotaGraph(EP[i].x,EP[i].y,0.5,0.0,enemy_img,TRUE);
		}
	}
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
    ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定
 
    //  定数
	TM_gpf = g/fps;
    //  座標系
	TP.ymax=480;
	TP.xmax=960;
	TP.ang=1.0/4.0;
	TP.at=5;
	TM_sy = max_y-TP.at-starty;
	TM_PMAXf = sqrt(PMAX * 2.0 / TM_gpf);
 
    //  最大高さ到達までの時間(単位フレーム)から初速値単位(m/フレーム)を計算する。
    //  v0 = gt
    TM_v0pf = TM_gpf * TM_PMAXf;//初速度
 
    //  初速の設定
    TM_vypf = TM_v0pf;
	TM_vxpf = PMAX/(TM_PMAXf*2);
	TP_img=LoadGraph("img/tama.png");//玉の画像のロード
 
	ZP.img=0;//LoadGraph("img/ziki.png");//自機の画像のロード
	LoadDivGraph("img/ziki[4]_ver0.10.png",4,1,4,53,35,ZP_img);//自機の画像の分割ロード
	waku_img=LoadGraph("img/waku.png");
	ZP.x=(max_x+min_x)/2;//自機のx座標の初期化
	ZP.y=max_y-17;//自機のy座標の初期化
	ZP.zanki=5;//自機の残機の初期化
	ZP.muteki=0;
	ZP.sp=4.000;//自機のスピードの初期化
	ZP.at=8.000;//自機の当たり判定の初期化

	memset(EP,0,sizeof(EnemyPara_t)*EnemyMax);//敵データの初期化
	memset(EO,0,sizeof(EnemyOrder_t)*EnemyOrderMax);//敵出現データの初期化
	enemy_img=LoadGraph("img/block.png");

	EnemyOrderload();
	StageCnt++;
    // while( 裏画面を表画面に反映, メッセージ処理, 画面クリア )
    while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 && gpUpdateKey()==0 ){

		if(GameStop>0){
			GameStop--;
		}
		if(GameStop==0){
			if(ZP.muteki>0){
				ZP.muteki--;
			}
		}
		EnemyEnter();
		if(GameStop==0){
			Enemyact();
			ZikiMove();
			TamaMove();
		}
		DrawRotaGraph(ZP.x,ZP.y,1.0,0.0,ZP_img[ZP.img],TRUE);//自機の描画
		DrawEnemy();
		DrawRotaGraph( TP.x , TP.y ,1.0,0.0, TP_img , TRUE );//玉の描画
		DrawGraph(0,0,waku_img,TRUE);//枠の描画
		DrawFormatString(  0,0,GetColor(255,255,0),"残機:%d",ZP.zanki);
		//if(GameCnt==0){
		//	GameStop=100;
		//};//デバッグ用
		if(ZP.muteki>0){
			DrawFormatString(  0,20,GetColor(255,255,255),"無敵状態");
		}
		if(GameStop>0){
			DrawFormatString(  0,20,GetColor(0,0,255),"停止中");
		}
		if(GameStop==0){
			GameCnt++;
		}
    }
 
    DxLib_End(); // DXライブラリ終了処理
    return 0;
} 
添付ファイル
ziki[4]_ver0.10.png
ziki[4]_ver0.10.png (878 バイト) 閲覧数: 3257 回
tama.png
tama.png (200 バイト) 閲覧数: 3257 回

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

Re: ブロック崩しの自機とボールの当たり判定がうまくいかない

#2

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

Estate.csvがないと実行できないようです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

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

Re: ブロック崩しの自機とボールの当たり判定がうまくいかない

#3

投稿記事 by dast » 13年前

すみません、ここに置いておきます。
添付ファイル
Estate.csv
(238 バイト) ダウンロード数: 90 回

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

Re: ブロック崩しの自機とボールの当たり判定がうまくいかない

#4

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

EnemyOrderloadでEnemyOrderMaxを超えるバグがありまして私の所では動きません。
こういうのは念のためにガードするようにしましょう。
まず、このバグの修正をお願いします。

EnemyOrderloadで
Run-Time Check Failure #2 - Stack around the variable 'inputc' was corrupted.
と言うのも出てます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

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

Re: ブロック崩しの自機とボールの当たり判定がうまくいかない

#5

投稿記事 by dast » 13年前

うーん、inputcも関数の最初に宣言してますしEnemyOrderMaxも200用意してあるので構造は問題ないと思ったんですが……。
どうやって直したらいいと思いますか?

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

Re: ブロック崩しの自機とボールの当たり判定がうまくいかない

#6

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

dast さんが書きました:うーん、inputcも関数の最初に宣言してますしEnemyOrderMaxも200用意してあるので構造は問題ないと思ったんですが……。
細かく調べてないですが、何らかのバグでサイズを越えている可能性が大きいです。これを調べるのも勉強になるのでご自分で調べてみてください。
宣言やらガードに問題がある例 : for(i=0;i<64;i++){なのにchar inputc[27];と数があっていない。これは危険。
とにかく自分やデータを信用せず配列を超えそうなところには必ずガードを行なって下さい。
バグ原因の調査は、こちらを参考に。

「簡単RPG講座 番外編。 デバッグ入門 • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/blog.php?u=114&b=982&c=2
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

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

Re: ブロック崩しの自機とボールの当たり判定がうまくいかない

#7

投稿記事 by dast » 13年前

とりあえずinputとinputcの行列の数をfor(i=0;i<64;i++)の数に合わせた物を貼り付けておきます。
Run-Time Check Failure #2 - Stack around the variable 'inputc' was corrupted.が出ないかどうかそちらで確かめてください。
もう一つの方については手詰まりとなっています。こちらの方では全くエラーが出ていないのでASSERT処理も分割法も縮小法も役に立たず、メモリのサイズを変更してもこれまで通り普通に動くといった感じで何の進展もありません。
この場合、どうやってバクを見つければいいのでしょうか?

コード:

void EnemyOrderload(){
    int n,num,i,fp;
    char fname[]={"Estate.csv"};
    int input[64];
    char inputc[64];
    
    fp = FileRead_open(fname);//ファイル読み込み
    if(fp == NULL){
        printfDx("read error\n");
        return;
    }
    for(i=0;i<2;i++){//最初の2行読み飛ばす
        while(FileRead_getc(fp)!='\n');
    }
 
    n=0 , num=0;
    while(1){
        for(i=0;i<64;i++){
            inputc[i]=input[i]=FileRead_getc(fp);//1文字取得する
            if(inputc[i]=='/'){//スラッシュがあれば
                while(FileRead_getc(fp)!='\n');//改行までループ
                i=-1;//カウンタを最初に戻して
                continue;
            }
            if(input[i]==',' || input[i]=='\n'){//カンマか改行なら
                inputc[i]='\0';//そこまでを文字列とし
                break;
            }
            if(input[i]==EOF){//ファイルの終わりなら
                goto EXFILE;//終了
            }
        }
        switch(num){
        case 0: EO[n].hp_max      =atoi(inputc);break;
        case 1: EO[n].nolma       =atoi(inputc);break;
        case 2: EO[n].pattern     =atoi(inputc);break;
        case 3: EO[n].item        =atoi(inputc);break;
        case 4: EO[n].knd         =atoi(inputc);break;
        case 5: EO[n].at          =atoi(inputc);break;
        case 6: EO[n].attime      =atoi(inputc);break;
        case 7: EO[n].stage       =atoi(inputc);break;
        case 8: EO[n].timing      =atoi(inputc);break;
        case 9: EO[n].x           =atoi(inputc);break;
        case 10: EO[n].y          =atoi(inputc);break;
        }
        num++;
        if(num==11){
            num=0;
            n++;
        }
    }
EXFILE:
    FileRead_close(fp);
}

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

Re: ブロック崩しの自機とボールの当たり判定がうまくいかない

#8

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

どの様にして動作を確認したか、そのデバッグコードを貼り付けてみて下さい。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

閉鎖

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