斜方投射での跳ね返りに違和感がある

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

斜方投射での跳ね返りに違和感がある

#1

投稿記事 by dast » 13年前

こんにちは。ブロック崩しのゲームを作る準備として斜方投射と反発定数の原理に従って玉が画面内を跳ね返るプログラムを組んだのですが、それまでx座標の変化を1フレーム毎に指定した定数を足すという方式を採っていたのですが、それだと不便に感じたので水平到達距離と角度を指定する方式に変えようとソースを改変したのですが、右端と左端での跳ね返りのときに違和感を感じるようになりました。座標をリアルタイムで画面に表示させて見たところ、どうやら跳ね返りのときにx座標が飛んでいるような感じなのですがどの処理の時点で飛んでいるのか、どうやって改善したらいいのかが分かりません。一体どこがまずくてどういう風に直したらいいのでしょうか?

改変前のソース

コード:

#include "DxLib.h"
#include <math.h>
 
#define g 9.8067//定数:重力加速度
#define e 0.85//反発係数
#define y_max 2.000//定数
#define max_x 640//玉の移動範囲:右端
#define min_x 0//玉の移動範囲:左端
#define max_y 480//玉の移動範囲:床(ただし計算途中では天井)
#define min_y 0//玉の移動範囲:天井(ただし計算途中では床)
 
//#define maxy 400.000//玉の軌道の最高点
 
 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 i;//forループ用変数(使用関数:全て)
int TRAM_x=640, TRAM_y=0, TRAM_flag=0, TRAM_q=0, TRAM_time1, TRAM_time2;//(使用関数:TamaRifAndMove())
double TRAM_m=1.000, TRAM_t, TRAM_v0, TRAM_sy, TRAM_ssy, TRAM_zmaxy;//玉の向き上下判定, 経過時間, 初速度, 発射地点のy座標, 床を跳ねた時の発射地点のy座標, 玉の飛び上がる高さ(使用関数:TamaRifAndMove())
 
typedef struct{
    int x,y,image;
    double xsp,at,maxy;//x方向のスピード,当たり判定の半径,現在の軌道の最高点
}TamaPara_t;TamaPara_t TP;

typedef struct{
	int x,y,img;
	double sp,at;
}ZikiPara_t;ZikiPara_t ZP;
int ZP_img[4];
 
void TamaRifAndMove(){
    if( TRAM_flag==0 || TRAM_flag==2 ){ //床の当たり判定1
        TRAM_time1 = GetNowCount();           //time1にエンターが押された時の時間を格納
        TRAM_sy=TRAM_y;
        if(TRAM_flag==0){
            TRAM_ssy=TRAM_y;
        }
        TRAM_zmaxy=TP.maxy-TRAM_ssy;
        TRAM_flag=1;                          //飛び上がりフラグを立てる。
    }
    else if(TRAM_flag==1){
        TRAM_time2 = GetNowCount() ;                  // 現在経過時間を得る
        TRAM_t = (double)(TRAM_time2 - TRAM_time1) / 1000.000;  // ミリ秒を秒に変換して、エンターが押されてからの経過時間を計算
        TRAM_v0 = sqrt ( 2.000 * g * y_max);
        if(TRAM_m==-1.000){
            TRAM_v0 = TRAM_v0-g*TRAM_t;
        }
        for(i=0;i<TRAM_q;i++){
            TRAM_v0 *=e;
        }
        TRAM_y = TRAM_sy+(TRAM_m*TRAM_v0 * TRAM_t - 0.500 * g * TRAM_t * TRAM_t ) * TRAM_zmaxy / y_max;//yの計算
        TRAM_x=(TRAM_x+TP.xsp);//xの計算
        if(TRAM_y<(min_y+TP.at)){//床の当たり判定2
            TRAM_y=min_y+TP.at;
            TRAM_m=1.000;
            TRAM_q+=1;
            TRAM_flag=0;// 画面外に来ると、飛び上がりフラグを戻す
        }
        else if(TRAM_y>(max_y-TP.at)){//天井の当たり判定
            TRAM_y=max_y-TP.at;
            TRAM_m=-1.000;
            TRAM_flag=2;
        }
        if(TRAM_x<(min_x+TP.at)){//左端の当たり判定&処理
            TRAM_x=min_x+TP.at+1;
            TP.xsp*=(-1);
        }
        else if(TRAM_x>(max_x-TP.at)){//右端の当たり判定&処理
            TRAM_x=max_x-1-TP.at;
            TP.xsp*=(-1);
        }
        TP.y=(int)(480-TRAM_y);//玉のy座標の更新
        TP.x=(int)(TRAM_x);//玉のx座標の更新
    }
}
 
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
        ChangeWindowMode( TRUE ) ;          // ウインドウモードに変更
        if( DxLib_Init() == -1 ) return -1; //DXライブラリ初期化 エラーが起きたら終了 
        
        char Key[256];
 
        SetDrawScreen( DX_SCREEN_BACK ) ;//描画先を裏画面に設定
 
		//初期化
        TP.image=LoadGraph("img/tama.png");//玉の画像のロード
        TP.at=5;//玉の当たり判定の設定
        TP.xsp=-4.000;//x方向のスピード
        TP.maxy=600.00;//現在の軌道の最高点の初期化

		ZP.img=0;//LoadGraph("img/ziki.png");//自機の画像のロード
		LoadDivGraph("img/ziki[4].png",4,1,4,25,29,ZP_img);//自機の画像の分割ロード
		ZP.x=(max_x+min_x)/2;//自機のx座標の初期化
		ZP.y=max_y-14;//自機のy座標の初期化
		ZP.sp=4.000;
                                
        while(ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0){
                //ClearDrawScreen();//裏画面のデータを全て削除
                GetHitKeyStateAll( Key ) ;           // すべてのキーの状態を得る
                if( ProcessMessage() == -1 ) break ; //異常がおきたら終了
                TamaRifAndMove();

				if( Key[ KEY_INPUT_RIGHT ] >= 1 && ZP.x<(max_x-12)){//左移動
					ZP.x+=ZP.sp;
				}
				if( Key[ KEY_INPUT_LEFT ] >= 1 && ZP.x>(min_x+12)){//右移動
					ZP.x-=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;
				}
 
				DrawRotaGraph(ZP.x,ZP.y,1.0,0.0,ZP_img[ZP.img],TRUE);//自機の描画
				DrawRotaGraph( TP.x , TP.y ,1.0,0.0, TP.image , TRUE );//玉の描画
                DrawFormatString( 0, 0, GetColor(255,255,255), "%d",TP.y); // x,0 の位置に白で ?! を描画
                DrawFormatString( 0, 20, GetColor(255,255,255), "%d",TRAM_y); // x,0 の位置に白で ?! を描画
                DrawFormatString( 0, 40, GetColor(255,255,255), "%d",TRAM_flag); // x,0 の位置に白で ?! を描画
                DrawFormatString( 0, 60, GetColor(255,255,255), "%d",TRAM_q); // x,0 の位置に白で ?! を描画
        
                if( Key[ KEY_INPUT_ESCAPE ]  == 1 ) break;   //Escボタンが押されたらブレイク
 
                //ScreenFlip() ;                               //裏画面データを表画面へ反映
        }
        DxLib_End() ;                                    // DXライブラリ使用の終了処理
        return 0 ;                                       // ソフトの終了
} 
改変後のソース

コード:

#include "DxLib.h"
#include <math.h>
 
#define g 9.8067//定数:重力加速度
#define e 0.85//定数:反発係数
#define PI 3.141592//定数:円周率
#define y_max 2.000//定数
#define x_max 2.000//定数
#define max_x 640//玉の移動範囲:右端
#define min_x 0//玉の移動範囲:左端
#define max_y 480//玉の移動範囲:床(ただし計算途中では天井)
#define min_y 0//玉の移動範囲:天井(ただし計算途中では床)
 
//#define maxy 400.000//玉の軌道の最高点
 
 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 i;//forループ用変数(使用関数:全て)
int TRAM_x=640, TRAM_y=0, TRAM_flag=0, TRAM_q=0, TRAM_time1, TRAM_time2, TRAM_xtime1, TRAM_xtime2;//(使用関数:TamaRifAndMove())
double TRAM_m=1.000, TRAM_t, TRAM_v0, TRAM_sy, TRAM_ssy, TRAM_zmaxy, TRAM_sx, TRAM_xt, TRAM_xm=-1.000;//玉の向き上下判定, 経過時間, 初速度, 発射地点のy座標, 床を跳ねた時の発射地点のy座標, 玉の飛び上がる高さ(使用関数:TamaRifAndMove())
 
typedef struct{
    int x,y,image;
    double xsp,at,maxy,maxx;//x方向のスピード,当たり判定の半径,現在の軌道の最高点,現在の軌道の飛距離
}TamaPara_t;TamaPara_t TP;

typedef struct{
	int x,y,img;
	double sp,at;
}ZikiPara_t;ZikiPara_t ZP;
int ZP_img[4];
 
void TamaRifAndMove(){
    if( TRAM_flag==0 || TRAM_flag==2 ){ //床の当たり判定1
        TRAM_time1 = GetNowCount();           //time1にエンターが押された時の時間を格納
		TRAM_xtime1 = GetNowCount();
        TRAM_sy=TRAM_y;
		TRAM_sx=TRAM_x;
        if(TRAM_flag==0){
            TRAM_ssy=TRAM_y;
        }
        TRAM_zmaxy=TP.maxy-TRAM_ssy;
        TRAM_flag=1;                          //飛び上がりフラグを立てる。
    }
    else if(TRAM_flag==1){
        TRAM_time2 = GetNowCount() ;                  // 現在経過時間を得る
		TRAM_xtime2 = GetNowCount();
        TRAM_t = (double)(TRAM_time2 - TRAM_time1) / 1000.000;  // ミリ秒を秒に変換して、エンターが押されてからの経過時間を計算
		TRAM_xt = (double)(TRAM_xtime2 - TRAM_xtime1) / 1000.000;
        TRAM_v0 = sqrt ( 2.000 * g * y_max);
		TRAM_x=TRAM_sx+((g*x_max*TRAM_xt*TRAM_xm)/(2.000*TRAM_v0*sin(PI*0.25))*TP.maxx/x_max);
        if(TRAM_m==-1.000){
            TRAM_v0 = TRAM_v0-g*TRAM_t;
        }
        for(i=0;i<TRAM_q;i++){
            TRAM_v0 *=e;
        }
        TRAM_y = TRAM_sy+(TRAM_m*TRAM_v0 * TRAM_t - 0.500 * g * TRAM_t * TRAM_t ) * TRAM_zmaxy / y_max;//yの計算
        //TRAM_x=(TRAM_sx+TP.xsp*TRAM_xt*TRAM_xm);//xの計算
        if(TRAM_y<(min_y+TP.at)){//床の当たり判定2
            TRAM_y=min_y+TP.at;
            TRAM_m=1.000;
            TRAM_q+=1;
            TRAM_flag=0;// 画面外に来ると、飛び上がりフラグを戻す
        }
        else if(TRAM_y>(max_y-TP.at)){//天井の当たり判定
            TRAM_y=max_y-TP.at;
            TRAM_m=-1.000;
            TRAM_flag=2;
        }
        if(TRAM_x<(min_x+TP.at)){//左端の当たり判定&処理
            TRAM_x=min_x+TP.at+1;
			TRAM_sx=TRAM_x;
			TRAM_xm=1.000;
			TRAM_xtime2 = GetNowCount();
            TP.xsp*=(-1);
        }
        else if(TRAM_x>(max_x-TP.at)){//右端の当たり判定&処理
            TRAM_x=max_x-1-TP.at;
			TRAM_sx=TRAM_x;
			TRAM_xm=-1.000;
			TRAM_xtime2 = GetNowCount();
            TP.xsp*=(-1);
        }
        TP.y=(int)(480-TRAM_y);//玉のy座標の更新
        TP.x=(int)(TRAM_x);//玉のx座標の更新
    }
}
 
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
        ChangeWindowMode( TRUE ) ;          // ウインドウモードに変更
        if( DxLib_Init() == -1 ) return -1; //DXライブラリ初期化 エラーが起きたら終了 
        
        char Key[256];
 
        SetDrawScreen( DX_SCREEN_BACK ) ;//描画先を裏画面に設定
 
		//初期化
        TP.image=LoadGraph("img/tama.png");//玉の画像のロード
        TP.at=5;//玉の当たり判定の設定
        TP.xsp=-400.000;//x方向のスピード
        TP.maxy=600.00;//現在の軌道の最高点の初期化
		TP.maxx=400.00;

		ZP.img=0;//LoadGraph("img/ziki.png");//自機の画像のロード
		LoadDivGraph("img/ziki[4].png",4,1,4,25,29,ZP_img);//自機の画像の分割ロード
		ZP.x=(max_x+min_x)/2;//自機のx座標の初期化
		ZP.y=max_y-14;//自機のy座標の初期化
		ZP.sp=4.000;
                                
        while(ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0){
                //ClearDrawScreen();//裏画面のデータを全て削除
                GetHitKeyStateAll( Key ) ;           // すべてのキーの状態を得る
                if( ProcessMessage() == -1 ) break ; //異常がおきたら終了
                TamaRifAndMove();

				if( Key[ KEY_INPUT_RIGHT ] >= 1 && ZP.x<(max_x-12)){//左移動
					ZP.x+=ZP.sp;
				}
				if( Key[ KEY_INPUT_LEFT ] >= 1 && ZP.x>(min_x+12)){//右移動
					ZP.x-=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;
				}
 
				DrawRotaGraph(ZP.x,ZP.y,1.0,0.0,ZP_img[ZP.img],TRUE);//自機の描画
				DrawRotaGraph( TP.x , TP.y ,1.0,0.0, TP.image , TRUE );//玉の描画
                DrawFormatString( 0, 0, GetColor(255,255,255), "%d",TP.x); // x,0 の位置に白で ?! を描画
                DrawFormatString( 0, 20, GetColor(255,255,255), "%d",TRAM_x); // x,0 の位置に白で ?! を描画
                DrawFormatString( 0, 40, GetColor(255,255,255), "%d",TRAM_flag); // x,0 の位置に白で ?! を描画
                DrawFormatString( 0, 60, GetColor(255,255,255), "%f",TRAM_sx); // x,0 の位置に白で ?! を描画
        
                if( Key[ KEY_INPUT_ESCAPE ]  == 1 ) break;   //Escボタンが押されたらブレイク
 
                //ScreenFlip() ;                               //裏画面データを表画面へ反映
        }
        DxLib_End() ;                                    // DXライブラリ使用の終了処理
        return 0 ;                                       // ソフトの終了
} 

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

Re: 斜方投射での跳ね返りに違和感がある

#2

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

とりあえず画像がなかったのでDrawCircleで代用させてもらいました。
ぱっと見せてもらいましたがベクトル組んだ方がシンプルになるのでXとYのベクトルで処理するものを目指された方がよいと思います。
ベクトルは理解できますか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

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

Re: 斜方投射での跳ね返りに違和感がある

#3

投稿記事 by dast » 13年前

vx=v0cosθ,vy=v0sinθt-1/2gt^2を1フレーム毎に足していくやり方ですね。確かに当たり判定を計算する段階ではこちらの方が扱いやすそうですね。ちょっと試してみます。分からなくなって新しいトピックを立てる未来がありありと見えますけど;
あと反射がおかしくなる件は自己解決しました。
88行目と95行目のTRAM_xtime1 = GetNowCount();となる筈のところがTRAM_xtime2 = GetNowCount();となっていただけでした。つまらぬことでトピックを立ててすみませんでした。

びす

Re: 斜方投射での跳ね返りに違和感がある

#4

投稿記事 by びす » 13年前

vx = v0cosθ
vy=v0sinθ-gt
x = (v0cosθ)t
y=(v0sinθ)t-1/2gt^2
じゃないでしょうか

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

Re: 斜方投射での跳ね返りに違和感がある

#5

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

ゲームループ処理でベクトルで処理する場合はtは入れなくて良いです。vx,vyを初速で初期化、vy方向に重力加速度を加味すること反射したらvx,vyを反発係数、反射方向で書き換えることだけが必要です。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

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

Re: 斜方投射での跳ね返りに違和感がある

#6

投稿記事 by dast » 13年前

>>ソフト屋さん
重力加速度を加味するというのは鉛直投げ上げの運動式のv0-gtの部分だと思うのですが、しかしtは必要ないとなると一体どういう風に加味していけばいいのでしょうか?試しに一回真上に投げ上げるだけの関数として

コード:

void TamaRifAndMove(){
	TRAM_v0 = sqrt ( 2.000 * g * y_max)*TP.maxy/y_max;
	TRAM_vy = (TRAM_v0-g*TRAM_w)*TP.maxy/y_max;
	TP.y-=TRAM_vy;
	TRAM_w++;
}
として動かしてみたのですが全然思うように動いてくれません。とりあえず今のところは重力加速度は9.8のままでいいのか、またどういう風にvyに加味していけばいいのか、初速度はこのままでいいのか、一度最高点をy_max=2.000(m)と置いてから*TP.maxy(ピクセル)/y_maxとする手順は必要なのか等を教えていただければ幸いです。それ以上はたぶん理解が追い付かないと思うので・・・

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

Re: 斜方投射での跳ね返りに違和感がある

#7

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

1.とりあえず初速はvx,vyに最初に初期値として与えて下さい。速度は描画周期が1/60フレーム/秒ですから秒速の1/60と考える必要があります。
vy=秒速/60.0;

2.加速度も同様に1/60フレームで考えます。垂直方向の速度の毎フレームの式は上方向を+としたら
vy=vy-9.8/60.0;
となるわけです。

3.位置は、毎フレーム速度を加算するだけですので
y=y+vy;
となります。

ちなみに+/-方向は表示座標系を考慮して表示時に変換して下さい。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

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

Re: 斜方投射での跳ね返りに違和感がある

#8

投稿記事 by dast » 13年前

これから追加する処理に初速度をいじる処理があるかもしれないので「玉を一回真上にTP.maxyだけ投げ上げる」処理として関数TamaRifAndMove(){}を

コード:

	TRAM_v0 = sqrt ( 2.000 * g * y_max)*TP.maxy/(y_max);//初速度の秒速を計算する

	TRAM_vy = (TRAM_v0-g*TRAM_w)/60;
	TRAM_w++;
	TP.y=(TP.y-TRAM_vy);
としてみました。一応提案していただいた処理と同じ内容だと思います。
これで動かしたら設定したTP.maxyよりも高い位置まで投げ上げてしまいました。
この場合初速度の計算に問題があると思うのですが、何が起こって設定よりも高く投げ上げてしまうのでしょうか?

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

Re: 斜方投射での跳ね返りに違和感がある

#9

投稿記事 by dast » 13年前

書き忘れていたので追記
初期値は
TRAM_w=0,TP.y=635,TP.x=475にしています。

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

Re: 斜方投射での跳ね返りに違和感がある

#10

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

間違いに気づいたので修正中。
► スポイラーを表示
原因は分かったのですが年末で時間が無いので時間を下さい。正月前には更新できる予定。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

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

Re: 斜方投射での跳ね返りに違和感がある

#11

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

出来ました~。

コード:

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

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

	//	定数
	const double PMAX = 500.0;		//最大高さ到達位置(単位m)
	const double g = 9.8;			//重力加速度(単位m/s^2)
	const double fps = 60.0;		//フレーム/秒
	const double gpf = g / fps;		//1フレームあたりの重力加速度
	
	//	座標と速度
	double y = 0;			//ボールの位置(単位m)
	double v0pf = 0;		//初速(単位m/フレーム)
	double vypf = 0;		//ボールのベクトル速度(単位m/フレーム)
	
	//	座標系
	int grand_lineY = 400;	//	地面の表示座標
	int top_lineY = 100;	//	頂点の表示座標
	int ball_r = 10;
	
	//	最大高さ到達までの時間(単位フレーム)。速度が0になるポイント。
	//	V = v0 - gt → 0 = v0 - gt → v0 = gt
	//	P = v0t - 1/2gt^2 → P = gt^2 - 1/2gt^2 → P = 1/2gt^2 → t^2 = P*2/g
	//		↓
	//	t = √(P*2/g) 
	double PMAXf = sqrt(PMAX * 2.0 / gpf);

	//	最大高さ到達までの時間(単位フレーム)から初速値単位(m/フレーム)を計算する。
	//	v0 = gt
	v0pf = gpf * PMAXf;

	//	初速の設定
	vypf = v0pf;

	// while( 裏画面を表画面に反映, メッセージ処理, 画面クリア )
	while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){
		//	地面と到達点。
		DrawLine( 0,top_lineY, 640,top_lineY, GetColor(0,255,255) );
		DrawFormatString( 0,top_lineY-8, GetColor(255,255,255), "高さ=%3.1fm", PMAX );
		DrawBox( 0,grand_lineY, 640,480, GetColor(128,64,64), TRUE );
		DrawFormatString( 0,grand_lineY-8, GetColor(255,255,255), "高さ=0m" );
		
		//	ボールを表示する(メーター単位からピクセル単位への換算と+/-の逆転)。
		double pos_high = ( (double)(grand_lineY-top_lineY) / PMAX ) * y;		//単位メーターから座標系への変換
		int posy = grand_lineY - (int)pos_high - ball_r;				//表示座標
		DrawCircle( 640/2, posy, ball_r, GetColor(255,255,255), TRUE );
		
		//	位置と速度を表示
		DrawFormatString( 0,420, GetColor(255,255,255), "頂点到達時間=%5.3fフレーム 初速=%5.3fm/フレーム", PMAXf, v0pf );
		DrawFormatString( 0,440, GetColor(255,255,255), "頂点到達時間=%5.3f秒    初速=%5.3fm/秒", PMAXf/fps, v0pf*fps );
		DrawFormatString( 0,460, GetColor(255,255,255), "位置=%5.3fm  速度=%5.3fm/フレーム", y, vypf );
		
		//	次のボールの位置の計算
		vypf = vypf - gpf;
		y = y + vypf;
		
		//	床の下に来たら無理やり反射。反発係数80%
		if( y < 0 ) {
			y = 0;
			vypf = -vypf * 0.8;
		}
	}

	DxLib_End(); // DXライブラリ終了処理
	return 0;
}
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

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

Re: 斜方投射での跳ね返りに違和感がある

#12

投稿記事 by dast » 13年前

わざわざプログラム例を作っていただきありがとうございました。それと返信及びお礼を言うのがが遅くなってしまってすみません。
早速作っていただいたプログラムを改変して最高点、落下点、玉が動くx座標・y座標の範囲、玉の初期位置のx・y座標を指定して玉をバウンドさせながら動かすプログラムを作ってみたのですが、玉の初期位置の部分を0,0以外にした時の跳ね方がおかしくなってしまいました。(320,240にした時が特におかしいと感じました)しかし、計算式を見直してみても一体どこでミスが起きているのかが分かりません。一体どこが間違っているのでしょう?

コード:

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

#define startx 320//玉の初期値x座標の右壁からの距離(0~max_x+TP.at-1)
#define starty 240//玉の初期値y座標の床からの距離(0~max_y+TP.at-1)
#define max_y 480//yの最大値
#define min_y 0  //yの最小値
#define max_x 640//xの最大値
#define min_x 0  //xの最小値
//定数
#define PMAX 500.0//最大高さ到達位置(単位m)
#define g 9.8//重力加速度(単位m/s^2)
#define fps 60.0//フレーム/秒

typedef struct{//玉のパラメータ
	int x,y;//x座標,y座標
	double at,ymax,xmax;//当たり判定、現軌道の最高点
}TamaPara_t;TamaPara_t TP;
 
int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
    ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定
 
    //  定数
    double gpf = g / fps;     //1フレームあたりの重力加速度
    
    //  座標と速度
    double y = 0;           //ボールの位置(単位m)
	double x = 0;
	//double startx;
    double v0pf = 0;        //初速(単位m/フレーム)
    double vypf = 0;        //ボールのyベクトル速度(単位m/フレーム)
	double vxpf = 0;        //ボールのxベクトル速度(単位m/フレーム)
    
    //  座標系
	TP.ymax=800;
	TP.xmax=400;
	TP.at=5;
	double sy;
	sy = max_y-TP.at-starty;

	int w=0;
 
    double PMAXf = sqrt(PMAX * 2.0 / gpf);//  最高点到達時間(単位フレーム)。
 
    //  最大高さ到達までの時間(単位フレーム)から初速値単位(m/フレーム)を計算する。
    //  v0 = gt
    v0pf = gpf * PMAXf;//初速度
 
    //  初速の設定
    vypf = v0pf;
	vxpf = PMAX/(PMAXf*2);
 
    // while( 裏画面を表画面に反映, メッセージ処理, 画面クリア )
    while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){
        //  ボールを表示する(メーター単位からピクセル単位への換算と+/-の逆転)。
		double high = ( (double)(TP.ymax-(max_y-TP.at-sy)) / PMAX ) * y;       //単位メーターから座標系への変換
		double kyori = ((double)TP.xmax/PMAX)*x;       //単位メーターから座標系への変換
        TP.y = max_y - (int)high - TP.at-starty;                //表示座標
		TP.x = max_x - TP.at - startx + (int)kyori;//表示座標
        DrawCircle( TP.x, TP.y, TP.at, GetColor(255,255,255), TRUE );
        
        //  次のボールの位置の計算
        vypf = vypf - gpf;
        y = y + vypf;
		x = x - vxpf;
        
        //  床の下に来たら無理やり反射。反発係数80%
		if( TP.y > max_y-TP.at ) {
            y = 0;
            vypf = -vypf * 0.8;
        }
		else if( TP.y <min_y+TP.at){
			y = (PMAX*(-1)*(min_y-max_y+2.00*TP.at+starty))/(TP.ymax-(max_y-TP.at-sy));
			vypf = -vypf;
		}
		if( TP.x<min_x+TP.at){//左壁の当たり判定
			x = (min_x-max_x+2.00*TP.at+startx)*PMAX/TP.xmax;
			vxpf = -vxpf;
		}
		else if( TP.x > max_x-TP.at ){//右壁の当たり判定
			x = 0;
			vxpf = -vxpf;
		}
    }
 
    DxLib_End(); // DXライブラリ終了処理
    return 0;
} 


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

Re: 斜方投射での跳ね返りに違和感がある

#13

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

こういう物は物事を単純にするために、まずxかyから直しましょう。と言うことでまずxベクトルは0にしてみましょう。どの処理が悪さをしているかチェックします。どこの条件判断で問題が出ていますでしょうか?
あと、メートル(m)を基準にするのか表示座標を基準にするのかどっちつかずなコードだと思います。
私としては表示以外の当たり判定もメートルで統一することをお勧めしたいです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

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

Re: 斜方投射での跳ね返りに違和感がある

#14

投稿記事 by dast » 13年前

vxとvyを片方ずつ0にして調べてみたら床と右側の壁の処理に問題がありました。どうやら初期位置を設定したことでx=0,y=0となった時に必ずしもTP.x,TP.yが床や右の壁の位置になるとは限らなくなってしまったようです。なので、69行目と81行目のx,yの値を
x = (startx*PMAX)/(TP.xmax*cos(TP.ang*PI));
y = (-1)*PMAX*starty/((TP.ymax*sin(TP.ang*PI))-(max_y-TP.at-TM_sy));
と直したらちゃんとした反射になりました。ありがとうございます。

あと、基準をメートルか表示座標のどっちかで統一した方がいいと指摘されましたが、TP.x,TP.yがメートル計算に依存しているので処理をメートル単位で行うしかなく、私は当たりの条件が座標として分かった方が使いやすいのでこのままでいきたいと思います。統一した方がいいのは重々承知していますが、「画面で言うとこのへん」っていうのがすぐに分からないとやり難いのでこのままでいきます。ご指摘を無駄にしてしまってすみません。

閉鎖

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