ゲームプログラミング講座 講義資料

2010
ゲームプログラミング講座
講義資料
内容
資料 1.
環境設定 ..................................................................................................................................... 3
1.
Visual C++ 2010 Express のダウンロード ...................................................................................... 3
2.
Visual C++ 2010 Express のインストール ..................................................................................... 3
3.
DX ライブラリのダウンロード ......................................................................................................... 4
4.
DX ライブラリのインストール ......................................................................................................... 4
5.
プロジェクトの作製 .......................................................................................................................... 4
6.
プロジェクトの設定 .......................................................................................................................... 6
7.
テストウィンドウの表示 ................................................................................................................... 8
8.
画面に画像を描画する ...................................................................................................................... 9
資料 2.
9.
画像の動作とキー入力 ............................................................................................................. 11
画像を動かす ................................................................................................................................... 11
10.
資料 3.
キー入力による操作 .................................................................................................................... 13
弾と敵の出現............................................................................................................................ 16
11.
弾の発射....................................................................................................................................... 16
12.
弾の連射....................................................................................................................................... 21
13.
敵の出現....................................................................................................................................... 26
資料 4.
当たり判定とタイトル画面 ...................................................................................................... 30
14.
当たり判定 ................................................................................................................................... 30
15.
タイトル画面とゲームオーバー画面 ........................................................................................... 34
16.
完成したゲームの「exe ファイル化」 ........................................................................................ 39
資料 5.
音声ファイルの再生、背景 ...................................................................................................... 41
17.
BGM の演奏 ................................................................................................................................ 41
18.
背景の作成 ................................................................................................................................... 44
資料 6.
敵の弾発射、レーザー、スコア .............................................................................................. 47
19.
敵の弾発射 ................................................................................................................................... 47
20.
レーザー....................................................................................................................................... 52
21.
スコア .......................................................................................................................................... 52
参考文献 ..................................................................................................................................................... 53
資料1.
環境設定
C 言語プログラミングの開発環境「Visual C++ 2010 ExpressEdition」の設定とゲーム作成用ライブ
ラリ「DX ライブラリ」のインストールを行う。
「Visual C++ 2010 ExpressEdition」は授業でもインス
トールするため、既にインストールしてある者は「DX ライブラリ」のインストールだけ行うこと。
1. Visual C++ 2010 Express のダウンロード
1.
Microsoft VisualStudio ホームページへ行く
google で(VisualC++2010)のキーワードで検索する、もしくは
このアドレス(http://www.microsoft.com/japan/msdn/vstudio/express/)を入力する
2.
Visual C++ 2010 Express Web インストール(ダウンロード)をクリック
3.
保存先をデスクトップに指定してダウンロード
2. Visual C++ 2010 Express のインストール
1.
DL した「VC_web.exe」を実行
2.
セットアップへようこそ
「次へ」をクリック
マイクロソフトにフィードバックを送信するかどうかは任意でチェックを入れる
3.
ライセンス条項 「同意する」にチェックを入れ
4.
コピー先フォルダー 「インストール」をクリック
「次へ」をクリック
インストールするフォルダはデフォルトのままで OK
5.
ダウンロードとインストールの進行状況 時間が経過すると自動で次の画面へ移動
インストールには時間がかかるため、その間に DX ライブラリのインストールを行う
6.
セットアップの完了 「終了」をクリック
注意:VC++は製品登録(無料)をしないと 30 日で使用できなくなる。製品登録は VC++のヘル
プから行えるので後で製品登録を行うこと。
3. DX ライブラリのダウンロード
1.
DX ライブラリのホームページへ行く
google で(DX ライブラリ)のキーワードで検索する、もしくは
このアドレス(http://homepage2.nifty.com/natupaji/DxLib/index.html)を入力する
2.
DX ライブラリのダウンロードページへ行く
3.
DXライブラリ VisualC++用(Ver3.03)をダウンロードする
4.
保存先をデスクトップに指定してダウンロード
をクリック
4. DX ライブラリのインストール
1.
DL した「DxLib_VC3_03.exe」を実行
2.
ファイルを展開します 次へをクリック
3.
展開先のフォルダを指定してください
4.
展開を開始します 次へをクリック
5.
展開中…
6.
展開が完了しました 完了をクリック
7.
デスクトップに出来上がった「DxLib_VC」フォルダを C ドライブ(C:¥)に移す
展開先をデスクトップに指定し次へをクリック
展開が完了すると自動で次へ進む
5. プロジェクトの作製
1.
VC++を起動する
デフォルト設定の場合、スタートメニューすべてのプログラム項目の中に
「Microsoft Visual C++ 2010 Express」の項目があるのでそこから起動する
「ファイル」 → 「新規作成」 → 「プロジェクト」
と選択する
新規プロジェクトの開き方
2.
「Win32 プロジェクト」を選択し、「プロジェクト名」を付け、
「ソリューションのディレクトリを作製」チェックをはずして OK をクリックする
Win32 プロジェクトが見つからない場合は左のインストールされたテンプレート欄で
Win32 を選択する。プロジェクトの場所はデフォルト設定の場合「c:¥Users¥ユーザー名
¥documents¥visual studio 2010¥Projects」となっている。
注意:プロジェクト名は半角英数字で付けること!
プロジェクト設定1
3.
Win32 アプリケーションウィザードへようこそ 「次へ」
4.
アプリケーションの設定 「Windows アプリケーション」
「空のプロジェクト」
をクリック
に
チェックを入れて完了をクリック
プロジェクトが完成したので、次は C++のソースコードを作製する。
「ソリューションエクスプローラー」→「ソースコードを右クリック」→「追加→新しい項目」
ソリューションエクスプローラーが表示されない場合は「表示」→「その他のウィンドウ」→
「ソリューションエクスプローラー」で表示することができる。
ソースコードの作製
5.
新しい項目の追加 「C++ファイル」を選択し、
「名前を付けて」
「追加」をクリック
注意:ソースコード名は半角英数字でつけること!
6.
ソースコードの完成
6. プロジェクトの設定
7.
「プロジェクト(P)」→「(プロジェクト名)のプロパティ(P)」を開く
プロパティの開き方
8.
左側のリストから「構成プロパティ」を選択し、構成(C):を「アクティブ(Debug)」から
「すべての構成」にする
構成の変更
9.
「構成プロパティ」→「全般」を選択し、
文字セットを「マルチバイト文字セットを使用する」に変更する
文字セットの変更
注意:画像では「Unicode 文字セットを使用する」となっているが、設定の際は「マルチバイ
ト文字セットを使用する」を選択する
10.
「構成プロパティ」→「C/C++」→「全般」を選択
11.
「追加のインクルードディレクトリ」に DX ライブラリの「プロジェクトに追加すべきフ
ァイル_VC 用」フォルダのアドレスを追加
追加のインクルードディレクトリ
「追加のインクルードディレクトリ」のウィンドウをこのようにして OK をクリックする
12.
「構成プロパティ」→「リンカー」→「全般」を選択
13.
「追加のライブラリディレクトリ」に上と同じように DX ライブラリの
「プロジェクトに追加すべきファイル_VC 用」フォルダのアドレスを追加
14.
構成(C):を「すべての構成」から「Release」にする
15.
「構成プロパティ」→「C/C++」→「コード生成」を選択
16.
「ランタイムライブラリ」の項目を「マルチスレッド(/MT)」に変更する
17.
構成(C):を「Release」から「Debug」にする
18.
「構成プロパティ」→「C/C++」→「コード生成」を選択
19. 「ランタイムライブラリ」の項目を「マルチスレッドデバッグ(/MTd)」に変更する
20. 設定が完了したので 「OK」をクリック
7. テストウィンドウの表示
21.
以下のソースコードを作製した cpp ファイルに書き込む
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine,
int nCmdShow )
{
ChangeWindowMode( TRUE ) ;
if( DxLib_Init() == -1 )
// ウインドウモードに変更
// DXライブラリ初期化処理
{
return -1 ;
}
WaitKey() ;
DxLib_End() ;
return 0 ;
// エラーが起きたら直ちに終了
// キー入力がされるまで待つ
// DXライブラリ使用の終了処理
// ソフトの終了
}
22.
「デバッグ」→「デバッグ開始(S)」を選択、もしくは「F5」を押す
23.
図のウィンドウが表示されれば設定が成功している
サンプルウィンドウ
8. 画面に画像を描画する
//画像表示ソースコードサンプル
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
//初期化処理-------------------------------------------------------------------------------------------ChangeWindowMode( TRUE ) ;
//ウィンドウモードに変更
if( DxLib_Init() == -1 )
// DXライブラリ初期化処理
return -1;
// エラーが起きたら直ちに終了
//本文----------------------------------------------------------------------------------------------------int gh ;
// グラフィックハンドルを入れる変数を宣言
int x, y;
//画像表示座標を宣言
x = 200;
y = 150;
//画像の表示位置を変数に代入
gh = LoadGraph( "test.bmp" ) ; // test.bmp 画像のメモリへの読みこみ
DrawGraph( x, y , gh , TRUE) ;
// (x,y)座標に画像を表示
//終了処理---------------------------------------------------------------------------------------------WaitKey() ;
// キーの入力待ち
DxLib_End() ;
// DXライブラリ使用の終了処理
return 0 ;
// ソフトの終了
}
test.bmp
test.bmp を VC++のプロジェクトファイルと同じディレクトリに移し、実行する。
(デフォルト設定の場合、画像を入れるディレクトリは
C:¥Users¥(ユーザー名)¥Documents¥Visual Studio 2010¥Projects¥(プロジェクト名) となる)
実行すると図3のようなウィンドウが表示され、
キー入力もしくはクリックをすると閉じる。
画面に画像が表示されない場合は test.bmp を保
存する場所を間違えている可能性がある。
新しい命令の説明
gh =LoadGraph( "test.bmp" );
この文は「test.bmp」画像を読み込み、
「gh」に識
別番号を入れる命令である。今後、gh 変数を使う
ことで画像を表示することができるようになる。
画像表示サンプル実行画面
DrawGraph( x, y , gh , TRUE) ;
この文は読み込んだ画像を表示する命令である。
x
横方向の表示位置
y
縦方向の表示位置
gh
表示する画像の番号
TRUE
画像の黒い部分を透明にするかどうか
(TRUE で透明にする、FALSE で透明にしない)
練習1:
図 3 のように画像を2つ読み込み、2つの画像を2つずつ表示せよ
条件:使用する変数は以下のものだけとする
int x1, y1;
//表示位置1
int x2, y2;
//表示位置2
int x3, y3;
//表示位置3
int x4, y4;
//表示位置4
int gh1;
//画像1
int gh2;
//画像2
図 3
練習サンプル画面
資料2.
画像の動作とキー入力
9. 画像を動かす
//画像を動かす サンプルソースコード
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
//初期化処理-------------------------------------------------------------------------------------ChangeWindowMode( TRUE ) ;
//ウィンドウモードに変更
if( DxLib_Init() == -1 )
// DXライブラリ初期化処理
return -1;
// エラーが起きたら直ちに終了
//本文----------------------------------------------------------------------------------------------------int gh ;
// グラフィックハンドルを入れる変数を宣言
int x, y;
//画像表示座標を宣言
x = 200;
y = 150;
//画像の表示位置を変数に代入
int i;
//for 文ループ用変数を宣言
gh = LoadGraph( "test.bmp" ) ; // test.bmp 画像のメモリへの読みこみ
DrawGraph( x, y , gh , TRUE) ;
// (x,y)座標に画像を表示
//メインループ---------------------------------------------------------------------------------for(i = 0 ; i< 200; i += 1)
{
x += 2;
//画像の表示位置を右方向に2ずらす
y += 1;
//画像の表示位置を下方向に1ずらす
DrawGraph(x, y, gh, TRUE);
//画像を(x,y)座標に表示する
if(ProcessMessage() == -1)
//ウィンドウのメッセージ処理
{
break;
//ウィンドウのメッセージ処理にエラーが発生した場合ループを抜
ける
}
}
//終了処理----------------------------------------------------------------------------------------------
}
DxLib_End() ;
// DXライブラリ使用の終了処理
return 0 ;
// ソフトの終了
実行すると図のようなウィンドウが表示
され、画像が右下方向へ移動する。
その後一定時間が経過するとウィンドウ
が閉じて終了する。
画像を動かす
実行画面サンプル
新しい命令の説明
for(i=0; i<200; i+=1)
{
//省略
}
真ん中の条件(i<200)が正しい場合、{ }内の処理を繰り返す命令文。つまり{ }内の処理が200回繰
り返し実行される。
for(① ; ② ; ③)
{ ④ }
①:最初に行われる処理
②:条件文
③:{ ④ }の処理が終わったあとに行われる処理
④:繰り返し実行される処理
①
→ [② → ④ → ③] →
[② → ④ → ③] →・・・
の順番に処理が行われ、②の条件が正しい間④の処理が実行される。
if(ProcessMessage() == -1)
{
//省略
}
DX ライブラリの特殊な命令で、この文を書かないと正常に動作しない場合がある。どのような処理が行
われているかを理解する必要はないが、知りたい場合は X ライブラリのリファレンスを参照すること。
練習2
サンプルソースコードを改造し、画面端まで移動
した画像が逆側の端に移動し、ループするようにせ
よ
ヒント:if()を使用し、x、yの値が画面の幅より大
きくなった場合に0を代入することでループするよ
うにみせることができる。
画面横幅:640 画面縦幅:480
練習2実行サンプル
10. キー入力による操作
//画像を動かすサンプルソースコード
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
//初期化処理-------------------------------------------------------------------------------------ChangeWindowMode( TRUE ) ;
//ウィンドウモードに変更
if( DxLib_Init() == -1 )
// DXライブラリ初期化処理
return -1;
// エラーが起きたら直ちに終了
SetDrawScreen( DX_SCREEN_BACK );
//画面を裏画面にセット
//本文----------------------------------------------------------------------------------------------------int gh ;
// グラフィックハンドルを入れる変数を宣言
int x, y;
//画像表示座標を宣言
x = 200;
y = 150;
//画像の表示位置を変数に代入
int i;
//for 文ループ用変数を宣言
gh = LoadGraph( "test.bmp" ) ; // test.bmp 画像のメモリへの読みこみ
char Key[256];
//キーの入力状況を記録する配列を宣言
//メインループ---------------------------------------------------------------------------------for(i = 0 ; i< 200; i += 1)
{
GetHitKeyStateAll(Key); //キーボードのすべてのキーの押下状態を Key に記録する
x += 2;
//画像の表示位置を右方向に2ずらす
y += 1;
//画像の表示位置を下方向に1ずらす
if(Key[KEY_INPUT_LEFT] == 1) //左が押されている場合
{
x -= 1;
//画像を左方向へずらす
}
if(Key[KEY_INPUT_RIGHT] == 1)
//右が押されている場合
{
x += 1; //画像を右方向へずらす
}
DrawGraph(x, y, gh, TRUE);
//画像を(x,y)座標に表示する
ScreenFlip();
//裏画面を表画面に反映する
ClearDrawScreen();
//裏画面に描かれたものを全て消去
if(ProcessMessage() == -1)
//ウィンドウのメッセージ処理
{
break;
//ウィンドウのメッセージ処理にエラーが発生した場合ループを抜
ける
}
}
//終了処理---------------------------------------------------------------------------------------------DxLib_End() ;
// DXライブラリ使用の終了処理
return 0 ;
// ソフトの終了
}
ボタンを押した方向へ画像が移動する。
キー入力による操作実行画面
新しい命令の説明
SetDrawScreen( DX_SCREEN_BACK );
//画面を裏画面にセット
DXライブラリがダブルバッファリングを行うように設定する。この処理を記述していないと
ダブルバッファリングが行われず画面がちらついてしまう。ダブルバッファリングについての説
明は「2.7 画面の更新」に記載してある。
ScreenFlip();
//裏画面を表画面に反映する
この関数が実行されたときにダブルバッファリングのために裏画面(ディスプレイに表示されな
い部分)で描画されていた内容が表画面(ディスプレイ)に表示される。
ClearDrawScreen();
//裏画面に描かれたものを全て消去
この関数を実行しないと前のフレームの画像が残ったまま次のフレームで描画が開始されてし
まう。
char Key[256];
//キーの入力状況を記録する配列を宣言
キーボードの入力状況を記録しておくための変数配列を宣言する。配列とは連続している変数
のことで、ここでは char 型の変数が 256 個並んでいる配列を宣言している。
GetHitKeyStateAll(Key);
//キーボードのすべてのキーの押下状態を Key に記録する
この関数は実行された時のキーボードの押下状態を配列 Key に保存するものである。この関数
が実行される前は配列 Key にはでたらめな数値が入っている。そのため、この関数が実行される
前に配列 Key の値を参照しようとするとエラーが発生して処理が止まることがあるので注意する
こと。
if(Key[KEY_INPUT_LEFT] == 1)
//左が押されている場合
{ x -= 1; }
//画像を左方向へずらす
if(Key[KEY_INPUT_RIGHT] == 1)
//右が押されている場合
{ x += 1; }
//画像を右方向へずらす
左キーが押されている場合は画像を左へ1ずらし、右キーが押されている場合は右へ1ずらす。
キーの押下状態は配列 Key[キーコード番号]の値が1になっているかを調べることで知ることが
できる。値が1の場合はキーが押されており、0の場合は押されていない。キーコードはDXラ
イブラリリファレンスページの CheckHitKey の項目に記載されている。また、主要なキーコード
は下記に記す。
主要なキーコード
KEY_INPUT_LEFT
// 左キー
KEY_INPUT_UP
// 上キー
KEY_INPUT_RIGHT
// 右キー
KEY_INPUT_DOWN
// 下キー
KEY_INPUT_RETURN
// エンターキー KEY_INPUT_ESCAPE
// エスケープキー
KEY_INPUT_SPACE
// スペースキー
KEY_INPUT_LSHIFT
// 左シフトキー KEY_INPUT_LSHIFT
KEY_INPUT_F1
~
KEY_INPUT_F12
// F1~F12 キー
KEY_INPUT_A
~
KEY_INPUT_Z
// A~Z キー
KEY_INPUT_0
~
KEY_INPUT_9
// 0~9 キー
// 右シフトキー
練習3
サンプルソースコードを改造し、スペースキーが押されている場合は2倍の速さで移動できるよう
にせよ。
資料3.
弾と敵の出現
この項目から先は簡単なシューティングゲームを作成していく。
新しくプロジェクトを作成し、もう一度プロジェクトの設定を行った後に以下のソースコードを入力せ
よ。
なお、各項目で重要と思われる部分は網掛けで表示し、説明を行う。
11. 弾の発射
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
//初期化処理-------------------------------------------------------------------------------------ChangeWindowMode( TRUE ) ;
//ウィンドウモードに変更
if( DxLib_Init() == -1 )
// DXライブラリ初期化処理
return -1;
// エラーが起きたら直ちに終了
SetDrawScreen( DX_SCREEN_BACK );//画面を裏画面にセット
//変数の宣言----------------------------------------------------------------------------------char Key[256];
//キーの入力状況を記録する配列を宣言
int pgh;
//プレイヤー画像ハンドル
int px, py;
//プレイヤーの描画座標
int bgh;
//弾の画像ハンドル
int bx, by;
//弾の描画座標
int bflag;
//弾の存在フラグ 存在する1 存在しない0
//初期値の設定----------------------------------------------------------------------------px = 300;
//プレイヤー初期座標入力
py = 400;
pgh = LoadGraph("player.bmp"); //プレイヤー画像の読み込み
bx = 0;
//弾の初期座標を入力
by = 0;
bflag = 0;
//弾は最初存在しないため、存在しないようにする
bgh = LoadGraph("bullet.bmp");
//弾の画像を読み込む
//メインループ---------------------------------------------------------------------------------for(;;)
//無限ループ
{
//キー入力状況取得
GetHitKeyStateAll(Key); //キーボードのすべてのキーの押下状態を Key に記録する
//プレイヤーの移動------------------------------------------------------------------------if(Key[KEY_INPUT_LEFT] == 1) //左が押されている場合
{
px -= 1; //画像を左方向へずらす
}
if(Key[KEY_INPUT_RIGHT] == 1)
//右が押されている場合
{
px += 1; //画像を右方向へずらす
}
//弾の発射--------------------------------------------------------------------------------if(Key[KEY_INPUT_SPACE])
//スペースキーが押されている場合
{
if(bflag == 0)
//弾が消滅状態の場合
{
bflag = 1;
//弾を存在させる
bx = px;
//弾の座標をプレイヤーの座標にする
by = py;
}
}
//弾の移動--------------------------------------------------------------------------------if(bflag == 1)
//弾が存在している時のみ処理を行う
{
by -= 5; //弾の座標を上方向へ移動させる
if(by < 0)
//弾が画面外へ移動した場合
{
bflag = 0;
//弾を消滅状態にする
}
}
//画像の描画------------------------------------------------------------------------------DrawGraph(px, py, pgh, TRUE);
if(bflag == 1)
//プレイヤー画像を(x,y)座標に表示する
//弾が存在している場合
{
DrawGraph(bx, by, bgh, TRUE);
//弾画像を描画する
}
//ダブルバッファリング処理----------------------------------------------------------------ScreenFlip();
//裏画面を表画面に反映する
ClearDrawScreen();
//裏画面に描かれたものを全て消去
//ゲーム終了処理--------------------------------------------------------------------------if(Key[KEY_INPUT_ESCAPE] == 1)
//エスケープキーが押された場合
{
break;
//ループを抜ける
}
if(ProcessMessage() == -1)
//ウィンドウのメッセージ処理
{
break;
//ウィンドウのメッセージ処理にエラーが発生した場合ループを抜
ける
}
}
//終了処理----------------------------------------------------------------------------------------------
}
DxLib_End() ;
// DXライブラリ使用の終了処理
return 0 ;
// ソフトの終了
・このソースコードからは今までの仮画像 (test.bmp)ではなく、シューティングゲーム用の画 像
(player.png、bullet.png)を使用する。画像は各自で用意すること。
・サンプルで使用した画像
自機画像(player.bmp)
弾画像(bullet.bmp)
実行画面サンプル
・キーボードの左右のキーでキャラクターを移動させることができる。
・ESCキーを押すと終了する
・Zキーを押すと弾が自機から発射される。
ただし、弾が画面内に存在しているときは弾を発射することができない
・弾は発射されると一定速度で上へ移動し、画面枠に接触すると消滅する
弾の発射
解説
画像の表示方法と動かし方はわかった。ここからは簡単なシューティングゲームを作っていく。
まずはキー入力による操作で左右に動かしたグラフィックを自機として、自機から弾を発射できる
ようにする。
なお、サンプルのソースコードでは読み取りやすくするために処理ごとに「//-------」を記述して
いる。
int bflag ;
弾の存在フラグとして bflag という変数を用意する。この変数の値が1の時は弾が存在するもの
として、弾の移動や表示の処理がされる。また、弾が存在するときに弾が発射されるとすでに存
在する弾が途中で消えて次弾となってしまう。その問題を解決するため、弾の存在フラグが0の
時しか弾を発射することができないようにする。
弾の移動処理
まず、弾が存在する時のみこの処理は行われる。次に今回作製するシューティングゲームでは
弾は上方向にしか移動しない。そのため、弾の移動は弾のy座標を減らすことでだけである。
弾は発射されたあと、画面外へ出て行ったときに消滅する。画面外へ出たかの判断は弾の座標
が画面外かどうかで判断する。弾が画面外へ出ている時に存在フラグを0とすることで弾は消滅
したことになる。この条件を変化させることで射程の短い弾や敵にあたると消滅する弾を作るこ
とができる。
弾の発射処理
弾が発射される条件は2つある。まず、攻撃キーが押されている時であること。もうひとつは
弾が画面上に存在しないことである。弾が画面上に存在しないとは bflag が 0 である時である。2
つの条件を満たしているときに発射処理は実行される。
弾の発射をゲームとして表現するにはどのようにしたらよいか? サンプルコードでは弾を画
面に表示させ、弾の座標を自機座標と同じにすることで表現する。
練習4
弾を発射するボタンを増やし、ボタンによって違う弾が発射されるようにせよ
ヒント:違う弾とは弾速や画像の違いで表現することができる。
・この項目から先のソースコードは前項目のソースコードを改造したものである
・元のソースコードから削除された部分は 取り消し線 で表示されている。
・新しく追加された部分は 太字 で表示されている。
・重要な部分は 網掛け で表示されている。
・このソースコードでは全体の行数を短くするため、下記のように「if」や「for」の「{ }」
の頭の位置を「()」の直後にしてある。
if(i==1){
}
↑このように記述してある
12. 弾の連射
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow ){
//初期化処理-------------------------------------------------------------------------------------省略
//変数の宣言----------------------------------------------------------------------------------char Key[256];
//キーの入力状況を記録する配列を宣言
int pgh;
//プレイヤー画像ハンドル
int px, py;
//プレイヤーの描画座標
int bgh;
//弾の画像ハンドル
int bx[20], by[20];
//弾の描画座標
int bflag[20];
//弾の存在フラグ 存在する1 存在しない0
int i;
//for 文用変数
int cool_time;
//次弾発射までの時間
//初期値の設定----------------------------------------------------------------------------px = 300;
//プレイヤー初期座標入力
py = 400;
pgh = LoadGraph("player.bmp"); //プレイヤー画像の読み込み
bx = 0;
//弾の初期座標を入力
by = 0;
bflag = 0;
//弾は最初存在しないため、存在しないようにする
for(i=0; i<20; i++){
bx[i] = 0;
//弾 20 発分処理
//弾の初期座標を入力
by[i] = 0;
bflag[i] = 0;
//弾は最初存在しないため、存在しないようにする
}
bgh = LoadGraph("bullet.bmp");
cool_time = 0;
//次弾発射までの時間を初期化
//メインループ---------------------------------------------------------------------------------for(;;){
//無限ループ
//キー入力状況取得
GetHitKeyStateAll(Key); //キーボードのすべてのキーの押下状態を Key に記録する
//プレイヤーの移動------------------------------------------------------------------------省略
//弾の発射--------------------------------------------------------------------------------if(Key[KEY_INPUT_SPACE]){
//スペースキーが押されている場合
if(cool_time < 0){ //cool_time が0より小さいときのみ弾が発射可能
for(i=0; i<20; i+=1){ //20発ある弾のうち、発射可能な弾を調べる
if(bflag[i] == 0){ //弾が発射可能
break;
//ここでループを抜けると
//変数 i には発射可能な弾の番号が入っている
}
}
if(i < 20){
//全ての弾が発射状態の場合、i は20になる。
//i が20より小さい場合のみ処理を行う
bflag[i] = 1;
//弾を存在させる
bx[i] = px;
//弾の座標をプレイヤーの座標にする
by[i] = py;
cool_time = 10;
//次弾発射までの時間を設定
}
}
}
cool_time -= 1;
//次弾発射までのカウントを減らす
//弾の移動--------------------------------------------------------------------------------for(i=0; i<20; i+=1){
//弾 20 発分処理を行う
if(bflag[i] == 1){ //弾が存在している時のみ処理を行う
by[i] -= 5;
//弾の座標を上方向へ移動させる
if(by[i] < 0){
//弾が画面外へ移動した場合
bflag[i] = 0;
//弾を消滅状態にする
}
}
}
//画像の描画------------------------------------------------------------------------------DrawGraph(px, py, pgh, TRUE);
for(i=0; i<20; i+=1){
//プレイヤー画像を(x,y)座標に表示する
//弾 20 発分処理を行う
if(bflag[i] == 1){ //弾が存在している場合
DrawGraph(bx[i], by[i], bgh, TRUE);
//弾画像を描画する
}
}
//ダブルバッファリング処理----------------------------------------------------------------省略
//ゲーム終了処理--------------------------------------------------------------------------省略
}
//終了処理---------------------------------------------------------------------------------------------省略
}
・キーボードの左右のキーでキャラクターを移動させること
ができる。
・ESCキーを押すと終了する
・Zキーを押している間、弾が連射される
・弾は最大20発同時に表示され、20発以上発射しようと
しても弾が発射されない
サンプルコード実行画面
弾の連射
解説
弾を発射することができるようになったが、現段階では1発ずつしか発射することができない。
この項目では配列を使い、弾を連射することができるようにする。配列を応用することで弾だけで
なく、数多く出現する敵キャラなども作ることができるようになる。
配列を使った弾の変数
bx[20], by[20], bflag[20];
弾の座標、フラグを配列を使って20発分作製する。なお、弾の画像ハンドルはロードした弾
の画像が1つだけのため、ひとつでいい。
ループを使った配列の初期化
int i=0;
for(i=0; i<20; i+=1){
bx[i] = 0;
by[i] = 0;
bflag[i] = 0;
}
20回繰り返されるループ。{ }の中の処理を i の数値を 0~19 まで変化させながら 20 回繰り返
される。
弾が 20 発になったため、初期値を入力するためには「bx[0]=0; bx[1]=0…」と 20 回も入力しな
くてはならなくなった。それではソースコードが無駄に長くなってしまうため、20 回繰り返すル
ープを使用して初期値を入れる。
弾の移動や弾の描画の部分でも同じようにループが使われ、20 回分の処理が行われている。
弾の発射間隔の調整
int cool_time;
if(cool_time < 0){ }
cool_time = 5;
cool_time -= 1;
cool_time は弾を発射してから次の弾が発射可能になるまでの時間である。cool_time は毎フレ
ームごとに cool_time -= 1; で値が減っていく。弾が発射されると cool_time の値は 5 となり、次
のフレームで 4、次で 3、2、1、0…と減っていく。弾が発射可能なのは cool_time の値が0以下
の時としておくことで、5 フレームの間弾が発射を不可能にすることができる。
発射する弾の選択
for(i=0; i<20; i+=1){
if(bflag[i] == 0){
break;
}
弾を発射するとき、どの弾を発射するかを決めている。0番から 19 番まである弾を0番から一
つずつ発射可能か調べていき、発射可能な弾を見つけるとその場でループ(for(i=0; i<20; i+=1){ }
のこと)を抜ける。
ループを抜けた時の i の番号の弾は発射可能であるため、
i 番目の弾を発射する。
全ての弾が発射できない場合、ループは 20 回実行され、i の値は 20 となる。弾は 0~19 番ま
での 20 発であるため、i が 20 だと存在しない弾を指定しまう。そのため「if( i < 20)」という条
件を加えて 20 以上の配列番号を指定しないようにしている。
右図を弾の配列とする。灰色の部分が画面に存在して
おり発射不可能の弾とし、白い部分が発射可能の弾とす
る。この配列の場合、for(;;)文は以下のようになる。
0 1 2 3 4 5 6 7 8
9
A
B
C
for( i=0; i<10 ; i+=1){ 処理 }
パターンAの場合は 0 番が発射可能のため、i=0 でル
ープを抜け、0 番の弾が発射される。
D
E
パターン B の場合は 0 番が不可能、1 番が不可能と一
発射する弾の選択例
つ一つ確認していき、6 番の時点で発射可能となり、i=6
でループを抜ける。パターン C では 0 番、パターン D では 2 番の弾が発射される。
パターンEの場合は 0 番、1 番、と調べていき、9 番を調べる。その後、i+=1 で i は 10 となり、
i<10 の条件を満たさなくなりループを抜ける。処理を抜けた段階で i は 10 となっており、存在し
ない弾を指定していることとなるが、存在しない弾は発射されないようになっているため、弾は
発射されない。
練習5
サンプルではボタンを押している間、常に弾を発射し続ける。そこで、サンプルを改造してボタ
ンを一度押したときには一発の弾しか発射されないようにせよ。なお、弾を連射したい場合はボ
タンを連打することで連射できるようにすること。
ヒント:キーが押されているか判断するとき
int Key[256]
int old_Key[256]
判断結果
に使われている Key 配列だが、Key 配列ひと
○
○
押し続けている
○
×
押し始めた
×
○
放された直後
×
×
放されている
つではボタンが押しているか放されているか
の2つの情報しか得ることができない。そこ
で、Key 配列と同じ大きさを持つ名前の違う
配列(例えば old_Key)を作り、そこに 1 フレ
ーム前の Key 配列の状態を保存する。キーが
図 1
2 つの配列を使ったキーの状態判断表
押されているかを判断するところで二つの配列の状態を調べることでキーが押され続けている、
押し始めである、放した、放されている、の 4 つの状態を知ることができる。
ボタンを一度押したときには 1 発の弾しか発射されないことは、弾の発射条件を押し始めの時
に限定することで実現できる。
注意:この方法を使う場合、最初のフレームでは old_Key 配列には値が入っていない。その状
態で old_Key 配列の値を参照しようとするとエラーが発生してしまう。そのため、配列を宣言し
た後でループを回して old_Key に初期値(0)を入れておく必要がある。
13. 敵の出現
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow ){
//初期化処理-------------------------------------------------------------------------------------省略
//変数の宣言----------------------------------------------------------------------------------省略
int egh;
//敵の画像ハンドル
int ex[10], ey[10];
//敵 10 体分の描画座標
int eflag[10];
//敵 10 体分の存在フラグ
int e_counter;
//敵の出現タイミングをはかるためのカウンター
//初期値の設定----------------------------------------------------------------------------省略
for(i=0; i<10; i+=1){
//敵 10 体分処理を行う
ex[i] = 0;
//敵の初期座標入力
ey[i] = 0;
eflag[i] = 0;
//敵は最初存在しないため、存在しないようにする
}
e_counter = 0;
egh = LoadGraph("enemy.bmp"); //敵画像の読み込み
//メインループ---------------------------------------------------------------------------------for(;;){
//無限ループ
//キー入力状況取得
GetHitKeyStateAll(Key); //キーボードのすべてのキーの押下状態を Key に記録する
//プレイヤーの移動------------------------------------------------------------------------省略
//弾の発射--------------------------------------------------------------------------------省略
//弾の移動--------------------------------------------------------------------------------省略
//敵の出現-------------------------------------------------------------------------------if(e_counter%30 == 0){
//30 カウントごとに処理を行う
for(i=0; i<10; i+=1){
//弾の発射と同じく 10 体の敵の中から出現可能な
敵を調べる
if(eflag[i] == 0){
break;
}
}
if(i < 10){
//全ての敵が出現できない場合、
//i は10になっている。i が10より小さい場合のみ処理を行う
eflag[i] = 1;
//敵を出現させる
ex[i] = GetRand(640);
//敵のx座標を 0~640 までの値からラン
ダムに決定する
ey[i] = 0;
//敵の出現位置は画面上端のため、y座標は0とす
る
}
}
e_counter += 1;
//敵の移動-------------------------------------------------------------------------------for(i=0; i<10; i+=1){
if(eflag[i] == 1){
ey[i] += 3;
if(ey[i] > 480){
//敵を下方向へ移動させる
//敵が画面外へ移動した場合
eflag[i] = 0;
}
}
}
//敵を消滅状態にする
//画像の描画------------------------------------------------------------------------------DrawGraph(px, py, pgh, TRUE);
for(i=0; i<20; i+=1){
//プレイヤー画像を(x,y)座標に表示する
//弾 20 発分処理を行う
if(bflag[i] == 1){ //弾が存在している場合
DrawGraph(bx[i], by[i], bgh, TRUE);
//弾画像を描画する
}
}
for(i=0; i<10; i+=1){
//敵 10 体分処理を行う
if(eflag[i] == 1){ //敵が存在している場合
DrawGraph(ex[i], ey[i], egh, TRUE);
//敵画像を描画する
}
}
//ダブルバッファリング処理----------------------------------------------------------------省略
//ゲーム終了処理--------------------------------------------------------------------------省略
}
//終了処理---------------------------------------------------------------------------------------------DxLib_End() ;
// DXライブラリ使用の終了処理
return 0 ;
// ソフトの終了
}
このソースコードから新たに敵画像(“enemy.png”)を使用する。画像は
各自で用意すること。
enemy.bmp
・キーボードの左右のキーでキャラクターを移
動させることができる。
・ESCキーを押すと終了する
・Zキーを押している間、弾が連射される
・弾は最大20発同時に表示され、20発以上
発射しようとしても弾が発射されない
・0.5 秒ごとに画面上端から敵が降ってくる。
・敵は下方向へのみ移動し、画面下端に消える
と消滅する
・敵はあたり判定をもっておらず、自機や弾が
触れてもなにもおこらない
敵の出現
サンプルコード実行画面
解説
この項目では敵を出現させる。敵は画面上端からランダムに出現し下方向に移動していき、画面
の下端を越すと消滅する。また、この段階ではあたり判定は作製しないため、敵は自機や弾をすり
抜けて移動していく。
敵の処理は基本的に弾と同じである。弾との違いは下方向へ移動すること、消滅方法、出現方法、
出現座標の指定方法だけである。そのため、解説する部分は弾との違いの部分だけとする。
敵の消滅
弾は上方向へ移動し(y -= 3)、画面の上端を超える(y < 0)と消滅した。敵の場合もほぼ同じで下
方向へ移動し(y += 2)、画面の下端を超える(y > 480)と消滅する。なお、480 とはデフォルト設定
の時の画面の縦幅のことである。ちなみに横幅は 640 であり、これらの幅は DX ライブラリの
SetWindowSizeExtendRate 関数で変更することができる。詳しくはDXライブラリのリファレ
ンスを参照すること。
カウンターを用いた一定間隔ごとの敵の出現
if(e_counter%30 == 0){
//30 カウントごとに処理を行う
「%30」とは「30で割った余り」という意味である。毎フレーム 1 ずつ増加していく e_counter
を30で割った場合、その余りは 0、1、2…28、29、0、1、2、…と0~29までの値を繰り返す
ことになる。そのため、余りが0となるのは 30 フレームに1度となり、e_counter%30==0 は 30
カウントごとに処理が行われることとなる。
乱数を用いた敵の出現座標の設定
ex[i] = GetRand(640);//敵のx座標を 0~640 までの値からランダムに決定する
GetRand 関数は0から引数で指定した数値までのどれかの数値を返す関数である。そのため、
ex には 0~640 までの値のどれかが入る。また、敵の y 座標は 0 としてあるため、敵の出現座標
は x 座標が 0~640、y座標が 0 となり、敵は画面上部からランダムに出現することになる。
練習6
サンプルコードを改造し、一定時間後にボス敵が出現するようにせよ。また、ボスキャラクターは
通常の敵とは違う動きをして画面に残り続けるようにすること。
ヒント:なし。各自で好みのボス敵を作ること。
資料4.
当たり判定とタイトル画面
14. 当たり判定
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow ){
//初期化処理-------------------------------------------------------------------------------------省略
//変数の宣言----------------------------------------------------------------------------------省略
int psizex, psizey;
//プレイヤーの当たり判定サイズ
int bsizex, bsizey;
//弾当たり判定サイズ
int esizex, esizey;
//敵当たり判定サイズ
int j=0;
//for 文回転用変数2
//初期値の設定----------------------------------------------------------------------------省略
psizex = 40;
//プレイヤーあたり判定サイズx
psizey = 30;
//プレイヤーあたり判定サイズy
bsizex = 10;
//弾あたり判定サイズx
bsizey = 10;
//弾あたり判定サイズy
esizex = 40;
//敵あたり判定サイズx
esizey = 40;
//敵あたり判定サイズy
//メインループ----------------------------------------------------------------------------------
for(;;){
//無限ループ
//キー入力状況取得
省略
//プレイヤーの移動------------------------------------------------------------------------省略
//弾の発射--------------------------------------------------------------------------------省略
//弾の移動--------------------------------------------------------------------------------省略
//敵の出現-------------------------------------------------------------------------------省略
//敵の移動-------------------------------------------------------------------------------省略
//弾と敵の当たり判定--------------------------------------------------------------------for(i=0; i<20; i+=1){
//弾の弾数回処理を行う
for(j=0; j<10; j+=1){
//敵の数分処理を行う
//敵と弾が両方とも存在している場合
if(bflag[i] == 1 && eflag[j] == 1){
//あたり判定の判断
if(bx[i] < ex[j] + esizex &&
bx[i] + bsizex > ex[j] &&
by[i] < ey[j] + esizey &&
by[i] + bsizey > ey[j]){
bflag[i] = 0;//命中したので弾を消す
eflag[j] = 0;//命中したので敵を消す
}
}
}
}
//敵とプレイヤーの当たり判定------------------------------------------------------------for(i=0; i<10; i+=1){
//敵の回数分処理を行う
if(eflag[i] == 1){ //敵が存在している場合
//あたり判定の判断
if(ex[i] < px + psizex &&
ex[i] + esizex > px &&
ey[i] < py + psizey &&
ey[i] + esizey > py){
eflag[i] = 0;
//命中したので敵を消す
//自機に攻撃が命中したが、
//命中した後の処理はまだ作っていない
//次の項目で命中した後の処理を作った後、
//下の行のコメントアウトを解除する
//game_state = 2; //ゲーム状態をゲームオーバー
にする
}
}
}
//画像の描画------------------------------------------------------------------------------省略
//ダブルバッファリング処理----------------------------------------------------------------省略
//ゲーム終了処理--------------------------------------------------------------------------省略
}
//終了処理----------------------------------------------------------------------------------------------
}
DxLib_End() ;
// DXライブラリ使用の終了処理
return 0 ;
// ソフトの終了
-・キーボードの左右のキーでキャラクターを移動させることができる。
・ESCキーを押すと終了する
・スペースキーを押している間、弾が連射される
・弾は最大20発同時に表示され、20発以上発射しようとしても弾が発射されない
・0.5 秒ごとに画面上端から敵が降ってくる。
・敵は下方向へのみ移動し、画面下端に消えると消滅する
・敵が弾にあたると敵と弾の両方が消滅する
・敵が自機にあたると弾が消えるが、自機にはなにもおこらない
当たり判定
解説
あたり判定
前項目では敵に弾を当ててもすり抜けてしまった。そこで、この項目ではあたり判定を作製し、
敵を倒すことができるようにする。なお、あたり判定の方法にはいくつか種類があるが、サンプル
ソースコードで行っているあたり判定は矩形(四角形)によるあたり判定である。また、この項目の練
習のヒントでは円によるあたり判定を説明する。
あたり判定のサイズ
int psizex = 40;
ここで宣言している変数にはあたり判定のサイズを記憶しておく。サンプルソースコードでは
画像のサイズとしているが、この値を小さくすることで画像の割に小さいあたり判定を作ること
もできる。
あたり判定の判断
あたり判定の判別方法は2つの四角形が重なって
いるかどうかで判断する。範囲が重なっているとは
・左上の座標が相手の右下の座標より左上にある
・右下の座標が相手の左上の座標より右下にある
の2つの条件を同時に満たしている状態をさす。
まずはAとDのx座標に注目する。Aの左上のx
座標は 26 で、右下のx座標は 85 である。そしてD
の左上のx座標は 65 で右下のx座標は 117 だ。条
件である「左上の座標が相手の右下の座標より左上
にある」をx座標に注目してみると、
「Aの左がDの
あたり判定例
右より小さい」
「Aの右がDの左より大きい」図1の
AとDの場合、Aの左は 26 でDの右の 117 より小さく、A の右の 85 は D の左の 65 より大きい。
そのため、x座標で比べるとAとDは重なっていると言える。この条件式と同じことをy座標で
も行い、両方が正しい場合2つの矩形は重なっており、あたり判定はあたっていると言える。
ソースコード中では「bx[i] < ex[j] + esizex &&」として表現されている。bx[i]は矩形の左端の
x座標、ex[j] + esizex は矩形の右端のx座標を意味している。&&は両方の条件が正しい場合のみ
正しいとう条件文(A && B ではAとBの両方がただしくないと正しいと判断されない)である。
なお、DrawGraph 関数で画像を表示する際は画像の左上の座標が指定した座標となる。画像の
右端や下端の座標を求める場合は「指定した座標+画像の幅」で座標を得ることができる。
練習7
矩形ではなく対象との直線距離を用いたあたり判定を実
装せよ
ヒント:直線距離を用いたあたり判定は矩形の重なり
によるあたり判定よりも仕組みが簡単である。
右図の場合、Cの長さが2つの円の半径の合計より短
ければ当たっている、長ければ当たっていないという仕
組みである。Cの長さは(A の2乗+Bの2乗)の平方根を
求めることで得ることができる。
計算式で表すならば、
A*A + B*B < (赤の半径+青の半径) * (赤の半径+青の半径)
が正しい場合、あたっていると表現される。
円を用いたあたり判定
注意:C 言語では 「A^2」と記述しても A の 2 乗とは計算されない
15. タイトル画面とゲームオーバー画面
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow ){
//初期化処理-------------------------------------------------------------------------------------省略
//変数の宣言----------------------------------------------------------------------------------省略
int game_state = 0;
//ゲームの状態
int white = GetColor(255, 255, 255);//白色データ
char OldKey[256];
//1フレーム前のキーの入力状況を記録する配列
//初期値の設定----------------------------------------------------------------------------省略
for(i=0; i<256; i+=1){
//Key、OldKey 配列の全ての変数に0を入れる
Key[i] = 0;
OldKey[i] = 0;
}
//メインループ=======================================================
for(;;){
//無限ループ
//キー入力状況取得--------------------------------------------------------------------for(i=0; i<256; i+=1){
//1 フレーム前のキー状況をコピー
OldKey[i] = Key[i];
}
GetHitKeyStateAll(Key); //キーボードのすべてのキーの押下状態を Key に記録する
switch(game_state){
//ゲームの状態ごとに処理をわける
case 0: //タイトル処理============================================
SetFontSize(32); //フォントサイズの設定
DrawString(100, 100, "サンプル☆シューティング", white);//タイトルの表示
SetFontSize(16); //フォントサイズの設定
//開始方法の表示
DrawString(100, 400, "スペースキーを押すと開始します", white);
if(Key[KEY_INPUT_SPACE] == 1 &&
OldKey[KEY_INPUT_SPACE] != 1){//Zキーが押されている場合
game_state = 1;
//ゲーム処理へ移行
}
break;
//タイトル処理終了
case 1: //ゲーム処理=============================================
//プレイヤーの移動------------------------------------------------------------------------省略
//弾の発射---------------------------------------------------------------------------------
省略
//弾の移動--------------------------------------------------------------------------------省略
//敵の出現-------------------------------------------------------------------------------省略
//敵の移動-------------------------------------------------------------------------------省略
//弾と敵の当たり判定--------------------------------------------------------------------省略
//敵とプレイヤーの当たり判定------------------------------------------------------------省略 ただし、以下の行のコメントアウトを解除する
「game_state = 2;
//ゲーム状態をゲームオーバーにする」
//画像の描画------------------------------------------------------------------------------省略
break;
//ゲーム処理終了
case 2: //ゲームオーバー処理========================================
SetFontSize(26); //フォントサイズの設定
DrawString(100, 300, "ゲームオーバー", white);//タイトルの表示
if(Key[KEY_INPUT_SPACE] == 1 &&
OldKey[KEY_INPUT_SPACE] != 1){//Zキーが押されている場合
//初期化処理
for(i=0; i<20; i+=1){
//弾の全消去
bflag[i] = 0;
}
for(i=0; i<10; i+=1){
//敵の全消去
eflag[i] = 0;
}
px = 200;
//自機の初期位置の設定
py = 400;
//自機の初期位置の設定
game_state = 0;
//ゲーム処理へ移行
}
break;
//ゲームオーバー処理終了
default: //タイトル、ゲーム、ゲームオーバー以外の状態の処理================
break;
//それ以外の状態処理終了
}
//ダブルバッファリング処理----------------------------------------------------------------省略
//ゲーム終了処理--------------------------------------------------------------------------省略
}
//終了処理---------------------------------------------------------------------------------------------DxLib_End() ;
// DXライブラリ使用の終了処理
return 0 ;
// ソフトの終了
}
・キーボードの左右のキーでキャラクターを移動させることができる。
・ESCキーを押すと終了する
・スペースキーを押している間、弾が連射される
・弾は最大20発同時に表示され、20発以上発射しようとしても弾が発射されない
・0.5 秒ごとに画面上端から敵が降ってくる。
・敵は下方向へのみ移動し、画面下端に消えると消滅する
・敵が弾にあたると敵と弾の両方が消滅する
・ゲームを開始するとタイトル画面が表示され、スペースキーが押されるとゲームが開始される
・自機が敵とあたるとゲームオーバー画面が表示される
・ゲームオーバー画面でスペースキーが押されるとタイトル画面に戻る
ゲーム画面移行図
タイトル画面とゲームオーバー画面
解説
自機が動き、弾を撃ち、敵を撃墜できる。現段階でシューティングゲームはほぼ完成していると
言える。この項目ではゲームにタイトル画面とゲームオーバー画面を追加し、自機と敵が衝突した
時にはゲームオーバー画面に移行するようにする。そして、この項目を終えた段階でサンプルのシ
ューティングゲームは完成したものとする。
ゲームの状態分け
ゲームの状態をタイトル、ゲーム、ゲームオーバーの3つの状態にわけ、処理を状態ごとにわけ
る。ゲームの状態は「int game_state」に記憶しておき、game_state の値が 0 の時はタイトル処理、
1 の時はゲーム処理、2 の時はゲームオーバー処理を行うものとする。
ゲームの状態を分けるときに使われている「swich(game_state){}」について説明をする。これは
swicth 文といい、
「( )」内の変数の値により「{ }」内の処理を分ける文である。
「( )」内の変数
の値と同じ「case 値:」の部分へ処理が移る(case に同じ値がない場合は default の処理へ処理が移
る)。そして、
「break;」 の部分で switch 文を抜ける。なお、case の後の処理の最後に「break;」が
ない場合、次にある case の中の処理も実行してしまうため注意すること。
右の例の場合
int a=0;
・
「a」の値が0の場合:処理0と処理1が実行される
switch( a ){
・「a」の値が1の場合:処理1が実行される
case 0:
//処理0
・「a」の値が2の場合:処理2が実行される
・「a」の値が 200 の場合:処理2が実行される
case 1:
//処理1
break;
default:
//処理2
break;
}
文字の表示
「DrawString( x座標, y座標, “表示する文字”, 文字色);」
DrawString 関数を使うことで画面に文字を描画することができる。関数の引数は上記のとおり
である。文字色は「GetColor(R 値, G 値, B 値)」関数で得ることができる。サンプルでは「int
white」に「white = GetColor(255, 255, 255);」で白い色の値を入れている。
なお、
「DrawFormatString」関数を使うことで変数の値を文字として表示することもできる。
この機能を使うことでゲームのスコアを表示することもできる。
Key 配列を2つ使ったキー入力
資料③の練習5を参照。
練習8
ゲームを開始し、一定時間自機が敵に接触せずに生き残ると「ゲームクリア」と表示されるクリア
画面へ移行するようにせよ。
ヒント:ゲーム状態へ移行した後に1フレームごとに1ずつ値が増加していくカウンターを作り、
その値が一定値以上になった時にゲーム状態をクリア状態へ移行するようにする。クリア状態の画
面はゲームオーバー状態の文字が違うだけで構わない。
16. 完成したゲームの「exe ファイル化」
現状ではゲームをプレイするためには VC++でプロジェクトファイルを開き、デバッグをしなけ
ればならない。しかしそれでは VC++が入っている PC でしかゲームがプレイできない。
そこで、この項目では完成したゲームを実行ファイル(exe ファイル)として出力し、以下の動作環
境を満たした全てのマシンで動かすことができるようにする方法を説明する。
表 1
動作環境
Windows98 以降の OS
動作環境
DirectX9 以降
32MB 以上の空きメモリ

ソリューション構成を「Debug」から「Release」にする
操作1

デバッグする
操作2
デバッグ後はソリューション構成を Debug に戻しておくこと。なお、Debug ではなくソリュ
ーションのビルドやデバッグなしで開始でも exe ファイルは出力される。

「Release」フォルダ内に exe ファイルが出力されている
exe ファイルの場所1
exe ファイルの場所2
exe ファイル以外にも多数のファイルが入っているが、exe ファイル以外のファイルがなくても
正常に動作する。そのため、別のフォルダに exe ファイルだけを移しても正常に動作する。

exe ファイルを入れたフォルダと同じフォルダに素材ファイルを入れる
画像や音声ファイルは exe ファイルと同じディレクトリに存在しないと読み込まない。その
ため、exe ファイルと同じフォルダに素材ファイルを入れる。

以上でゲームファイルの完成とする。
資料5.
音声ファイルの再生、背景
17. BGM の演奏
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow ){
//初期化処理-------------------------------------------------------------------------------------省略
//変数の宣言----------------------------------------------------------------------------------省略
int titlebgm;
//タイトル画面で演奏する BGM ハンドル
int bgmflag;
//BGM が演奏中か判断するフラグ
1なら演奏中 0なら停止中
//初期値の設定----------------------------------------------------------------------------省略
titlebgm = LoadSoundMem("title.mp3");
//タイトル画面 BGM を読み込む
bgmflag = 0;
//BGM を停止中にする
//メインループ==========================================================
for(;;){
//無限ループ
//キー入力状況取得--------------------------------------------------------------------省略
switch(game_state){
//ゲームの状態ごとに処理をわける
case 0: //タイトル処理============================================
if(bgmflag == 0){ //BGM がまだ演奏されていない場合、演奏する
PlaySoundMem(titlebgm, DX_PLAYTYPE_LOOP); //BGM の演奏
開始
bgmflag = 1;
//BGM 演奏中フラグを立てる
}
SetFontSize(32); //フォントサイズの設定
DrawString(100, 100, "サンプル☆シューティング", white);//タイトルの表示
SetFontSize(16); //フォントサイズの設定
DrawString(100, 400, "スペースキーを押すと開始します", white);
if(Key[KEY_INPUT_SPACE] == 1 && OldKey[KEY_INPUT_SPACE] !=
1){//Zキーが押されている場合
game_state = 1;
//ゲーム処理へ移行
StopSoundMem(titlebgm);
//BGM を停止する
bgmflag = 0;
}
//BGM 演奏中フラグを折る
break;
//タイトル処理終了
case 1: //ゲーム処理==============================================
省略
break;
//ゲーム処理終了
case 2: //ゲームオーバー処理=====================================
省略
break;
//ゲームオーバー処理終了
default: //タイトル、ゲーム、ゲームオーバー以外の状態の処理=================
break;
//それ以外の状態処理終了
}
//ダブルバッファリング処理----------------------------------------------------------------省略
//ゲーム終了処理--------------------------------------------------------------------------省略
}
//終了処理---------------------------------------------------------------------------------------------省略
}
画像と同じように音声ファイル「"title.mp3”」を用意し、プロジェクトファイル内に入れること。
・タイトル画面で音楽ファイルが演奏されるようになる。
BGM の演奏 解説
音声ファイルの再生は基本的に画像を表示する時と同じである。LoadSoundMem 関数で音声ファイル
を 読 み 込 み 、 PlaySoundMem 関 数 で 再 生 す る 。 画 像 フ ァ イ ル と 違 う 点 は ル ー プ の 中 で 常 に
PlaySoundMem 関数を呼び出し続けてはいけない点と、StopSoundMem 関数で音楽を停止しないとい
けない点である。

LoadSoundMem("title.mp3");
LoadGraph 関数と同じように音声ファイルをメモリに読み込む。読み込むことのできる音声ファ
イル形式は「mp3」
「WAV」
、
「Ogg」の3種類である。音声ファイルは画像ファイルと比べてファイ
ルサイズが大きいことが多い。サイズの大きなファイルを読み込もうとすると若干のロード時間が
発生するため注意すること。

PlaySoundMem(titlebgm, DX_PLAYTYPE_LOOP);
DrawGraph 関数と似たように音楽ファイルを再生する。DX_PLAYTYPE_LOOP は再生形式を指
定するもので、以下の3つから選んで記述する。
DX_PLAYTYPE_NORMAL
: ノーマル再生
音楽の再生が終了するまで他の処理を停止する。
DX_PLAYTYPE_BACK
: バックグラウンド再生
音楽を再生しながら他の処理を実行する。なお、音楽は演奏が終了すると停止する。
DX_PLAYTYPE_LOOP
: ループ再生
バックグラウンド再生と同じだが、音楽が終了した後にもう一度最初から音楽が再生される。
バックグラウンド再生は効果音を再生する時に使用し、ループ再生は BGM を再生する時に使用す
る。

StopSoundMem(titlebgm);
titlebgm の音声ファイルの再生を停止する。

多重再生の防止
if(bgmflag == 0){
PlaySoundMem(titlebgm, DX_PLAYTYPE_LOOP); //BGM の演奏開始
bgmflag = 1;
//BGM 演奏中フラグを立てる
}
この条件を付けずに PlaySoundMem 関数をメインループの中に直接書き込んでしまうと、毎フレ
ームごとに BGM が最初から再生されてしまい、まるでなにも聞こえてないようになってしまう。
そのため、音楽再生中は別の音楽を再生しないようにフラグを使って管理する。
練習9
ゲーム処理、ゲームオーバー処理でそれぞれ別の BGM が再生されるようにせよ。また、バックグラウ
ンド再生を利用しゲームに効果音を追加せよ。
ヒント:なし
18. 背景の作成
#include "DxLib.h"
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow ){
//初期化処理-------------------------------------------------------------------------------------省略
//変数の宣言----------------------------------------------------------------------------------省略
int backgh;
//背景画像 画像ハンドル
//初期値の設定----------------------------------------------------------------------------省略
backgh = LoadGraph("back.bmp"); //背景画像の読み込み
//メインループ==========================================================
for(;;){
//無限ループ
//キー入力状況取得--------------------------------------------------------------------省略
switch(game_state){
//ゲームの状態ごとに処理をわける
case 0: //タイトル処理=============================================
省略
break;
//タイトル処理終了
case 1: //ゲーム処理==============================================
省略
//画像の描画------------------------------------------------------------------------------DrawGraph(0,0,backgh, TRUE);
//背景画像を描画する
DrawGraph(px, py, pgh, TRUE);
//プレイヤー画像を(x,y)座標に表示する
for(i=0; i<20; i+=1){
//弾 20 発分処理を行う
if(bflag[i] == 1){ //弾が存在している場合
DrawGraph(bx[i], by[i], bgh, TRUE);//弾画像を描画する
}
}
for(i=0; i<10; i+=1){
//敵 10 体分処理を行う
if(eflag[i] == 1){ //敵が存在している場合
DrawGraph(ex[i], ey[i], egh, TRUE);//敵画像を描画する
}
}
break;
//ゲーム処理終了
case 2: //ゲームオーバー処理========================================
省略
break;
//ゲームオーバー処理終了
default: //タイトル、ゲーム、ゲームオーバー以外の状態の処理================
break;
//それ以外の状態処理終了
}
//ダブルバッファリング処理----------------------------------------------------------------省略
//ゲーム終了処理--------------------------------------------------------------------------省略
}
//終了処理---------------------------------------------------------------------------------------------省略
}
画面いっぱいに表示することのできる背景画像(640,480 の画像ファイル)を新たに使用する(“back.bmp”)
図のように背景が表示されるようになる
サンプルソース実行画面
back.bmp
背景の追加
解説
全ての画像の下になるように大きな画像を表示しただけである。しかし、それでもゲームの世界観を
表現するためには非常に有効な手段である。
練習10
背景画像を動かし、画面のスクロールを表現せよ。
ヒント:右の画像を背景画像として用いた場合、下の図の
ように画面がスクロールする。
同じ画像を2つ用意し、2つの画像を同時に下方向へ移動させる。下の画像が完全に画面から外へでた
とき、下へ出た方の画像を上へと持ってくる。そのようにしてそのようして途切れることなく画像を表
示させることでスクロールを表現する。
・波線部がウィンドウである。
・右方向へ進むにつれ後のフレームとなる

資料6.
敵の弾発射、レーザー、スコア
19. 敵の弾発射
#include "DxLib.h"
#include <math.h>
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow ){
//初期化処理-------------------------------------------------------------------------------------//変数の宣言----------------------------------------------------------------------------------省略
float ebx[20], eby[20];
//敵が発射する弾座標
int ebflag[20];
//敵が発射する弾フラグ
int ebcounter[10];
//敵が弾を発射するまでの時間
float ebangle[20];
//敵の弾の移動角度
//初期値の設定----------------------------------------------------------------------------省略
for(i=0; i<20; i+=1){
ebx[i] = 0;
//敵の弾数回処理をする
//敵の弾の初期化
eby[i] = 0;
ebflag[i] = 0;
ebangle[i] = 0;
}
for(i=0; i<10; i+=1){
//敵の数分処理をする
ebcounter[i] = 0; //敵ごとに弾発射カウンターを設定
}
//メインループ=======================================================
for(;;){
//無限ループ
//キー入力状況取得--------------------------------------------------------------------case 0: //タイトル処理============================================
case 1: //ゲーム処理========================================
//プレイヤーの移動------------------------------------------------------------------------//弾の発射--------------------------------------------------------------------------------//弾の移動--------------------------------------------------------------------------------//敵の出現-------------------------------------------------------------------------------//敵の移動--------------------------------------------------------------------------------
//敵の弾の発射--------------------------------------------------------------------------for(i=0; i<10; i+=1){
//敵の数回処理
if(eflag[i] == 1){ //敵が存在している場合
if(ebcounter[i] < 0){//敵の弾発射までの時間が0の場合
for(j=0; j<20; j+=1){//20発ある弾のうち、発射可能な弾を調べる
if(ebflag[j] == 0){ //弾が発射可能
break;
}
}
if(j < 20){
ebflag[j] = 1;
//弾を存在させる
ebx[j] = ex[i];//弾の座標を弾を発射する敵の座標にする
eby[j] = ey[i];
ebangle[j] = atan2((float)(py - ey[i]), (float)(px - ex[i]));
//敵の座標とプレイヤーの座標から弾の発射角度を得る
ebcounter[i] = 300;
//次弾発射までの時間を設定
}
}
ebcounter[i] -= 1; //敵の弾の発射までの時間を減らす
}
}
//敵の弾の移動-------------------------------------------------------------------------for(i=0; i<20; i+=1){
//弾の回数分処理をする
if(ebflag[i] == 1){ //弾が存在している場合のみ処理をする
ebx[i] += cos(ebangle[i]) * 5;
//x座標の移動
eby[i] += sin(ebangle[i]) * 5;
//y座標の移動
if(eby[i] < 0)
//敵の弾の座標が画面外の場合、弾を消滅させる
ebflag[i] = 0;
if(eby[i] > 480)
//敵の弾の座標が画面外の場合、弾を消滅させる
ebflag[i] = 0;
}
}
//弾と敵の当たり判定--------------------------------------------------------------------//敵とプレイヤーの当たり判定-------------------------------------------------------------
//画像の描画------------------------------------------------------------------------------省略
for(i=0; i<20; i+=1){
//敵の弾 20 発分処理を行う
if(ebflag[i] == 1){ //敵の弾が存在している場合
DrawGraph(ebx[i], eby[i], bgh, TRUE);
// 敵 の 弾 画 像
を描画する
}
}
break;
//ゲーム処理終了
case 2: //ゲームオーバー処理=========================================
default: //タイトル、ゲーム、ゲームオーバー以外の状態の処理==============
break;
//それ以外の状態処理終了
}
//ダブルバッファリング処理----------------------------------------------------------------//ゲーム終了処理--------------------------------------------------------------------------}
//終了処理---------------------------------------------------------------------------------------------}
今回のソースコードからは「省略」という文字も省略してある。
・敵が一定のタイミングでプレイヤーを狙う弾を発射してくる。
敵の弾発射
解説
#include <math.h>
atan2();関数や cos()関数、sin()関数を使用するために必要なヘッダーファイルをインクルードする。
この一文を書いておくことで他にも数学関係の関数を使うことができるようになる。詳しくは「math.h」
で検索し調べること。
ebangle[j] = atan2((float)(py - ey[i]), (float)(px - ex[i]));
//敵の座標とプレイヤーの座標から弾の
発射角度を得る
敵が弾を発射するとき、敵の座標とプレイヤーの座標から、両者間の角度を得る。得た角度を敵の
弾ごとに所持している ebangle 変数に記憶しておき、弾を移動させるときにこの角度を使用してプレ
イヤー方向へ弾を移動させる。そうすることでプレイヤーの方へ移動してくる弾を作る。
なお、atan2 関数で得ることのできる角度は単位が度数ではなくラジアンの点に注意すること。ま
た、atan2 関数でxとyを記入する場合、yが先にくることに注意すること。
関数中にある「(float)」とは次の( )内の数値の変数型を float にするという命令である。atan2 関数
の引数は3種類用意されており、それぞれ引数の型が float、double、long double となっている。こ
こに int 型の整数値を書きこむと、コンパイラ(VisualC++2010ExpressEdition)が float、double、long
double のどの引数として扱えばいいかわからず、エラーをはいてしまう。そのため、引数として使う
値は float 型であると(float)を使い指定する必要がある。
ebx[i] += cos(ebangle[i]) * 5;
//x座標の移動
eby[i] += sin(ebangle[i]) * 5;
//y座標の移動
「cos(角度)×距離」
で図のxの値を得ることができ
る。また、
「sin(角度)×距離」で図のyの値を得ることが
できる。
atan2 関数で得た角度を使用することで、敵からプレ
イヤー方向への角度の分だけ弾を移動させることができ
る。
自機を追尾する弾について
今回のサンプルでは敵から弾が発射された瞬間にのみ
cos、sin の値
弾の角度が決められた。常にプレイヤーを追尾してくる
弾を作る場合、一定間隔ごとに弾とプレイヤーの間の距離を算出し直し、修正してやる必要がある。
一定間隔は弾ごとにカウンターを追加することで実現することができる。ただし、角度をそのまま指
定してしまうと確実にプレイヤーの方向へ向かってきて絶対によけることのできない弾になってしま
うので、角度を修正するときに一度角度を度数に変換し、一定の角度以上は変化しないようにするな
ど工夫する必要がある。
今回のソースコードのバグについて
今回のソースコードでは弾の当たり判定は作成していない。以前のサンプルを参考に、各自で作成
すること。
また、自機狙い弾の消滅条件は弾が上下どちらかの画面枠から外に出た場合となっている。しかし、
敵が真横に弾を撃った場合、弾のy座標は変化せず、弾が消えなくなってしまう。プレイが長引くと
敵が発射する弾がなくなってしまい、敵が弾を発射しなくなってしまう。このバグは各自で修正する
こと。
より複雑な軌跡の弾を作成したい場合
下記のホームページが DX ライブラリを用いた弾幕シューティングゲームの作成をわかりやすく説
明している。このホームページを参考にし、弾を作成すること。
検索方法:google「龍神録プログラミングの館」と検索
アドレス:
「http://dixq.net/rp/」
20. レーザー
この項目にはソースコードは載せず、レーザーの作り方のみ説明する。
レーザーとは弾と弾との境目が存在しない連射弾のことである。プレイヤーの弾の連射で cool_time
を設定したが、この値を1や2などの小さい値にすることで弾の発射間隔が短くなり、弾同士が連結し
ているように見える。この連結はあたり判定としても連結しているため、横方向から抜けることができ
ない弾となる。あとは画像を変更するなど、見せ方を工夫することでレーザーを作ることができる。
必殺技の極めて太いレーザーなどはあたり判定が非常に大きな弾を用意し、それを連続発射すること
と画像やエフェクトを使うことで表現することができる。
21. スコア
この項目にはソースコードは載せず、スコアの表示方法のみ説明する。
DX ライブラリには「DrawFormatString」という関数が用意されている。この関数はタイトルを表示
するときに使用した文字描画の機能に変数を追加できる機能を追加したものである。
DrawFormatString(
表示座標x, 表示座標 y , 表示する文字 , 表示する変数 );
と書くことで画面に文字を表示することができる。
なお、表示する文字と表示する変数の記入方法は C 言語の授業で習う「printf()」と同じである。
%d で整数値を表示し、%f で浮動小数点を用いた値を表示する。
スコアを画面に表示したい場合、まずはスコアを入力する変数を用意しておき、敵が倒されるたびに
スコアに値を加算していく。そして、DrawFormatString 関数でスコア変数の値を表示しておくことで
スコアを表現することができる。
例:
int score = 0;
//スコアを入れる変数を宣言
DrawFormatString( 0, 0, GetColor(255,255,255), “スコア:%d” , score)
参考文献
Microsoft Visual Studio Express
( http://www.microsoft.com/japan/msdn/vstudio/express/#top )<2010/08/09 アクセス>
DX ライブラリ置き場
( http://homepage2.nifty.com/natupaji/DxLib/index.html )<2010/08/12 アクセス>
C 言語ゲームプログラミングの館
( http://dixq.net/g/ ) <2010/08/12 アクセス>
初心者のためのポイント学習 C 言語
( http://www9.plala.or.jp/sgwr-t/ ) <2010/08/12 アクセス>
龍神録プログラミングの館
(http://dixq.net/rp/ ) <2010/12/02 アクセス>