ポンクソフト

シューティングゲームの作成(チュートリアル) - C言語とelで様々なゲームを作ろう

前ページ C言語とelで様々なゲームを作ろう TOP 次ページ

目次

  1. C言語とelで様々なゲームを作ろう
  2. Visual C++ .NET での設定
  3. テンプレートファイルの解説
  4. シューティングゲームの作成(チュートリアル)
  5. パックマン的ゲームの作成(チュートリアル)
  6. ブロック崩しの作成
  7. 15パズルの作成
  8. 横スクロールジャンピングゲームの作成
  9. オセロの作成
  10. 神経衰弱の作成
  11. 7ならべの作成
  12. テトリスの作成
  13. ぷよぷよの作成

プロジェクトファイル

今回の講座のソースを全て含んだプロジェクトファイル(Visual C++ 6.0)を以下に置いておきます。ソースを入力するのが面倒な人は使ってください。
shooting.zip

自機が左右に動く

まず、自機が左右に動くだけのプログラムを作ってみます。

自機キャラクタの作成

ペイントソフトなどで自機画像を 64 × 64 ピクセル、24 ビットカラーの BMP 形式で作り、「jiki.bmp」という名前でプロジェクトフォルダ下の Debug フォルダに保存してください。

プログラムの入力

「shooting」というプロジェクトを作成して、「shooting1.cpp」という新規ファイルを作り template.cpp の内容をコピーし、以下のプログラムの太字部分を追加・変更してください。
///////////////////////////////////////////////////////////////////////////////
//  シューティングゲーム1

#include "el.h"

#define MAIN_SCREEN 1

void MainScreen(void);

DDOBJ jiki;

///////////////////////////////////////////////////////////////////////////////
//  メイン関数

int elMain("シューティングゲーム1");
{
  elWindow(640, 480, FALSE);
  elLoop()
  {
    elSetScreen(MAIN_SCREEN, MainScreen());
  }
  elExitMain();
}

///////////////////////////////////////////////////////////////////////////////
//  ウインドウ生成関数

void elCreate(void)
{
  elDraw::Screen(640, 480);
  jiki = elDraw::LoadObject("jiki.bmp");
  elCallScreen(MAIN_SCREEN);
}

///////////////////////////////////////////////////////////////////////////////
//  キーボード関数

void elKeyboard(void)
{
  case VK_ESCAPE:
  {
    elDraw::Exit();
    break;
  }
  elExitKeyboard();
}

///////////////////////////////////////////////////////////////////////////////
//  イベント関数

long elEvent(void)
{
  elExitEvent();
}

///////////////////////////////////////////////////////////////////////////////
//  メイン画面

void MainScreen(void)
{
  static int mx = 288;

  elDraw::Clear();

  switch (PushKey) {
    case VK_LEFT: mx -= 8; break;
    case VK_RIGHT: mx += 8; break;
  }
  elDraw::Layer(mx, 400, jiki, 0, 0, 64, 64);

  SHOW(0, 0, "ESCキーで終了");
  elDraw::Refresh();
}

ビルド・実行

メニューの「ビルド」→「ビルド」を選び、ソースをビルドします。エラーがなければ、メニューの「ビルド」→「実行」を選び、プログラムを実行します。自機が画面下に表示され、カーソルキーで左右に移動することができれば正常です。

解説

DDOBJ jiki;
DDOBJ はビットマップデータが保存されるオブジェクト型です。格納されたオブジェクトは、自動的に黒色が透けるスプライトになります。
jiki = elDraw::LoadObject("jiki.bmp");
上で定義したオブジェクト jiki に、画像を読み込みます。
static int mx = 288;
自機の初期 x 座標を 288 (画面の中央)にしています。
MainScreen 関数はゲームが動いている限り繰り返し呼び出されるので、x 座標を static 変数にすることによって値を保持しています。
switch (PushKey) {
PushKey は直前に押されたキーが入る変数です。ここでは押されたキーによって分岐しています。
case VK_LEFT: mx -= 8; break;
カーソルキーの左が押されると、PushKey に VK_LEFT が入ります。ここでは x 座標を -8 しています
case VK_RIGHT: mx += 8; break;
カーソルキーの右が押されると、PushKey に VK_RIGHT が入ります。ここでは x 座標を +8 しています
elDraw::Layer(mx, 400, jiki, 0, 0, 64, 64);
elDraw::Layer 関数は画面にスプライトを表示する関数です。定義は以下の通り。
void elDraw::Layer(int Px, int Py, DDOBJ ObjDD, int X1, int Y1, int X2, int Y2);
スプライト ObjDD の左上座標(X1, Y1) - 右下座標(X2, Y2)の領域を(Px, Py)に表示します。

弾・敵を出す

次に弾と敵が出現するプログラムを作成します。

弾・敵キャラクタの作成

弾画像を 16 × 16 ピクセルで「tama.bmp」、敵画像を 64 × 64 ピクセルで「teki.bmp」という名前でそれぞれ作成し、プロジェクトフォルダ下の Debug フォルダに保存してください。

プログラムの入力

「shooting2.cpp」という新規ファイルを作り shooting1.cpp の内容をコピーし、shooting1.cpp をプロジェクトから削除します(FileView から shooting1.cpp を選んで Delete キーを押す)。その後、以下のプログラムの太字部分を追加・変更してください。
///////////////////////////////////////////////////////////////////////////////
//  シューティングゲーム2

#include "el.h"

#define MAIN_SCREEN 1

void MainScreen(void);

DDOBJ jiki, teki, tama;

///////////////////////////////////////////////////////////////////////////////
//  メイン関数

int elMain("シューティングゲーム2");
{
  elWindow(640, 480, FALSE);
  elLoop()
  {
    elSetScreen(MAIN_SCREEN, MainScreen());
  }
  elExitMain();
}

///////////////////////////////////////////////////////////////////////////////
//  ウインドウ生成関数

void elCreate(void)
{
  elDraw::Screen(640, 480);
  jiki = elDraw::LoadObject("jiki.bmp");
  teki = elDraw::LoadObject("teki.bmp");
  tama = elDraw::LoadObject("tama.bmp");
  elCallScreen(MAIN_SCREEN);
}

///////////////////////////////////////////////////////////////////////////////
//  キーボード関数

void elKeyboard(void)
{
  case VK_ESCAPE:
  {
    elDraw::Exit();
    break;
  }
  elExitKeyboard();
}

///////////////////////////////////////////////////////////////////////////////
//  イベント関数

long elEvent(void)
{
  elExitEvent();
}

///////////////////////////////////////////////////////////////////////////////
//  メイン画面

void MainScreen(void)
{
  static int mx = 288;
  static int tx, ty;
  static bool tf = false;
  static int ex = 0;
  static int ed = 1;

  elDraw::Clear();

  switch (PushKey) {
  case VK_LEFT:
    mx -= 8;
    if (mx < 0) mx = 0;
    break;
  case VK_RIGHT:
    mx += 8;
    if (mx > 640 - 64) mx = 640 - 64;
    break;
  case VK_SPACE:
    if (!tf) {
      tf = true;
      tx = mx + 24;
      ty = 400;
    }
    break;
  }

  ex += ed * 8;
  if (ex <= 0) ed = 1;
  else if (ex >= 640 - 64) ed = -1;

  if (tf) {
    ty -= 16;
    if (ty < 0) tf = false;
  }

  elDraw::Layer(mx, 400, jiki, 0, 0, 64, 64);
  if (tf) elDraw::Layer(tx, ty, tama, 0, 0, 16, 16);
  elDraw::Layer(ex, 16, teki, 0, 0, 64, 64);

  SHOW(0, 0, "ESCキーで終了");
  elDraw::Refresh();
}

実行

ビルドして実行します。敵が画面上を左右に動き、弾をスペースキーで発射することができれば正常です。

解説

DDOBJ jiki, teki, tama;
敵画像は teki、弾画像は tama というオブジェクトに入ります。
static int tx, ty;
弾の x, y 座標です。
static bool tf = false;
弾が画面上にあれば true、無ければ false になるフラグ変数です。
static int ex = 0;
敵の x 座標です。
static int ed = 1;
敵の移動方向です。右へ移動中なら 1、左へ移動中なら -1 になります。
if (mx < 0) mx = 0;
自機が画面左へはみ出さないようにしています。
if (mx > 640 - 64) mx = 640 - 64;
同じく自機が画面右へはみ出さないようにしています。
case VK_SPACE:
  if (!tf) {
    tf = true;
    tx = mx + 24;
    ty = 400;
  }
  break;
スペースキーが押されたときの処理を行なっている部分です。まず弾が画面上に存在しているかチェックし、存在していなければ弾発射フラグを true にし、弾の座標を自機の中央から発射されるように設定しています。
ex += ed * 8;
敵の x 座標に方向 * 8 を加えて移動方向に移動します。
if (ex <= 0) ed = 1;
else if (ex >= 640 - 64) ed = -1;
敵が移動した結果、画面端に着いたら、方向を反転しています。
if (tf) {
  ty -= 16;
  if (ty < 0) tf = false;
}
弾発射フラグが true ならば、弾を上方向へ 16 ピクセル移動し、その結果画面外に出たなら弾発射フラグを false にしています。
if (tf) elDraw::Layer(tx, ty, tama, 0, 0, 16, 16);
弾発射フラグが true のときのみ弾を描画しています。
elDraw::Layer(ex, 16, teki, 0, 0, 64, 64);
敵を描画している部分です。

複数キーの同時入力・当たり判定・スコア

前回のプログラムでは、自機の移動中に弾を発射すると自機が止まる不具合がありました。今回は複数キーの同時入力によってその部分を修正します。さらに弾と敵との当たり判定とスコアを追加します。

プログラムの入力

「shooting3.cpp」という新規ファイルを作り shooting2.cpp の内容をコピーし、shooting2.cpp をプロジェクトから削除します。その後、以下のプログラムの太字部分を追加・変更してください。
///////////////////////////////////////////////////////////////////////////////
//  シューティングゲーム3

#include "el.h"

#define MAIN_SCREEN 1

void MainScreen(void);

DDOBJ jiki, teki, tama;

///////////////////////////////////////////////////////////////////////////////
//  メイン関数

int elMain("シューティングゲーム3");
{
  elWindow(640, 480, FALSE);
  elLoop()
  {
    elSetScreen(MAIN_SCREEN, MainScreen());
  }
  elExitMain();
}

///////////////////////////////////////////////////////////////////////////////
//  ウインドウ生成関数

void elCreate(void)
{
  elDraw::Screen(640, 480);
  jiki = elDraw::LoadObject("jiki.bmp");
  teki = elDraw::LoadObject("teki.bmp");
  tama = elDraw::LoadObject("tama.bmp");
  elCallScreen(MAIN_SCREEN);
}

///////////////////////////////////////////////////////////////////////////////
//  キーボード関数

void elKeyboard(void)
{
  case VK_ESCAPE:
  {
    elDraw::Exit();
    break;
  }
  elExitKeyboard();
}

///////////////////////////////////////////////////////////////////////////////
//  イベント関数

long elEvent(void)
{
  elExitEvent();
}

///////////////////////////////////////////////////////////////////////////////
//  メイン画面

void MainScreen(void)
{
  static int mx = 288;
  static int tx, ty;
  static bool tf = false;
  static int ex = 0;
  static int ed = 1;
  static int score = 0;
  static int kl = FREE_KEY;
  static int kr = FREE_KEY;
  static int ks = FREE_KEY;

  elDraw::Clear();

  elSystem::GetKey(VK_LEFT, &kl);
  elSystem::GetKey(VK_RIGHT, &kr);
  elSystem::GetKey(VK_SPACE, &ks);
  if (kl != FREE_KEY) {
    mx -= 8;
    if (mx < 0) mx = 0;
  }
  if (kr != FREE_KEY) {
    mx += 8;
    if (mx > 640 - 64) mx = 640 - 64;
  }
  if (ks != FREE_KEY) {
    if (!tf) {
      tf = true;
      tx = mx + 24;
      ty = 400;
    }
  }

  ex += ed * 8;
  if (ex <= 0) ed = 1;
  else if (ex >= 640 - 64) ed = -1;

  if (tf) {
    ty -= 16;
    if (ty < 0) tf = false;
  }

  if (tf) {
    if (tx + 16 > ex && tx < ex + 64 &&
      ty + 16 > 16 && ty < 16 + 64) {
      score += 100;
      tf = false;
      ex = 0;
      ed = 1;
    }
  }

  elDraw::Layer(mx, 400, jiki, 0, 0, 64, 64);
  if (tf) elDraw::Layer(tx, ty, tama, 0, 0, 16, 16);
  elDraw::Layer(ex, 16, teki, 0, 0, 64, 64);

  SHOW2(0, 0, "SCORE:%d", score);
  elDraw::Refresh();
}

実行

ビルドして実行します。弾が敵に当たったらスコアが100点入れば正常です。

解説

static int score = 0;
スコア変数です。
static int kl = FREE_KEY;
static int kr = FREE_KEY;
static int ks = FREE_KEY;
複数キーの同時入力を行なうためにそれぞれのキーの状態を保持する変数を宣言しています。kl がカーソルキー左、kr がカーソルキー右、ks がスペースキーです。初期値として、キーが押されていないことを表わす FREE_KEY を代入しています。
elSystem::GetKey(VK_LEFT, &kl);
elSystem::GetKey(VK_RIGHT, &kr);
elSystem::GetKey(VK_SPACE, &ks);
カーソル左、右、スペースの各キーの状態を取得して、変数に格納している部分です。
if (kl != FREE_KEY) {
FREE_KEY 以外の値が入っている場合、左キーが押されていると判断しています。
if (tf) {
  if (tx + 16 > ex && tx < ex + 64 &&
    ty + 16 > 16 && ty < 16 + 64) {
    score += 100;
    tf = false;
    ex = 0;
    ed = 1;
  }
}
当たり判定を行なっている部分です。4つの条件があります。
tx + 16 > ex
tx + 16 は弾の右端です。弾の右端が敵の左端よりも大きいならば真です。
tx < ex + 64
ex + 64 は敵の右端です。弾の左端が敵の右端よりも小さいならば真です。
ty + 16 > 16
ty + 16 は弾の下端です。弾の下端が敵の上端よりも大きいならば真です。
ty < 16 + 64
16 + 64 は敵の下端です。弾の上端が敵の下端よりも小さいならば真です。
以上4つの条件を全て満たせば弾が敵に当たっていると見なし、スコアに 100 点を追加し、弾発射フラグをクリアし、敵の位置を初期位置に戻しています。
前ページ C言語とelで様々なゲームを作ろう TOP 次ページ
このエントリーをはてなブックマークに追加 そっか0