トップページ > 過去ログ > 記事閲覧
壁と床の当たり判定
名前:もあい 日時: 2011/11/28 14:52

14歳からはじめるC言語わくわくゲームプログラミング教室」という参考書で勉強しています。自分で新しくマップを作ったのですが、ジャンプする位置によって、壁をすり抜けてしまい、凸部分で前の壁をすり抜け、後ろの壁にあたって、落ちてゲームオーバーといいった状態になってしまいます。 #include "myhelper.h" #include "mymain.h" #include "stage1.h" //ステージ切り替え用の関数ポインタ void (*pf_initgamefunc)() = InitializeGame01; void (*pf_drawenemyfunc)() = DrawEnemies01; BOOL (*pf_checkclearfunc)() = CheckClearJyouken01; //前回のキーの状態を記録する BOOL g_akey_previous = FALSE; BOOL g_bkey_previous = FALSE; //自キャラのデータ Point2D g_jikipos = {40, 340}; //自キャラの座標 Vector g_jikivector = {0, 0}; //自キャラのベクトル BOOL g_direction = DIRECTION_RIGHT; //自キャラの向き BOOL g_nowsit = FALSE; //現在しゃがみ中か否か BOOL g_nowjumping = FALSE; //現在ジャンプ中か否か //自キャラの武器 Weapon g_weapons[MAXWEAPON]; //敵キャラのデータ int g_usedenemy_num = 0; //実際に使用する数 Enemy g_enemies[MAXENEMY]; //地面を表す線 int g_usedline_num = 1; //実際に使用している線の数 Line2D g_landlines[MAXLANDLINE]; //画像ハンドル int g_jikiimage[11]; int g_backimage; int g_wallimage; int g_weaponimage; int g_bulletimage; int g_titleimage; int g_clashimage[3]; int g_hitimage; //音声ファイル int g_backmusic; //バックで再生する音楽 //効果音 int g_clip1, g_clip2, g_clip3, g_clip4, g_clip5; //爆発などの一時アニメーション表示 SplashAnimation g_splashes[MAXSPLASH]; //ゲーム状態の記録 GameState g_gamestate = GAME_TITLE; //一時アニメーションの表示関数 void DrawSplash(){ for (int i=0; i<MAXSPLASH; i++ ){ if ( g_splashes[i].used == TRUE ){ DrawAnimation( g_splashes[i].position.x, g_splashes[i].position.y, 1, 0, g_splashes[i].hanten, g_splashes[i].imgarray, g_splashes[i].allframe, g_splashes[i].fps); //表示時間を超えたら終了 if ( (GetNowCount() & INT_MAX) - g_splashes[i].starttime > g_splashes[i].life ){ g_splashes[i].used = FALSE; } } } } //一時アニメーションのセット void SetSplash(SplashAnimation sp){ sp.used = TRUE; //念のため sp.starttime = (GetNowCount() & INT_MAX); //表示開始時間設定 for (int i=0; i<MAXSPLASH; i++ ){ if ( g_splashes[i].used == FALSE ){ g_splashes[i] = sp; } } } /*地面との当たり判定 当たり判定を行うBall2Dの配列と要素数を与え、衝突していればTRUEを返す。 */ bool CollisionLandLines( Ball2D *ballarray, int ballnum, Line2D **pphitland, Line2D **pphitwall ){ bool result = FALSE; Line2D *presultland = NULL; Line2D *presultwall = NULL; for(int i=0; i<g_usedline_num; i++){ for(int j=0; j<ballnum; j++){ //衝突チェック if ( HitTestLineAndBall(g_landlines[i], ballarray[j]) == TRUE ){ //衝突した線が90度±20度の場合、その線は壁とみなす if ( PIE/2.0 - fabs(g_landlines[i].katamuki) < PIE/9.0f){ result = TRUE; if (presultwall == NULL) presultwall = &g_landlines[i]; } else { //そうでないなら地面 //地面に対して円の中心が上側にあるかチェック if ( IsPointAtLineFace(g_landlines[i], ballarray[j].position) == TRUE ){ result = TRUE; if (presultland == NULL) presultland = &g_landlines[i]; } } } } } //Line2Dを返す if (pphitland != NULL) *pphitland = presultland; if (pphitwall != NULL) *pphitwall = presultwall; return result; } //武器の発射処理 void ShootWeapon(){ for (int i=0; i<MAXWEAPON; i++){ if (g_weapons[i].used == FALSE){ //左右共通の初期値セット g_weapons[i].used = TRUE; g_weapons[i].position = g_jikipos; g_weapons[i].movec = WEAPONSPEED; g_weapons[i].hanten = g_direction; //g_directionがFALSEなら右、TRUEなら左へ発射 if (g_direction==TRUE){ g_weapons[i].movec.x = -WEAPONSPEED.x; SetSimpleTimer(0, 200); PlaySoundMem(g_clip2, DX_PLAYTYPE_BACK); } break; } } } //武器の表示と移動 void DrawWeapon(){ for (int i=0; i<MAXWEAPON; i++){ if (g_weapons[i].used == TRUE){ g_weapons[i].position = AddVectorInFrameTime( g_weapons[i].position, g_weapons[i].movec ); DrawAnimation( g_weapons[i].position.x, g_weapons[i].position.y, 1, 0, g_weapons[i].hanten, &g_weaponimage, 1, 1.0f); //武器が画面範囲から出たらusedをFALSEにする if ( HitTestPointAndBox(g_framerect, g_weapons[i].position) == FALSE ){ g_weapons[i].used = FALSE; } } } } /*敵と武器、自キャラの当たり判定 自キャラが消滅したらTRUEを返す*/ bool CollisionCharacter(){ for (int i=0; i<g_usedenemy_num; i++){ if (g_enemies[i].used == TRUE){ Rect2D b = { {g_enemies[i].position.x - 20, g_enemies[i].position.y-50}, {g_enemies[i].position.x + 20, g_enemies[i].position.y+70}, 40,120 }; //敵と武器の当たり判定 for (int j=0; j<MAXWEAPON; j++){ if (g_weapons[j].used == TRUE){ Line2D w = { {g_weapons[j].position.x - WEAPONVECTOR.x/2, g_weapons[j].position.y - WEAPONVECTOR.y/2}, {g_weapons[j].position.x + WEAPONVECTOR.x/2, g_weapons[j].position.y + WEAPONVECTOR.y/2} }; if ( HitTestLineAndRect(w,b) == TRUE ){ //衝突したら敵のライフを1減らす g_enemies[i].life--; //敵消滅 if ( g_enemies[i].life == 0 ) { g_enemies[i].used = FALSE; //敵消滅時アニメーションの表示 Vector v = {15,0}; SplashAnimation clashsp = { TRUE, 250, //0.25秒 0, //現在時間はSetSplash側で設定 AddVector(g_enemies[i].position, v), g_clashimage, 3, 12.0f, !(g_weapons[i].hanten) }; SetSplash(clashsp); PlaySoundMem(g_clip4, DX_PLAYTYPE_BACK); }else{ if (g_enemies[i].life > 0 ){ //ヒット時アニメーションの表示 SplashAnimation hitsp = { TRUE, 100, //0.1秒 0, //現在時間はSetSplash側で設定 g_enemies[i].position, &g_hitimage, 1, 1.0f, !(g_weapons[i].hanten) }; SetSplash(hitsp); PlaySoundMem(g_clip3, DX_PLAYTYPE_BACK); } //どちらの条件も満たさない(つまりlifeが最初から負の値)のときは //敵は武器が当たってもダメージを受けない } g_weapons[j].used = FALSE; //lifeが最初から負の値のときは //敵は武器が当たってもダメージを受けない } } } //敵と自キャラの当たり判定 Rect2D m = { {g_jikipos.x - 20, g_jikipos.y-50}, {g_jikipos.x + 20, g_jikipos.y+70}, 40,120 }; if(HitTestRectAndRect(b,m) == TRUE){ return TRUE; } } } return FALSE; } /*ゲーム状態切り替え用のグローバル変数と関数群*/ //ゲームオーバー状態に移行する Point2D gmbodypos; //ゲームオーバー用の自キャラ座標 Vector gmbodyspd; //移動速度 void GoGameOver(){ //すでにゲームオーバー状態なら即脱出 if (g_gamestate == GAME_OVER) return; gmbodypos = g_jikipos; gmbodyspd = JUMPMOVEC; if (g_direction == DIRECTION_RIGHT){ gmbodyspd.x = -40.0f; } else { gmbodyspd.x = 40.0f; } g_jikipos.y = 10000000; g_jikivector.x = 0; g_jikivector.y = 0; g_gamestate = GAME_OVER; SetSimpleTimer(31,7000); StopSoundMem( g_backmusic ); PlaySoundMem( g_clip5, DX_PLAYTYPE_BACK ); } //ゲームクリアー状態に移行する void GoGameClear(){ //すでにゲームクリアー状態なら即脱出 if (g_gamestate == GAME_CLEAR) return; g_gamestate = GAME_CLEAR; SetSimpleTimer(31,7000); StopSoundMem( g_backmusic ); } //ゲームタイトル状態に移行する void GoGameTitle(){ //すでにゲームタイトル状態なら即脱出 if (g_gamestate == GAME_TITLE) return; g_gamestate = GAME_TITLE; StopSoundMem( g_backmusic ); } //ゲームスタート状態に移行する void GoGameMain(){ //すでにゲーム中なら即脱出 if (g_gamestate == GAME_MAIN) return; //ここにステージ切り替え? pf_initgamefunc(); //データ初期化 g_gamestate = GAME_MAIN; PlaySoundMem( g_clip1, DX_PLAYTYPE_BACK ); PlaySoundMem( g_backmusic, DX_PLAYTYPE_LOOP ); } //ゲームオーバー、ゲームクリアー、ゲームタイトル画面の描画 void DrawGameOver(){ gmbodypos = AddVectorInFrameTime(gmbodypos, gmbodyspd); gmbodyspd = AddVectorInFrameTime(gmbodyspd, GRAVITY); DrawAnimation(gmbodypos.x, gmbodypos.y, 1, 0, g_direction, &g_jikiimage[5], 1, 1.0f); if ( GetPassedTime(31)> -5000) { int col = GetColor(255,0,0); SetFontSize(100); SetFontThickness(9); ChangeFontType(DX_FONTTYPE_EDGE); int x = 320 - GetDrawStringWidth("GAMEOVER",8)/2; DrawString(x,160, "GAMEOVER", col); } if ( GetPassedTime(31)>0 ) GoGameTitle(); } void DrawGameClear(){ if ( GetPassedTime(31)> -5000) { int col = GetColor(0,255,255); SetFontSize(100); SetFontThickness(9); ChangeFontType(DX_FONTTYPE_EDGE); int x = 320 - GetDrawStringWidth("GAMECLEAR",9)/2; DrawString(x,160, "GAMECLEAR", col); } if ( GetPassedTime(31)>0 ) GoGameTitle(); } void DrawGameTitle(){ DrawRotaGraph(320,140, 1,0, g_titleimage, TRUE, FALSE); int col = GetColor(255,0,255); SetFontSize(32); SetFontThickness(9); int x = 320 - GetDrawStringWidth("PUSH Z KEY",10)/2; DrawString(x,300, "PUSH Z KEY", col); //キー入力チェック int key = GetJoypadInputState(DX_INPUT_KEY_PAD1); if (key & PAD_INPUT_A){ if (g_akey_previous == FALSE){ g_akey_previous = TRUE; GoGameMain(); } }else{ g_akey_previous = FALSE; } //ステージ切り替え if ( CheckHitKey(KEY_INPUT_1) == 1 ){ pf_initgamefunc = InitializeGame01; pf_drawenemyfunc = DrawEnemies01; pf_checkclearfunc = CheckClearJyouken01; } } //ゲームメイン描画 void DrawGameMain(){ //背景描画 DrawGraph(0,0, g_backimage, FALSE); //地形を描画 DrawAnimation(g_stagesize.width/2 , g_stagesize.height/2, 1, 0, FALSE, &g_wallimage, 1, 1.0f); #ifdef _DEBUG for( int i=0; i<g_usedline_num; i++ ){ DrawLine2D(g_landlines[i], GetColor(128,128,128), 4); } #endif //敵の処理の呼び出し pf_drawenemyfunc(); Vector curvec1 = {0,0} ; int key = GetJoypadInputState( DX_INPUT_KEY_PAD1 ); if (key & PAD_INPUT_LEFT) { curvec1 = AddVector( curvec1, LEFTMOVEC ); g_direction = DIRECTION_LEFT; } if (key & PAD_INPUT_RIGHT){ curvec1 = AddVector( curvec1, RIGHTMOVEC ); g_direction = DIRECTION_RIGHT; } if(key & PAD_INPUT_DOWN){ g_nowsit = TRUE; }else{ g_nowsit = FALSE; } //地面との当たり判定 BOOL ontheground = FALSE; //着地フラグ Ball2D jikifeet[2] = { { {g_jikipos.x - 8, g_jikipos.y + 55} , 8}, { {g_jikipos.x + 8, g_jikipos.y + 55} , 8} }; Line2D *phitland, *phitwall; bool hit = CollisionLandLines( jikifeet, 2, &phitland, &phitwall ); if (hit == TRUE){ //地面としてヒットした線があるかチェック if ( phitland != NULL ){ //自キャラが下方向に移動しているかチェックする g_jikivector.x = 0; if ( (g_nowjumping == TRUE) ){ //ジャンプ中 if ( g_jikivector.y > 0 ){ g_jikivector.y = 0; g_nowjumping = FALSE; } } else { //非ジャンプ中 g_jikivector.y = 0; } ontheground = TRUE; //線の傾きを反映 curvec1 = RotateVector(curvec1, phitland->katamuki); } //壁としてヒットした線があるかチェック if ( phitwall != NULL ){ //もし移動方向に壁があるなら、その方向に移動できないようにする Point2D curpos = { g_jikipos.x, g_jikipos.y+26 }; Line2D myline1 = { curpos, AddVector(curpos, g_jikivector) }; Line2D myline2 = { curpos, AddVector(curpos, curvec1) }; if( (HitTestLineAndLine(myline1, *phitwall) == TRUE) || (HitTestLineAndLine(myline2, *phitwall) == TRUE) ){ curvec1.x = 0; curvec1.y = 0; g_jikivector.x = 0; } } } //ジャンプ処理 if (key & PAD_INPUT_A){ if ( g_akey_previous == FALSE ){ curvec1 = AddVector( curvec1, JUMPMOVEC ); g_nowjumping = TRUE; g_akey_previous = TRUE; } } else { g_akey_previous = FALSE; } //弾の発射・移動 if (key & PAD_INPUT_B) { if (g_bkey_previous == FALSE){ ShootWeapon(); //武器を発射したらその時点の移動はストップする curvec1.x = 0; curvec1.y = 0; g_bkey_previous = TRUE; } } else { g_bkey_previous = FALSE; } DrawWeapon(); //自キャラ座標の計算 if ( ontheground == TRUE ){ //接地時 g_jikivector = AddVector( g_jikivector, curvec1 ); } else { //ジャンプ中 g_jikivector = AddVectorInFrameTime( g_jikivector, GRAVITY ); if(g_jikivector.y > 400) g_jikivector.y = 400; } //自キャラ座標の計算 g_jikipos = AddVectorInFrameTime( g_jikipos, g_jikivector); //自キャラの描画 if( (key & PAD_INPUT_B) && (GetPassedTime(0) < 500) ){ //武器の発射 DrawAnimation(g_jikipos.x,g_jikipos.y, 1, 0, g_direction, &g_jikiimage[6],2, 4.0f); }else{ if ( g_nowsit == FALSE ){ if ( fabs(curvec1.x) > ZEROVALUE ){ //歩く DrawAnimation( g_jikipos.x,g_jikipos.y, 1, 0, g_direction, &g_jikiimage[1],4, 10 ); }else{ //停止状態 DrawAnimation( g_jikipos.x,g_jikipos.y, 1, 0, g_direction, &g_jikiimage[0],1, 12 ); } }else{ DrawAnimation(g_jikipos.x,g_jikipos.y,1,0,g_direction, &g_jikiimage[8],1,12); } } //スプラッシュアニメーションの表示 DrawSplash(); //スクロール if (XInView(g_jikipos.x) <= SCROLL_LIMIT) ScrollToLeft(g_jikipos.x); if (XInView(g_jikipos.x) >= 639 - SCROLL_LIMIT) ScrollToRight(g_jikipos.x); if (YInView(g_jikipos.y) <= SCROLL_LIMIT) ScrollToUp(g_jikipos.y); if (YInView(g_jikipos.y) >= 479 - SCROLL_LIMIT) ScrollToDown(g_jikipos.y); //終了判定 if ( g_gamestate == GAME_MAIN ){ //敵と武器、自キャラの当たり判定 if ( CollisionCharacter() == TRUE ) GoGameOver(); //自キャラが画面外に落ちてもゲームオーバー if ( g_jikipos.y > 1000 ) GoGameOver(); //ゲームクリアチェック if (pf_checkclearfunc() == TRUE) GoGameClear(); } } //メインループ void MyMain(){ switch(g_gamestate){ case GAME_TITLE: DrawGameTitle(); break; case GAME_MAIN: DrawGameMain(); break; case GAME_OVER: DrawGameMain(); DrawGameOver(); break; case GAME_CLEAR: DrawGameMain(); DrawGameClear(); break; } } //マップ用のラインデータを読み込む関数 int LoadMap(char *pfname){ int f; //ファイルハンドル char buf[1024]; //テキスト一時読み込み一時バッファ f = FileRead_open(pfname); if (f==0) return -1; //読み込みエラー g_usedline_num = 0; while( FileRead_eof(f) == 0 ) //終端チェック { // 一行読み込み FileRead_gets( buf, 1023, f ) ; int sx,sy,ex,ey; sscanf_s(buf, "%d,%d,%d,%d", &sx, &sy, &ex, &ey); Line2D l = { {(float)sx, (float)sy}, {(float)ex, (float)ey}, 0, {0,0}}; SetLine2DKatamuki(&l); g_landlines[g_usedline_num] = l; g_usedline_num++; } // ファイルを閉じる FileRead_close( f ) ; return 0; } //色々なファイルの読み込み int LoadFiles(){ //画像ファイルの読み込み if (LoadDivGraph("media\\player.bmp", 11,6,2,113,134,g_jikiimage) == -1) return -1; g_weaponimage = LoadGraph("media\\weapon01.bmp", 0); if (g_weaponimage == -1) return -1; g_titleimage = LoadGraph("media\\title01.bmp", 0); if (g_titleimage == -1) return -1; if (LoadDivGraph("media\\clash01.bmp", 3,3,1,64,106,g_clashimage) == -1) return -1; g_hitimage = LoadGraph("media\\hit01.bmp", 0); if (g_hitimage == -1) return -1; //音楽ファイルの読み込み g_backmusic = LoadSoundMem("media\\music02.wav"); if (g_backmusic == -1) return -1; g_clip1 = LoadSoundMem("media\\power33.wav"); if (g_clip1 == -1) return -1; g_clip2 = LoadSoundMem("media\\swing25_c.wav"); if (g_clip2 == -1) return -1; g_clip3 = LoadSoundMem("media\\hit_p07.wav"); if (g_clip3 == -1) return -1; g_clip4 = LoadSoundMem("media\\crash18_d.wav"); if (g_clip4 == -1) return -1; g_clip5 = LoadSoundMem("media\\fall05.mp3"); if (g_clip5 == -1) return -1; //ゲームの初期化 pf_initgamefunc(); return 1; }

Page: 1 |

Re: 壁と床の当たり判定 ( No.1 )
名前:いっち 日時:2011/11/28 19:39

ソースをコンパイルできる形でご提供下さい。 プロジェクトを丸ごと圧縮してアップロードしていただけると助かります。
Re: 壁と床の当たり判定 ( No.2 )
名前:もあい 日時:2011/11/28 22:10

ご返信ありがとうございます。 ファイルをアップロードしました。 >:ttp://w w w1.axfc.net/uploader/Li/so/115229&key=mai パスワードはmaiです。
Re: 壁と床の当たり判定 ( No.3 )
名前:もあい 日時:2011/11/28 22:24

すみません。最初の凸部分、何度やってもすりぬけるのと段差が高すぎて登れなかったので少し低くしてしまいました。おそらく今のままの段差だとうまくすり抜けないと思います。 なので、新しくアップロードし直しました。 申し訳ございませんでした。 ttp://w w  w 1.axfc.net/uploader/Li/so/115234.zip パスワードはmaiです。
Re: 壁と床の当たり判定 ( No.4 )
名前:いっち 日時:2011/11/29 00:54

ソース拝見しました。 一度目の当たり判定のチェックは以下のように「g_jikipos.y + 55」となっており、 キャラクタのほぼ足元で判定を行っています。 -- mymain.cpp Line:368 -- //地面との当たり判定 BOOL ontheground = FALSE; //着地フラグ Ball2D jikifeet[2] = { { {g_jikipos.x - 8, g_jikipos.y + 55} , 8}, { {g_jikipos.x + 8, g_jikipos.y + 55} , 8} }; Line2D *phitland= 0, *phitwall = 0; bool hit = CollisionLandLines( jikifeet, 2, &phitland, &phitwall ); ---- その判定結果を元に壁と移動方向のあたり判定を行う部分は以下のように「g_jikipos.y+26」となっており、 おそらくキャラクタの腰辺りを原点にチェックしており、足元部分のみが壁に当たってもすり抜けていました。 -- mymain.cpp Line:398 -- //もし移動方向に壁があるなら、その方向に移動できないようにする Point2D curpos = { g_jikipos.x, g_jikipos.y+26 }; ---- とりあえず対策としては、mymain.cpp Line:398 辺りを以下のように変更すると意図通りになるのではないでしょうか? > Point2D curpos = { g_jikipos.x, g_jikipos.y+26 }; ↓↓↓↓ 変更 ↓↓↓↓ > Point2D curpos = { g_jikipos.x, g_jikipos.y+55 };
Re: 壁と床の当たり判定 ( No.5 )
名前:もあい(解決) 日時:2011/11/29 09:27

なんとかなりました。 こんな初歩的なミスで手間をかけさせてしまって申し訳ありませんでした。 ありがとうございました。

Page: 1 |