シェルスクリプトマガジン

香川大学SLPからお届け!(Vol.61掲載)

著者:宇野 光純

 今回は、Windowsアプリケーションとして動く簡単な2Dゲームの開発を紹介します。汎用プログラミング言語の「C++」と、オープンソースのパソコンゲーム開発用ライブラリの「DXライブラリ」を組み合わせることで、時間と労力は必要ですが、Unityなどのゲームエンジンよりも自由度の高いゲーム開発ができます。

シェルスクリプトマガジン Vol.61は以下のリンク先でご購入できます。

図1 DXライブラリを使用する際の基本コード(Main.cpp)

#include "DxLib.h"
// プログラムはWinMainから開始
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow )
{
  // DXライブラリ初期化処理
  if( DxLib_Init() == -1 ) {
    return -1; // エラーが起きたら直ちに終了
  }
  // ウィンドウモードで起動、拡大して表示する
  ChangeWindowMode(TRUE);
  SetWindowSizeExtendRate(1.5);
  // ちらつきを消すために、描画先を裏画面にする
  SetDrawScreen(DX_SCREEN_BACK);
  while (ProcessMessage() == 0 && ScreenFlip() == 0 &&
         ClearDrawScreen() == 0) {
    // ここにゲームの処理を書く
  }
  DxLib_End(); // DXライブラリ使用の終了処理
  return 0; // ソフトの終了
}

図2 「Info.cpp」ファイルに記述するコード

#include "DxLib.h"
#include "Info.h"
int g_Width, g_Height; // ウィンドウの横幅と縦幅
int g_StageTime; // ステージ開始からの経過時間
static const int KEY_NUM = 256; // キー配列の最大値
char g_Buf[KEY_NUM]; // キーの状態を保持する配列

void InfoInitialize() { // 各データの初期化
  GetScreenState(&g_Width, &g_Height, NULL);
  g_StageTime = 0;
}
void InfoUpdate() { // 各データの更新
  g_StageTime++;
}
int GetWidth() { // ウィンドウの横幅を返す
  return g_Width;
}
int GetHeight() { // ウィンドウの縦幅を返す
  return g_Height;
}
int GetStageTime() { // ステージ開始からの経過時間を得る
  return g_StageTime;
}
void KeyUpdate() { // 全キーの状態を更新する
  GetHitKeyStateAll(g_Buf);
}
bool GetKey(int key_code) { // 指定したキーの状態を取得する
  if (g_Buf[key_code]) { return true; }
  return false;
}

図3 「Info.h」ファイルに記述するコード

#pragma once

void InfoInitialize();
void InfoUpdate();
int GetWidth();
int GetHeight();
int GetStageTime();
void KeyUpdate();
bool GetKey(int key_input);

図4 「Object.h」ファイルに記述するコード

#pragma once
class Object {
protected:
  int size;                // 画像サイズ
public:
  double x, y;             // X座標、Y座標
  virtual void Update() {} // 更新
  virtual void Draw() {}   // 描画
};

図5 「Player.cpp」ファイルに記述するコード

#include "DxLib.h"
#include "Player.h"
#include "Info.h"
#include <math.h>

int Player::image;
Player::Player() {
  x = y = 200.0; v = 4;    // 座標と速度の初期化
  SetImage();              // 画像関連の設定
}
void Player::Update() {
  Move();                 // 移動の更新
}
void Player::Draw() {      // 描画
  DrawGraph((int)(x - size / 2), (int)(y - size / 2), image, TRUE);
}
void Player::SetImage() {  // 画像関連の設定
  size = 64; image = LoadGraph("./images/player.png");
}
void Player::Move() {      // 自機操作
  double nxv = 0, nyv = 0; // 移動量保持用
  // X方向の移動量の調整
  if (GetKey(KEY_INPUT_LEFT)) { nxv -= v; }
  if (GetKey(KEY_INPUT_RIGHT)) { nxv += v; }
  // Y方向の移動量の調整
  if (GetKey(KEY_INPUT_UP)) { nyv -= v; }
  if (GetKey(KEY_INPUT_DOWN)) { nyv += v; }
  if (GetKey(KEY_INPUT_LSHIFT)) { nxv /= 2; nyv /= 2; }
  if (nxv != 0 && nyv != 0) { nxv /= sqrt(2); nyv /= sqrt(2); }
  x += nxv; y += nyv; // 移動量の加算
 // 移動範囲外に出たときは範囲内に戻す
  if (y < 0) { y = 0; }
  if (GetHeight() < y) { y = GetHeight(); }
  if (GetWidth() < x) { x = GetWidth(); }
  if (x < 0) { x = 0; }
}

図6 「Player.h」ファイルに記述するコード

#pragma once
#include "Object.h"

class Player : public Object {
private:
  static int image; // 画像ハンドル
  void SetImage();  // 画像関連の設定
  void Move();      // 自機の操作
public:
  double v;         // 移動速度
  Player();
  void Update();    // 更新
  void Draw();      // 描画
};

図7 「Enemy.cpp」ファイルに記述するコード

#include "DxLib.h"
#include "Enemy.h"
#include "Info.h"

int Enemy::image;
Enemy::Enemy() {
  x = GetWidth() / 2; y = 50.0; // X、Y座標の初期化
  xv = yv = 0.0;                // 増加量の初期化
  this->SetImage();             // 画像関連の設定
}
void Enemy::Update() {
  x += xv; y += yv;
}
void Enemy::Draw() {
  DrawGraph((int)(x - size / 2),
            (int)(y - size / 2), image, TRUE);
}
void Enemy::SetImage() {
  size = 64;
  image = LoadGraph("./images/enemy.png");
}

図8 「Enemy.h」ファイルに記述するコード

#pragma once
#include "Object.h"

class Enemy : public Object {
private:
  static int image; // 画像ハンドル
  void SetImage();  // 画像関連の設定
public:
  double xv, yv;    // X、Y 方向の増加量
  Enemy();
  void Update();    // 更新
  void Draw();      // 描画
};

図9 「MainScene.cpp」ファイルに記述するコード

#include "DxLib.h"
#include "MainScene.h"
#include "Info.h"

MainScene::MainScene() {
  // 背景を灰色に
  SetBackgroundColor(100, 100, 100);
  InfoInitialize(); // ウィンドウサイズなどの初期化
  player = Player(); // 自機の初期化
  enemy = Enemy(); // 敵機の初期化
}
void MainScene::Update() {
  InfoUpdate(); // 各データの更新
  player.Update(); // 自機の操作
  enemy.Update(); // 敵機の更新
}
void MainScene::Draw() {
  player.Draw(); enemy.Draw(); // 自機と敵機の描画
  // 経過時間の描画
  DrawFormatString(GetWidth() / 2 - 40, 0,
                   GetColor(255, 255, 255),
                   "Time : %d", GetStageTime());
}

図10 「MainScene.h」ファイルに 記述するコード

#pragma once
#include "Player.h"
#include "Enemy.h"

class MainScene {
private:
  Player player;
  Enemy enemy;
public:
  MainScene();
  void Update(); // 更新
  void Draw();   // 描画
};

図11 コードを追加した「Main.cpp」ファイルの内容

#include "DxLib.h"
#include "MainScene.h"
#include "Info.h"

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow )
{
  if( DxLib_Init() == -1 ) {
    return -1;
  }
  ChangeWindowMode(TRUE);
  SetWindowSizeExtendRate(1.5);
  SetDrawScreen(DX_SCREEN_BACK);
  // シーンの初期化
  MainScene ms = MainScene();
  while (ProcessMessage() == 0 && ScreenFlip() == 0 &&
         ClearDrawScreen() == 0) {
    KeyUpdate();
    // ゲーム終了
    if (GetKey(KEY_INPUT_ESCAPE)) { break;}
    ms.Update(); // 更新
    ms.Draw();   // 描画
  }
  DxLib_End();
  return 0;
}