DXライブラリでゲームタイトルからゲーム本編、データ選択、オプション(コンフィグ)など画面(シーン)を切り替える機能の実装。Unityでいうシーンの切り替えを実装するお話。
画面(シーン)を切り替えたい
色んなジャンルのゲームはあれどゲームを起動したら普通はタイトル(メニュー)画面になって、そこからゲーム本編(もしくはセーブデータ選択)あるいはオプション(コンフィグ)を選択して画面が切り替わると思う。
2Dアクションゲームだとこんな感じが多いか(ロード画面は非同期処理の話なのでここでは省略)。

このシーン切り替えはUnityだと土台がすでにあってちょっとコードを追加するだけで簡単にできるのだが、DXライブラリだと土台から自前で作る必要がある。
シーン切り替え機能の基本
基本はシーンを保存する変数を用意してswitch文で切り替えていく(別にif-else文でもいい)。そして分岐先に各シーンの処理を書く。以下一番簡単な例。
static int Scene; //シーンを保存する変数。初期化を忘れずに
void Scene_Management(){
switch (Scene) {
case 0:
//ここに各シーンの処理を書く
break;
case 1:
//ここに各シーンの処理を書く
break;
case 2:
//ここに各シーンの処理を書く
break;
//中略
default:
break;
}
}
実際はシーン切り替えの際の初期化や終了処理の他にもシーンごとの更新や画像・文字などの描画の必要があるので上のコードのようにswitch文1つでやると無理がある。
なのでシーンの初期化・更新・描画・終了処理を行う関数をそれぞれ作って制御していく。
サンプルコード
とりあえず先にコード例をペタリ。こちらのサイトのコードを参考にしました(ほとんどコピペですが)。
今回は以下のcpp/hファイルを追加した。ファイルの追加方法についてはこちらで。
- SceneMgr.cpp/h (シーン切り替えを行う)
- Menu.cpp/h (メニューあるいはタイトル画面の処理)
- Game.cpp/h (ゲームのメイン処理)
- Config.cpp/h (設定画面の処理)
SceneMgr.cpp/h
SceneMgr.h
#ifndef DEF_SCENEMGR_H
#define DEF_SCENEMGR_H
typedef enum {
eScene_Menu, //メニュー画面
eScene_Game, //ゲーム画面
eScene_Config, //設定画面
eScene_None, //無し
} eScene;
void SceneMgr_Initialize();//初期化
void SceneMgr_Finalize();//終了処理
void SceneMgr_Update();//更新
void SceneMgr_Draw();//描画
// 引数 nextScene にシーンを変更する
void SceneMgr_ChangeScene(eScene nextScene);
#endif
enum(列挙体)を使って各画面に番号を割り当てている。上の例だと一番上から順に0、1、2、…となる。
SceneMgr.cpp
#include "DxLib.h"
#include "SceneMgr.h"
#include "Menu.h"
#include "Game.h"
#include "Config.h"
static eScene mScene = eScene_Menu; // 現シーンの管理変数
static eScene mNextScene = eScene_None; //次のシーン管理変数
static void SceneMgr_InitializeModule(eScene scene);//指定モジュールを初期化する
static void SceneMgr_FinalizeModule(eScene scene);//指定モジュールの終了処理を行う
//初期化
void SceneMgr_Initialize() {
SceneMgr_InitializeModule(mScene);
}
//シーンの更新
void SceneMgr_Update() {
if (mNextScene != eScene_None) { //次のシーンがセットされていたら
SceneMgr_FinalizeModule(mScene); //現在のシーンの終了処理を実行
mScene = mNextScene; //次のシーンを現在のシーンセット
mNextScene = eScene_None; //次のシーン情報をクリア
SceneMgr_InitializeModule(mScene); //現在のシーンを初期化
}
//現在のシーンによって処理を分岐
switch (mScene) {
case eScene_Menu:
Menu_Update(); //メニュー画面の更新処理をする
break;
case eScene_Game:
Game_Update();
break;
case eScene_Config:
Config_Update();
break;
default:
break;
}
}
//各シーンの描画
void SceneMgr_Draw() {
//現在のシーンによって処理を分岐
switch (mScene) {
case eScene_Menu:
Menu_Draw(); //メニュー画面の更新処理をする
break;
case eScene_Game:
Game_Draw();
break;
case eScene_Config:
Config_Draw();
break;
default:
break;
}
}
//引数 nextScene のシーンを変更する
void SceneMgr_ChangeScene(eScene NextScene) {
mNextScene = NextScene; //次のシーンをセットする
}
//終了処理
void SceneMgr_Finalize() {
SceneMgr_FinalizeModule(mScene);
}
// 引数sceneモジュールを初期化する
static void SceneMgr_InitializeModule(eScene scene) {
switch (scene) { //シーンによって処理を分岐
case eScene_Menu: //指定画面がメニュー画面なら
Menu_Initialize(); //メニュー画面の初期化処理をする
break;//以下略
case eScene_Game:
Game_Initialize();
break;
case eScene_Config:
Config_Initialize();
break;
}
}
// 引数sceneモジュールの終了処理を行う
static void SceneMgr_FinalizeModule(eScene scene) {
switch (scene) { //シーンによって処理を分岐
case eScene_Menu: //指定画面がメニュー画面なら
Menu_Finalize(); //メニュー画面の終了処理処理をする
break;//以下略
case eScene_Game:
Game_Finalize();
break;
case eScene_Config:
Config_Finalize();
break;
}
}
シーンの初期化・更新・描画・終了処理を行う関数をそれぞれ作り、初期化以外の関数でswitch文を使って各シーンごとに処理を行う。
各シーンの処理につなげるヘッダーファイルの追加も忘れずに。
Menu.cpp/h
Menu.h
#ifndef DEF_MENU_H #define DEF_MENU_H void Menu_Initialize();//初期化 void Menu_Finalize();//終了処理 void Menu_Update();//更新 void Menu_Draw();//描画 #endif
Menu.cpp
#include "Menu.h"
#include "SceneMgr.h"
#include "DxLib.h"
#include "Input.h"
const static int MENU_X = 32;
const static int MENU_Y = 32;
//初期化
void Menu_Initialize() {
}
//更新
void Menu_Update() {
if (Input_GetKeyboardDown(KEY_INPUT_G)) {//Gキーが押されていたら
SceneMgr_ChangeScene(eScene_Game);//シーンをゲーム画面に変更
}
if (Input_GetKeyboardDown(KEY_INPUT_C)) {//Cキーが押されていたら
SceneMgr_ChangeScene(eScene_Config);//シーンを設定画面に変更
}
}
//描画
void Menu_Draw() {
DrawString(MENU_X, MENU_Y, "メニュー画面です。", GetColor(255, 255, 255));
DrawString(MENU_X, MENU_Y + 20, "Gキーを押すとゲーム画面に進みます。", GetColor(255, 255, 255));
DrawString(MENU_X, MENU_Y + 40, "Cキーを押すと 設定画面に進みます。", GetColor(255, 255, 255));
}
//終了処理
void Menu_Finalize() {
}
メニュー(タイトル)画面の処理。こちらも初期化・更新・描画・終了処理を行う関数をそれぞれ作成している。
Game.cpp/h
Game.h
#ifndef DEF_GAME_H #define DEF_GAME_H void Game_Initialize();//初期化 void Game_Finalize();//終了処理 void Game_Update();//更新 void Game_Draw();//描画 #endif
Game.cpp
#include "Game.h"
#include "SceneMgr.h"
#include "DxLib.h"
#include "Input.h"
const static int GAME_X = 32;
const static int GAME_Y = 32;
//初期化
void Game_Initialize() {
}
//更新
void Game_Update() {
if (Input_GetKeyboardDown(KEY_INPUT_M)) { //Mキーが押されていたら
SceneMgr_ChangeScene(eScene_Menu);//シーンをメニューに変更
}
}
//描画
void Game_Draw() {
DrawString(GAME_X, GAME_Y, "ゲーム画面です。", GetColor(255, 255, 255));
DrawString(GAME_X, GAME_Y + 20, "Mキーを押すとメニュー画面に戻ります。", GetColor(255, 255, 255));
}
//終了処理
void Game_Finalize() {
}
ゲームのメイン処理を行う部分。こちらも初期化・更新・描画・終了処理を行う関数をそれぞれ作成している。
Config.cpp/h
Config.h
#ifndef DEF_CONFIG_H #define DEF_CONFIG_H void Config_Initialize();//初期化 void Config_Finalize();//終了処理 void Config_Update();//更新 void Config_Draw();//描画 #endif
Config.cpp
#include "Config.h"
#include "SceneMgr.h"
#include "DxLib.h"
#include "Input.h"
const static int CONFIG_X = 32;
const static int CONFIG_Y = 32;
//初期化
void Config_Initialize() {
}
//更新
void Config_Update() {
if (Input_GetKeyboardDown(KEY_INPUT_M)) { //Mキーが押されていたら
SceneMgr_ChangeScene(eScene_Menu);//シーンをメニューに変更
}
}
//描画
void Config_Draw() {
DrawString(CONFIG_X, CONFIG_Y, "設定画面です。", GetColor(255, 255, 255));
DrawString(CONFIG_X, CONFIG_Y + 20, "Mキーを押すとメニュー画面に戻ります。", GetColor(255, 255, 255));
}
//終了処理
void Config_Finalize() {
}
キーコンフィグや音量調整などを行う部分。上と同じく初期化・更新・描画・終了処理を行う関数をそれぞれ作成している。
System.cpp
上の追加が終わったらSystem.cppにコードを追加してシーン切り替えができるようにする。
// 略
#include "SceneMgr.h" // 追加部分
//DXライブラリなどの初期化
bool System_Intialize() {
// 略
//シーンの初期化(追加部分)
SceneMgr_Initialize();
// 略
}
//略
//終了処理
void System_Finalize() {
//シーンの終了処理(追加部分)
SceneMgr_Finalize();
// 略
}
//メインループ
bool System_MainLoop() {
while (1) {
//略
//シーンの更新(追加部分。エラー処理・フレームレート制御の更新後に)
SceneMgr_Update();
//略
}
return true;
}
//略
補足
今回追加したソースファイルに共通して書いた
- 初期化を行う ***_Initialize()
- 更新を行う ***_Update()
- 描画を行う ***_Draw()
- 終了処理を行う ***_Finalize()
これらの関数は他のソースファイルを書く時(例えばプレイヤーや敵関連の処理をするソースファイル)も必要となる関数なので忘れないように。
ちなみにC++だとクラスを使うことでswitch文のところをもっとスマートに書けます。が、ここではC言語で書くことを前提しているので割愛。

コメント