シューティングゲームの作成(チュートリアル) - C/C++言語とDXライブラリでゲーム作成入門
目次
- C/C++言語とDXライブラリでゲーム作成入門
- シューティングゲームの作成(チュートリアル)
- ドットイートゲームの作成(チュートリアル)
- 15パズルの作成
- 神経衰弱の作成
- オセロの作成
- オブジェクト指向を活用したシューティングゲーム
このページの内容
今回の目的
簡単なシューティングゲームの作成を通じて、DXライブラリによるゲーム作成の基本を学びます。ソースファイル
今回の講座のソースを全て含んだプロジェクトファイル(Visual C++ 2010)を以下に置いておきます。shooting.zip
自機を表示する
まず、自機を表示するだけのプログラムを作成してみます。自機キャラクタの作成
「shooting」というプロジェクトを作成して、ペイントソフトなどで自機画像を64×64ピクセル、BMP形式で作り、「player.bmp」という名前でプロジェクトフォルダに保存してください。プログラムの入力・実行
「shooting」プロジェクトに「shooting1.cpp」という名前の新規ソースファイルを追加し以下のプログラムを入力してください。
#include "DxLib.h"
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
DxLib_Init();
LoadGraphScreen(288, 400, "player.bmp", FALSE);
WaitKey();
DxLib_End();
return 0;
}
入力が完了したらメニューの「デバッグ」→「デバッグなしで開始」を選び、プログラムを実行します。全画面表示となり、自機が画面下に表示されます。解説
#include "DxLib.h"
DXライブラリのヘッダファイルをインクルードしています。これでDXライブラリが使えるようになります。
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
Windowsアプリケーションはこの関数から始まります。この形式でないと動かないので決め打ちでOKです。引数は使いません。
DxLib_Init();
DXライブラリの初期化を行います。DXライブラリの各種機能を使う前に必要です。
LoadGraphScreen(288, 400, "player.bmp", FALSE);
LoadGraphScreen関数は指定した画像ファイルを読み込み画面に表示します。ここでは座標(288, 400)に「player.bmp」を表示しています。最後の引数はFALSEにすると画像が透過しません。TRUEにすると画像の暗い色で透過します。
WaitKey();
キー入力待ちをします。
DxLib_End();
プログラム終了前に必ずこの関数を呼び出してDXライブラリの後片付けをします。自機を動かす・ダブルバッファリング
次に自機を左右に動かしてみましょう。プログラムの入力・実行
「shooting」プロジェクトに「shooting2.cpp」ファイルを追加し、shooting1.cppの内容をコピーし、shooting1.cppはプロジェクトから除外しておきます(ソリューションエクスプローラーでソースファイルを選択し、メニューの「プロジェクト」→「プロジェクトから除外)。shooting2.cppを以下のように変更します。
#include "DxLib.h"
int player;
int px = 288;
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
ChangeWindowMode(TRUE);
DxLib_Init();
SetDrawScreen(DX_SCREEN_BACK);
player = LoadGraph("player.bmp");
while (!ProcessMessage()) {
ClearDrawScreen();
if (CheckHitKey(KEY_INPUT_LEFT)) px -= 8;
if (CheckHitKey(KEY_INPUT_RIGHT)) px += 8;
DrawGraph(px, 400, player, FALSE);
ScreenFlip();
}
DxLib_End();
return 0;
}
入力が完了したらメニューの「デバッグ」→「デバッグなしで開始」を選び、プログラムを実行します。ウインドウ表示となり、カーソルキーの左右で自機が動けば正常です。解説
int player;
自機のグラフィックハンドルが入る変数です。グラフィックハンドルとはメモリに保存した画像の識別番号です。
int px = 288;
自機のx座標です。座標は画像の左上隅を画面上で表示する位置を指定します。デフォルトの画面の横幅が640ピクセルなので、その半分320に画像の半分32ピクセルを引いた288を指定するとちょうど中央に表示されます。
ChangeWindowMode(TRUE);
ゲーム画面をウインドウ表示にします。
SetDrawScreen(DX_SCREEN_BACK);
グラフィックの描画先を裏画面にします。ゲームではキャラクタが動くたびに画像を消して再描画する必要があるのですが、消したタイミングでチラつきが起こる可能性があります。そこでメモリ上に表示画面(表画面)と同じ構成の見えない画面(裏画面)を用意しておき、裏画面に必要な画像を全て描画し、最後に一気に表画面に転送するとチラつきを防ぐことができます。この手法を「ダブルバッファリング」と言います。
player = LoadGraph("player.bmp");
player.bmpをメモリに読み込み、そのグラフィックハンドルをplayerにセットします。毎回画像をファイルから読み込むのでは遅くなるので最初にメモリに読み込み、ゲーム中はメモリから画面に転送することにより高速化しています。
while (!ProcessMessage()) {
Windowsのメッセージループを処理します。これを行わないとゲームがWindowsの処理を奪ってしまって重くなります。この関数の戻り値は通常は0、ウインドウが閉じられると-1になります。ここではウインドウが閉じられるまでループしています。
ClearDrawScreen();
描画画面(裏画面)に描かれた以前の画像をすべて消しています。
if (CheckHitKey(KEY_INPUT_LEFT)) px -= 8;
if (CheckHitKey(KEY_INPUT_RIGHT)) px += 8;
CheckHitKeyは引数で指定したキーが押されているか調べる関数です。カーソルキーの左が押されていれば自機のx座標を-8、カーソルキーの右が押されていれば自機のx座標を+8しています。
DrawGraph(px, 400, player, FALSE);
カーソルキーで動いた位置にグラフィックハンドルを指定してメモリに保存された自機画像を表示しています。
ScreenFlip();
最後に裏画面を表画面に一気に転送します。敵の表示・自機の移動制限
次に敵を表示して動かしてみましょう。敵キャラクタの作成
敵画像を64×64ピクセル、BMP形式で作り、「enemy.bmp」という名前でプロジェクトフォルダに保存してください。プログラムの入力・実行
「shooting」プロジェクトに「shooting3.cpp」ファイルを追加し、shooting2.cppの内容をコピーし、shooting2.cppはプロジェクトから除外しておきます。shooting3.cppを以下のように変更します。
#include "DxLib.h"
int player, enemy;
int px = 288;
int ex = 0, es = 8;
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
ChangeWindowMode(TRUE);
DxLib_Init();
SetDrawScreen(DX_SCREEN_BACK);
player = LoadGraph("player.bmp");
enemy = LoadGraph("enemy.bmp");
while (!ProcessMessage()) {
ClearDrawScreen();
if (CheckHitKey(KEY_INPUT_LEFT)) {
px -= 8;
if (px < 0) px = 0;
}
if (CheckHitKey(KEY_INPUT_RIGHT)) {
px += 8;
if (px > 640 - 64) px = 640 - 64;
}
ex += es;
if (ex <= 0 || ex >= 640 - 64) es = -es;
DrawGraph(px, 400, player, FALSE);
DrawGraph(ex, 16, enemy, FALSE);
ScreenFlip();
}
DxLib_End();
return 0;
}
入力が完了したらメニューの「デバッグ」→「デバッグなしで開始」を選び、プログラムを実行します。敵が画面上を左右に跳ね返りながら動けばOKです。解説
int player, enemy;
変数enemyが敵画像のグラフィックハンドルです。
int ex = 0, es = 8;
exは敵のx座標、esは敵の速度です。
if (px < 0) px = 0;
自機が画面の左側にはみ出したらそこで止まる処理を入れています。
if (px > 640 - 64) px = 640 - 64;
自機が画面の右側にはみ出したらそこで止まる処理を入れています。
ex += es;
敵のx座標に速度を追加しています。最初の速度は8なので、8ピクセルずつ右に移動します。
if (ex <= 0 || ex >= 640 - 64) es = -es;
敵が画面の左端、または右端にぶつかったら、速度を反転して、跳ね返るようにしています。弾の発射
続いて、弾を発射できるようにしてみましょう。弾キャラクタの作成
弾画像を16×16ピクセル、BMP形式で作り、「shot.bmp」という名前でプロジェクトフォルダに保存してください。プログラムの入力・実行
「shooting」プロジェクトに「shooting4.cpp」ファイルを追加し、shooting3.cppの内容をコピーし、shooting3.cppはプロジェクトから除外しておきます。shooting4.cppを以下のように変更します。
#include "DxLib.h"
int player, enemy, shot;
int px = 288;
int ex = 0, es = 8;
int sx, sy;
bool sf = false;
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
ChangeWindowMode(TRUE);
DxLib_Init();
SetDrawScreen(DX_SCREEN_BACK);
player = LoadGraph("player.bmp");
enemy = LoadGraph("enemy.bmp");
shot = LoadGraph("shot.bmp");
while (!ProcessMessage()) {
ClearDrawScreen();
if (CheckHitKey(KEY_INPUT_LEFT)) {
px -= 8;
if (px < 0) px = 0;
}
if (CheckHitKey(KEY_INPUT_RIGHT)) {
px += 8;
if (px > 640 - 64) px = 640 - 64;
}
if (CheckHitKey(KEY_INPUT_SPACE)) {
if (!sf) {
sf = true;
sx = px + 24;
sy = 400;
}
}
ex += es;
if (ex <= 0 || ex >= 640 - 64) es = -es;
if (sf) {
sy -= 16;
if (sy < 0) sf = false;
}
if (sf) DrawGraph(sx, sy, shot, FALSE);
DrawGraph(px, 400, player, FALSE);
DrawGraph(ex, 16, enemy, FALSE);
ScreenFlip();
}
DxLib_End();
return 0;
}
入力が完了したらメニューの「デバッグ」→「デバッグなしで開始」を選び、プログラムを実行します。スペースキーを押すと弾が発射されます。解説
int player, enemy, shot;
変数shotが弾画像のグラフィックハンドルです。
int sx, sy;
bool sf = false;
弾のx座標がsx、y座標がsyとなります。sfは弾が画面上にある間trueとなるフラグ変数です。
if (CheckHitKey(KEY_INPUT_SPACE)) {
if (!sf) {
sf = true;
sx = px + 24;
sy = 400;
}
}
スペースキーが押されたら弾を発射する部分です。まず弾発射フラグsfをチェックして画面上に弾が残っていないときのみ発射します。弾が発射されたらフラグsfをtrueにし、弾の座標を自機中央に設定しています。
if (sf) {
sy -= 16;
if (sy < 0) sf = false;
}
弾が発射中なら、弾を上方向へ16ピクセル移動し、その結果画面外に出たなら弾発射フラグsfをfalseにしています。
if (sf) DrawGraph(sx, sy, shot, FALSE);
弾が発射中のときのみ弾画像を描画します。当たり判定・スコア
最後に、敵と弾との当たり判定、及びスコア処理を入れます。プログラムの入力・実行
「shooting」プロジェクトに「shooting5.cpp」ファイルを追加し、shooting4.cppの内容をコピーし、shooting4.cppはプロジェクトから除外しておきます。shooting5.cppを以下のように変更します。
#include "DxLib.h"
int player, enemy, shot;
int px = 288;
int ex = 0, es = 8;
int sx, sy;
bool sf = false;
int score = 0;
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
ChangeWindowMode(TRUE);
DxLib_Init();
SetDrawScreen(DX_SCREEN_BACK);
player = LoadGraph("player.bmp");
enemy = LoadGraph("enemy.bmp");
shot = LoadGraph("shot.bmp");
while (!ProcessMessage()) {
ClearDrawScreen();
if (CheckHitKey(KEY_INPUT_LEFT)) {
px -= 8;
if (px < 0) px = 0;
}
if (CheckHitKey(KEY_INPUT_RIGHT)) {
px += 8;
if (px > 640 - 64) px = 640 - 64;
}
if (CheckHitKey(KEY_INPUT_SPACE)) {
if (!sf) {
sf = true;
sx = px + 24;
sy = 400;
}
}
ex += es;
if (ex <= 0 || ex >= 640 - 64) es = -es;
if (sf) {
sy -= 16;
if (sy < 0) sf = false;
}
if (sf) {
if (sx + 16 > ex && sx < ex + 64 && sy + 16 > 16 && sy < 16 + 64) {
sf = false;
ex = 0;
es = 8;
score += 100;
}
}
if (sf) DrawGraph(sx, sy, shot, FALSE);
DrawGraph(px, 400, player, FALSE);
DrawGraph(ex, 16, enemy, FALSE);
DrawFormatString(0, 0, GetColor(255, 255, 255), "SCORE : %d", score);
ScreenFlip();
}
DxLib_End();
return 0;
}
入力が完了したらメニューの「デバッグ」→「デバッグなしで開始」を選び、プログラムを実行します。弾が敵に当たると敵が消滅し、スコアが加算されます。解説
int score = 0;
スコアが入ります。
if (sf) {
if (sx + 16 > ex && sx < ex + 64 && sy + 16 > 16 && sy < 16 + 64) {
sf = false;
ex = 0;
es = 8;
score += 100;
}
}
当たり判定を行なっている部分です。以下の4つの条件があります。
sx + 16 > ex
sx + 16 は弾の右端です。弾の右端が敵の左端よりも大きいならば真です。
sx < ex + 64
ex + 64 は敵の右端です。弾の左端が敵の右端よりも小さいならば真です。
sy + 16 > 16
sy + 16 は弾の下端です。弾の下端が敵の上端よりも大きいならば真です。
sy < 16 + 64
16 + 64 は敵の下端です。弾の上端が敵の下端よりも小さいならば真です。以上4つの条件を全て満たせば弾が敵に当たっていると見なし、弾発射フラグsfをクリアし、敵の位置を初期位置に戻し、スコアに100点を追加しています。