【DXライブラリ】シーン切替え(画面遷移)機能の実装

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言語で書くことを前提しているので割愛。

コメント

タイトルとURLをコピーしました