| 構造体型を使って,ゲームに登場する自機と敵機を表現してみよう! |
●元のソースコード (こちら)

■このように,構造体を使うことによってプログラム上で事物を表現することが容易になり,プログラムも見やすくまた書きやすくなる。●できあがり!(こちら)
#include "DxLib.h" /* DXライブラリという便利な機能を使うため */ #include <math.h> /* C言語に用意されているsin()とcos()を使うため */ int main() { const int windowWidth = 640; /* 画面の横幅 */ const int windowHeight = 480; /* 画面の高さ */ const int numOfMyShot = 10; /* 自機の弾数 */ const int numOfEnemyShot = 8; /* 敵機の弾数 */ enum shotStatus { /* 弾の状態を表す列挙型定数の定義 */ kUnShot, /* 0 : 未発射状態(弾は画面内に無い) */ kShot /* 1 : 既発射状態(弾は画面内に有る) */ }; struct Ship { /* 自機や敵機など「機体」を表す構造体 */ int x, y; /* 座標を表すフィールド x, y */ int life; /* ライフを表すint型のフィールド life */ int graphic; /* グラフィック番号を表すint型のフィールド graphic */ }; struct Ship myShip, eShip; /* 最初の描画位置を画面の真ん中にセット(画面サイズはwindowHeight x windowWidth) */ myShip.x = windowWidth/2 - 32/2; myShip.y = windowHeight/2 - 32/2; myShip.life = 5; myShip.graphic = 0; int keyInput; /* キー入力の結果を保存しておくための変数 */ int bg = 0; /* 背景グラフィックのグラフィック番号を記憶しておく変数 */ int i; /* カウントダウンを表示するfor文用の変数 */ int shots_Life[numOfMyShot]; /* 自機の弾が未発射(kUnShot)か発射済み(kShot)かを表す変数numOfMyShot個分の配列 */ int shots_x[numOfMyShot]; /* 自機の弾のx座標を表す変数numOfMyShot個分の配列 */ int shots_y[numOfMyShot]; /* 自機の弾のy座標を表す変数numOfMyShot個分の配列 */ int j; /* 自機の弾を表している配列をfor文で処理するためのカウンタ変数 */ int myShot = 0; /* 自機弾グラフィックのグラフィック番号を記憶しておく変数 */ /* 敵機の最初の描画位置を画面の真ん中上部にセット */ eShip.x = windowWidth/2 - 32/2; eShip.y = 0; eShip.life = 30; eShip.graphic = 0; int e_shots_Life[numOfEnemyShot]; /* 敵機の弾が未発射(kUnShot)か発射済み(kShot)かを表す変数numOfEnemyShot個分の配列 */ int e_shots_x[numOfEnemyShot]; /* 敵機の弾のx座標を表す変数numOfEnemyShot個分の配列 */ int e_shots_y[numOfEnemyShot]; /* 敵機の弾のy座標を表す変数numOfEnemyShot個分の配列 */ int eShot = 0; /* 敵機弾グラフィックのグラフィック番号を記憶しておく変数 */ int eShot_Go = 0; /* 敵機が弾を発射できるかどうか(0のとき発射不可能、1の時発射可能) */ int bg_scroll_y = 0; /* 背景グラフィックのスクロール位置 */ int bgm_sound = 0; /* BGMの音声データ番号を記憶しておく変数 */ int shot_sound = 0; /* 弾の発射音声データ番号を記憶しておく変数 */ int eshot_sound = 0; /* 敵の弾の発射音性データ番号を記憶しておく変数 */ int crash_sound = 0; /* 爆発の音声データ番号を記憶しておく変数 */ int hit_sound = 0; /* 弾のヒット音声データ番号を記憶しておく変数 */ int win_sound = 0; /* 勝利BGMの音声データ番号を記憶しておく変数 */ int time = GetNowCount(); /* Windowsが起動してからの時間をミリ秒の単位で得る */ for( i = 0; i < numOfMyShot; i++ ) { shots_Life[ i ] = kUnShot; shots_x[ i ] = 0; shots_y[ i ] = 0; } for( i = 0; i < numOfEnemyShot; i++ ) { e_shots_Life[ i ] = kUnShot; e_shots_x[ i ] = 0; e_shots_y[ i ] = 0; } SetWindowSizeChangeEnableFlag( TRUE ); SetGraphMode( windowWidth, windowHeight, 32 ); eShip.graphic = LoadGraph( "EnemyShip32.bmp" ); /* 敵機グラフィック(32pixels × 32pixels)の読み込み */ eShot = LoadGraph( "Chars16_blue.bmp" ); /* 敵機弾グラフィック(16pixels × 16pixels)の読み込み */ myShot = LoadGraph( "Chars16_orange.bmp" ); /* 自機弾グラフィック(16pixels×16pixels)の読み込み */ bgm_sound = LoadSoundMem( "ForGame008.mp3" ); /* BGM音声データの読み込み */ shot_sound = LoadSoundMem( "shot.mp3" ); /* 弾の発射音声データの読み込み */ eshot_sound = LoadSoundMem( "eshot.mp3" ); /* 敵の弾の発射音声データの読み込み */ crash_sound = LoadSoundMem( "crash.mp3" ); /* 爆発の音声データの読み込み */ hit_sound = LoadSoundMem( "hit.mp3" ); /* 弾のヒット音声データの読み込み */ win_sound = LoadSoundMem( "win.mp3" ); /* 勝利BGMの音音声データの読み込み */ SetFontSize( 30 ); /* 字体(フォント)のサイズを30に設定 */ for( i = 3; i >= 0; i-- ) { ClsDrawScreen(); /* 描画先画面をきれいに消去する */ /* 数値iを画面のだいたい中央あたり(windowWidth/2, windowHeight/2)に青色で表示 */ DrawFormatString( windowWidth/2, windowHeight/2, GetColor( 50, 50, 255 ), "%d", i ); WaitTimer( 700 ); /* 700ミリ秒動作をとめる */ } PlaySoundMem( bgm_sound, DX_PLAYTYPE_LOOP, TRUE ); /* BGMを繰り返し再生開始 */ bg = LoadGraph( "bg.jpg" ); /* 背景グラフィック(windowWidthpixels × 600pixels)の読み込み */ myShip.graphic = LoadGraph( "MyShip32.bmp" ); /* 自機グラフィック(32pixels × 32pixels)の読み込み */ SetDrawScreen( DX_SCREEN_BACK ); /* 描画先を裏画面に設定する */ while( 1 ){ /* 無限に繰り返す(無限ループ) */ ClsDrawScreen(); /* 描画先画面をきれいに消去する */ /* 背景描写( スクロール対応版 ) */ if( bg_scroll_y < 0 ) bg_scroll_y = bg_scroll_y + 600; if( (bg_scroll_y + windowHeight) > 600 ) DrawRectGraph( 0, 600 - bg_scroll_y, 0, 0, windowWidth, bg_scroll_y + windowHeight - 600, bg, TRUE, FALSE ); DrawRectGraph( 0, 0, 0, bg_scroll_y, windowWidth, 600 - bg_scroll_y, bg, TRUE, FALSE ); bg_scroll_y = bg_scroll_y - 2; keyInput = GetJoypadInputState( DX_INPUT_KEY_PAD1 ); /* キーボードからの入力を変数keyInputへ保存 */ if( keyInput == PAD_INPUT_RIGHT ) { /* 右矢印キーを押し続けても画面の右側に自機が出ないようにする */ if( (myShip.x + 5) >= ((windowWidth - 1)-31) ) { myShip.x = ((windowWidth - 1)-31); } else { myShip.x = myShip.x + 5; } } if( keyInput == PAD_INPUT_LEFT ) { /* 左矢印キーを押し続けても画面の左側に自機が出ないようにする */ if( (myShip.x - 5) <= 0 ) { myShip.x = 0; } else { myShip.x = myShip.x - 5; } } if( keyInput == PAD_INPUT_DOWN ) { if( (myShip.y + 5) >= ((windowHeight - 1) - 31) ) { myShip.y = ((windowHeight - 1) - 31); } else { myShip.y = myShip.y + 5; } } if( keyInput == PAD_INPUT_UP ) { if( (myShip.y + 5) <= 0 ) { myShip.y = 0; } else { myShip.y = myShip.y - 5; } } /* キーボードから入力された文字がスペースなら自機が弾を撃つ */ if( GetInputChar( TRUE ) == ' ' ) { /* GetInputChar(TRUE)はDXライブラリの機能でキーボードで押した文字を入力する */ for( j = 0; j < numOfMyShot; j++ ) { if( shots_Life[j] == kUnShot ) { /* 未発射の弾を見つけたら... */ shots_Life[j] = kShot; /* 発射済みにして */ shots_x[j] = myShip.x + (32/2) - (16/2); /* 弾のx座標を自機の鼻先に設定する */ shots_y[j] = myShip.y; /* 弾のy座標を自機の鼻先に設定する */ PlaySoundMem( shot_sound, DX_PLAYTYPE_BACK, TRUE ); /* 弾の発射音性を再生 */ break; /* 発射する弾は1個だけでいいので、繰り返しを強制的に終了させる */ } } } for( j = 0; j < numOfMyShot; j++ ) { /* 自機の弾numOfMyShot個に対して次の処理を行う */ if( shots_y[j]+16 < 0 ) shots_Life[j] = kUnShot; /* 画面から飛び出した弾は未発射状態にする */ if( shots_Life[j] != kUnShot ) { /* もし自機のj番目の弾が発射済みなら... */ DrawGraph( shots_x[j], shots_y[j], myShot, TRUE ); /* 自機のj番目の弾を画面に描画する */ shots_y[j] = shots_y[j] - 8; /* 次の描画時のためにj番目の弾の位置を前に進める */ } } eShot_Go = 1; /* とりあえず発射可能状態にしておく */ for( j = 0; j < numOfEnemyShot; j++ ) { if( e_shots_x[j] < 0 ) e_shots_Life[j] = kUnShot; /* 画面から飛び出した弾は未発射状態にする */ if( e_shots_x[j]+16 > (windowWidth - 1) ) e_shots_Life[j] = kUnShot; /* 画面から飛び出した弾は未発射状態にする */ if( e_shots_y[j]+16 < 0 ) e_shots_Life[j] = kUnShot; /* 画面から飛び出した弾は未発射状態にする */ if( e_shots_y[j] > (windowHeight - 1) ) e_shots_Life[j] = kUnShot; /* 画面から飛び出した弾は未発射状態にする */ if( e_shots_Life[j] != kUnShot ) eShot_Go = 0; /* numOfEnemyShot発の弾のうち1個でも発射済なら発射不可能とする */ } if( eShot_Go == 1 ) { /* 敵機の弾が発射可能なら全ての弾を発射済みにして位置をセット */ for( j = 0; j < numOfEnemyShot; j++ ) { e_shots_Life[j] = kShot; e_shots_x[j] = (eShip.x + 16 - 8); e_shots_y[j] = (eShip.y + 16 - 8); } PlaySoundMem( eshot_sound, DX_PLAYTYPE_BACK, TRUE ); /* 敵の弾の発射音性を再生 */ } for( j = 0; j < numOfEnemyShot; j++ ) { /* 敵機の弾のうち、発射済みの弾を描画する。 */ if( e_shots_Life[j] != kUnShot ) { DrawGraph( e_shots_x[j], e_shots_y[j], eShot, TRUE ); /* 敵機弾のグラフィックを描画する */ } /* 次の描画時のためにj番目の弾の位置を前に進める */ e_shots_x[j] = e_shots_x[j] + 6 * cos( j * ((2.0/numOfEnemyShot) * 3.1415926) ); e_shots_y[j] = e_shots_y[j] + 6 * sin( j * ((2.0/numOfEnemyShot) * 3.1415926) ); } DrawGraph( eShip.x, eShip.y, eShip.graphic, TRUE ); /* 座標ex,eyに敵機のグラフィックを描画する */ /* 敵機の座標をランダムに自機に近づける。なお、GetRand(n)は、0からnまでのでたらめな値を返す */ eShip.x = eShip.x + (myShip.x - eShip.x)/( GetRand(200) + 15 ); eShip.y = eShip.y + (myShip.y - eShip.y)/( GetRand(200) + 15 ); DrawGraph( myShip.x, myShip.y, myShip.graphic, TRUE ); /* 座標x,yに自機のグラフィックを描画する */ /* 衝突判定を行う */ /* 衝突判定1.「自機と敵機」 */ if( (( myShip.x <= eShip.x) && ( eShip.x <= (myShip.x + 31))) && (( myShip.y <= eShip.y) && ( eShip.y <= (myShip.y + 31))) ) { PlaySoundMem( crash_sound, DX_PLAYTYPE_BACK, TRUE ); /* 爆発音を再生 */ myShip.life = 0; /* 自機のライフを0に設定 */ } /* 衝突判定2.「自機の弾と敵機」 */ for( i = 0; i < numOfMyShot; i++ ) { if( shots_Life[i] != kUnShot ) { /* 発射状態の弾に関してだけ敵機との衝突判定をする */ if( ( (eShip.x <= shots_x[i]) && (shots_x[i] <= (eShip.x + 31) ) && ( (eShip.y <= shots_y[i]) && (shots_y[i] <= (eShip.y + 31) ) )) ) { shots_Life[i] = kUnShot; /* 当たった弾を未発射状態にする */ PlaySoundMem( hit_sound, DX_PLAYTYPE_BACK, TRUE ); /* 弾のヒット音を再生 */ eShip.life = eShip.life - 1; /* 敵機のライフを1減らす */ } } } /* 衝突判定3.「敵機の弾と自機」 */ for( i = 0; i < numOfEnemyShot; i++ ) { if( e_shots_Life[i] != kUnShot ) { /* 発射状態の弾に関してだけ自機との衝突判定をする */ if( ( (myShip.x <= e_shots_x[i]) && (e_shots_x[i] <= (myShip.x + 31) ) && ( (myShip.y <= e_shots_y[i]) && (e_shots_y[i] <= (myShip.y + 31) ) )) ) { e_shots_Life[i] = kUnShot; /* 当たった弾を未発射状態にする */ PlaySoundMem( hit_sound, DX_PLAYTYPE_BACK, TRUE ); /* 弾のヒット音を再生 */ myShip.life = myShip.life - 1; /* 自機のライフを1減らす */ } } } /* ライフと経過時間の表示 */ DrawFormatString( 0, 0, GetColor( 20, 20, 255 ), "%02d", myShip.life ); DrawFormatString( windowWidth - GetDrawFormatStringWidth( "%02d", eShip.life ), 0, GetColor( 255, 0, 0 ), "%02d", eShip.life ); DrawFormatString( windowWidth/2 - ( GetDrawFormatStringWidth( "%03.2f", (GetNowCount() - time) / 1000.0 ) ) / 2, 2 , GetColor( 255, 255, 0 ), "%03.2f", (GetNowCount() - time) / 1000.0 ); /* 勝負が決まったら無限ループから抜け出す */ if( eShip.life <= 0 || myShip.life <= 0 ) break; ScreenFlip(); /* 裏画面に描画したものを表画面に転写する */ if( CheckHitKey(KEY_INPUT_ESCAPE) ) break; /* エスケープキーが押されたら繰り返し処理から出る */ } /* while()の閉じ中括弧 */ SetDrawScreen( DX_SCREEN_FRONT ); /* 描画先を表画面にする */ /* プレイヤーが勝った場合 */ if( eShip.life <= 0 ) { PlaySoundMem( crash_sound, DX_PLAYTYPE_BACK, TRUE ); /* 爆発音を再生 */ StopSoundMem( bgm_sound ); /* BGM再生を停止 */ PlaySoundMem( win_sound, DX_PLAYTYPE_LOOP, TRUE ); /* 勝利BGMをループ再生 */ SetFontSize( 60 ); /* フォントサイズを60に設定 */ DrawFormatString( windowWidth/2 - (GetDrawFormatStringWidth( "You Win!!" ) / 2 ), windowHeight/2 - 20, GetColor( 20, 20, 255 ), "You Win!!" ); } /* プレイヤーが負けた場合 */ if( myShip.life <= 0 ) { PlaySoundMem( crash_sound, DX_PLAYTYPE_BACK, TRUE ); /* 爆発音を再生 */ SetFontSize( 60 ); /* フォントサイズを60に設定 */ DrawFormatString( windowWidth/2 - (GetDrawFormatStringWidth( "You Lose!!" ) / 2 ), windowHeight/2 - 20, GetColor( 255, 100, 100 ), "You Lose!!" ); } while( CheckHitKey( KEY_INPUT_ESCAPE ) == 0 ) {} /* エスケープキーが押されるのを待つ */ return 0; }