ポンクソフト

神経衰弱の作成 - C/C++言語とDXライブラリでゲーム作成入門

前ページ C/C++言語とDXライブラリでゲーム作成入門 TOP 次ページ

目次

  1. C/C++言語とDXライブラリでゲーム作成入門
  2. シューティングゲームの作成(チュートリアル)
  3. ドットイートゲームの作成(チュートリアル)
  4. 15パズルの作成
  5. 神経衰弱の作成
  6. オセロの作成
  7. オブジェクト指向を活用したシューティングゲーム

今回の目的

ソースファイル

今回の講座のソースを全て含んだプロジェクトファイル(Visual C++ 2010)を以下に置いておきます。
suijaku.zip

ゲームの説明

左クリックでカードをめくり数字が同じカードを当てて行きます。裏のカードが無くなればクリアです。
suijaku_img/game.png

パズル画像の作成

「suijaku」というプロジェクトを作成し、以下のような画像を作成し、プロジェクトフォルダに置きます。

カード画像

ひとつ40×60ピクセルで左が表、右が裏となるように横に並べ「card.png」という名前で保存します。
suijaku_img/card.png

マーク画像

ひとつ28×28ピクセルで左からスペード・ハート・ダイヤ・クラブの順に並べ「mark.png」という名前で保存します。
suijaku_img/mark.png

ナンバー画像

ひとつ28×28ピクセルで左からA,2,3,4,5,6,7,8,9,10,J,Q,Kの順に並べ「number.png」という名前で保存します。
suijaku_img/number.png

プログラムの入力・実行

プロジェクトに「suijaku.cpp」という名前の新規ソースファイルを追加し以下のプログラムを入力してください。
#include "DxLib.h"

// 場のカードデータ
struct {
  int mark;
  int number;
  bool reverse;
} cards[4][13];

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
  int turns = 0;
  int ox, oy, nx, ny;
  int remains = 52;
  int start_time, elapsed_time;
  bool mouse_flag = false;
  int graph_cards[2], graph_marks[4], graph_numbers[13];
  ChangeWindowMode(TRUE);
  DxLib_Init();
  SetDrawScreen(DX_SCREEN_BACK);
  LoadDivGraph("card.png", 2, 2, 1, 40, 64, graph_cards);
  LoadDivGraph("mark.png", 4, 4, 1, 28, 28, graph_marks);
  LoadDivGraph("number.png", 13, 13, 1, 28, 28, graph_numbers);
  // シャッフル
  bool deal[4][13] = {};
  for (int y = 0; y < 4; y++) for (int x = 0; x < 13; x++) {
    int mk, no;
    do {mk = GetRand(3); no = GetRand(12);} while (deal[mk][no]);
    cards[y][x].mark = mk;
    cards[y][x].number = no;
    cards[y][x].reverse = true;
    deal[mk][no] = true;
  }
  start_time = GetNowCount();
  while (!ProcessMessage()) {
    ClearDrawScreen();
    // カードめくり処理
    if (GetMouseInput() & MOUSE_INPUT_LEFT) {
      if (!mouse_flag) {
        mouse_flag = true;
        if (turns == 2) {
          cards[oy][ox].reverse = true;
          cards[ny][nx].reverse = true;
          turns = 0;
        } else {
          GetMousePoint(&nx, &ny);
          nx = (nx - 60) / 40;
          ny = (ny - 96) / 64;
          if (nx >= 0 && nx <= 12 && ny >= 0 && ny <= 3 && cards[ny][nx].reverse) {
            cards[ny][nx].reverse = false;
            if (turns == 0) {
              ox = nx; oy = ny;
              turns = 1;
            } else {
              if (cards[ny][nx].number == cards[oy][ox].number) {
                remains -= 2;
                turns = 0;
              } else turns = 2;
            }
          }
        }
      }
    } else mouse_flag = false;
    // 場のカード表示
    for (int y = 0; y < 4; y++) for (int x = 0; x < 13; x++) {
      int x2 = x * 40 + 60;
      int y2 = y * 64 + 96;
      DrawGraph(x2, y2, graph_cards[cards[y][x].reverse], FALSE);
      if (!cards[y][x].reverse) {
        DrawGraph(x2 + 6, y2 + 5, graph_marks[cards[y][x].mark], FALSE);
        DrawGraph(x2 + 6, y2 + 32, graph_numbers[cards[y][x].number], FALSE);
      }
    }
    // メッセージ表示
    int color = GetColor(255, 255, 255);
    if (remains <= 0) DrawString(8, 8, "クリア!", color);
    else elapsed_time = (GetNowCount() - start_time) / 1000;
    DrawFormatString(8, 450, color, "残り %d枚  経過時間 %d秒", remains, elapsed_time);
    ScreenFlip();
  }
  DxLib_End();
}
入力が完了したらメニューの「デバッグ」→「デバッグなしで開始」を選び、プログラムを実行します。

プログラムの解説

カードデータ構造体

struct {
  int mark;
  int number;
  bool reverse;
} cards[4][13];
52枚のカードデータです。場には以下のように配置されます。
cards[0][0]cards[0][1]・・・cards[0][11]cards[0][12]
cards[1][0]cards[1][1]・・・cards[1][11]cards[1][12]
cards[2][0]cards[2][1]・・・cards[2][11]cards[2][12]
cards[3][0]cards[3][1]・・・cards[3][11]cards[3][12]
構造体のメンバは以下のようになります。
markカードのマーク。0:スペード 1:ハート 2:ダイヤ 3:クラブ
numberカードのナンバー。0~12でA~K。
reverse裏返ったカードならばtrue

変数

  int turns = 0;
未確定のカードをめくった状態を表します。0ならめくっていない状態、1なら1枚めくった状態、2なら2枚めくった状態となります。既にペアになって確定したカードはカウントしません。
  int ox, oy, nx, ny;
未確定のカードを裏返したとき、(ox, oy)が1枚目、(nx, ny)が2枚目の座標になります。
  int remains = 52;
裏返っている残りカード数。
  int start_time, elapsed_time;
start_timeはゲーム開始時刻、elapsed_timeは経過時間になります。

シャッフル

  bool deal[4][13] = {};
  for (int y = 0; y < 4; y++) for (int x = 0; x < 13; x++) {
    int mk, no;
    do {mk = GetRand(3); no = GetRand(12);} while (deal[mk][no]);
    cards[y][x].mark = mk;
    cards[y][x].number = no;
    cards[y][x].reverse = true;
    deal[mk][no] = true;
  }
カードを場にランダムに配る部分です。deal配列は既に配ったカードのチェック用です。

メインループ

  start_time = GetNowCount();
まずメインループ開始前に、GetNowCount関数によってゲーム開始時刻をstart_timeに格納します。
        if (turns == 2) {
          cards[oy][ox].reverse = true;
          cards[ny][nx].reverse = true;
          turns = 0;
左クリックしたときに未確定のカードを2枚めくっていれば、それは外れなので裏返しにします。
          GetMousePoint(&nx, &ny);
          nx = (nx - 60) / 40;
          ny = (ny - 96) / 64;
          if (nx >= 0 && nx <= 12 && ny >= 0 && ny <= 3 && cards[ny][nx].reverse) {
            cards[ny][nx].reverse = false;
クリックした位置がカードの範囲内にあり、その場にあるカードが裏返しならば、めくる処理をします。
            if (turns == 0) {
              ox = nx; oy = ny;
              turns = 1;
            } else {
              if (cards[ny][nx].number == cards[oy][ox].number) {
                remains -= 2;
                turns = 0;
              } else turns = 2;
            }
カードを1枚めくった状態ならば(ox, oy)にめくったカードの座標を代入します。カードを2枚めくった状態ならばカードのナンバーを比較して、同じならば残りカード数を2枚減らします。
前ページ C/C++言語とDXライブラリでゲーム作成入門 TOP 次ページ
このエントリーをはてなブックマークに追加 そっか0