DXライブラリとC言語でゲーム作成するその2。今回はcppファイルが肥大化する前に分割する方法について。DXライブラリというよりはVisual Studioでファイルを分割する方法になります。
ファイルを分割する意味
キー入力ができるようになったので次は画像や3Dモデルを表示させて動かして…といきたいところだがその前に意識しておきたいのがソースファイルの分割。
現在キー入力のみだけ書いているのでコードは短く済んでいるが、これからシーン管理やプレイヤー、敵、マップ、エフェクト、BGMなどいろんな処理を書いていくことになる。これらの処理を1つのファイルに全部書き込んでしまうとどこに何の機能があったかわかりにくくなってしまう。グローバル変数関連で思わぬバグを生み出してしまうことも。
ということでとある単位でファイルを分割することを意識しておきたい。今回は試しに前回書いたソースコードからゲームシステム部分・キー入力部分を別のソースファイルに分離してみる。またソースファイルを分割する際そのままだと他のソースファイルから関数にアクセスできないのでヘッダーファイルもセットで作成する。
Visual Studioでファイルを分割する
上のメニューバーから「プロジェクト」→「新しい項目の追加」を選択する。

C++ファイル(.cpp)とヘッダーファイル(.h)をそれぞれ選んで名前を入力して「追加」をクリックする。今回はSystem.cpp、System.h、Input.cpp、Input.hの4つを追加した。

追加したcppファイルにコードを移動・追加していく。hファイルにはcppファイルに書いた変数や関数を書いて定義して他のcppファイルからアクセスできるようにする。
最初に前回作成したメインファイルはこうなった。
#include <Windows.h> #include "System.h" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { //ゲームシステムのメイン関数を呼ぶ System_Main(); return 0; }
ゲームシステムのメイン関数を呼ぶだけのシンプルなものに。これぐらいならSystem.cpp/hに統合してもいいような気がするがDXライブラリ置き場のサンプルゲームのコードはこうなっていたので右に倣えということで。
System.cpp
#include "DxLib.h" #include "System.h" #include "Input.h" //DXライブラリなどの初期化 bool System_Intialize() { // ウィンドウモードに設定 ChangeWindowMode(TRUE); // DXライブラリ初期化処理 if (DxLib_Init() < 0) { //エラーなら終了する return false; } //描画先を裏画面にする SetDrawScreen(DX_SCREEN_BACK); // 画面モードの変更 SetGraphMode(WINDOWSIZE_X, WINDOWSIZE_Y, COLORBITNUM); return true; } //描画処理 void System_Draw() { DrawFormatString(0, 0, GetColor(255, 255, 255), "ESCキーで終了します"); } //終了処理 void System_Finalize() { DxLib_End(); // DXライブラリ終了処理 } //メインループ bool System_MainLoop() { while (1) { //裏画面を表画面に反映, メッセージ処理, 画面クリアの3つが正しく動作してるか確認 //どれか1つでも異常があれば強制終了 if (!(ScreenFlip() == 0)) return false; if (!(ProcessMessage() == 0)) return false; if (!(ClearDrawScreen() == 0)) return false; //キーボードの入力状態を更新 Input_UpdateKeyboard(); //テキストなどの描画 System_Draw(); //ESCキーが30フレーム以上押されていれば終了 //押された瞬間で判定する場合は if(Input_GetKeyboardDown()) //離された瞬間で判定する場合は if(Input_GetKeyboardUp()) とする if (Input_GetKeyboard(KEY_INPUT_ESCAPE) > 30) { break; } } return true; } //ソフトの処理を実行 bool System_Main() { //初期化 失敗(false)なら終了 if (!System_Intialize()) return false; //メインループ if (!System_MainLoop()) return false; //終了処理 System_Finalize(); //正常終了 return true; }
System.h
#ifndef DEF_SYSTEM_H #define DEF_SYSTEM_H #define WINDOWSIZE_X 1280 #define WINDOWSIZE_Y 720 #define COLORBITNUM 32 bool System_Intialize(); bool System_MainLoop(); void System_Draw(); void System_Finalize(); bool System_Main(); #endif
System.cpp/hはゲームのメイン処理を行う関数。大事なのはSystem_MainLoop()のwhile文内でここがゲームの中核となる。
Input.cpp
#include "DxLib.h" #include "Input.h" static int Input_Keyboard[256]; //キーが押されているフレーム数を格納する //キーボードの入力状態を更新する void Input_UpdateKeyboard() { int i; char tmpKey[256]; //現在のキーボードの入力状態を格納する // 全てのキーの入力状態を得る GetHitKeyStateAll(tmpKey); for (i = 0; i < 256; i++) { if (tmpKey[i] != 0) { // i番のキーコードに対応するキーが押されていたら Input_Keyboard[i]++; // 加算 } else { // 押されていなければ if (Input_Keyboard[i] > 0) { //前フレームで対応するキーが押されていれば Input_Keyboard[i] = -1; // -1にする。-1はキーが離された時用の値 } else { Input_Keyboard[i] = 0; // 0にする } } } } //指定のキーが押された瞬間か調べる bool Input_GetKeyboardDown(int KeyCode) { if (Input_Keyboard[KeyCode] == 1) return true; return false; } //指定のキーが押されているか調べる。返り値は押されているフレーム数 int Input_GetKeyboard(int KeyCode) { return Input_Keyboard[KeyCode]; } //指定のキーが離された瞬間か調べる bool Input_GetKeyboardUp(int KeyCode) { if (Input_Keyboard[KeyCode] == -1) return true; return false; }
Input.h
#ifndef DEF_INPUT_H #define DEF_INPUT_H void Input_UpdateKeyboard(); bool Input_GetKeyboardDown(int KeyCode); int Input_GetKeyboard(int KeyCode); bool Input_GetKeyboardUp(int KeyCode); #endif
Input.cppは前回のキーボード入力関連の部分を移動しただけ。なお変数Input_Keyboardは外部ファイルから値を書き変えられると困るのでこの変数にはstaticをつけた。
ヘッダーファイルについて、以下の部分は二重インクルードを防ぐ効果がある。ファイルの数が増えてくるとうっかり同じヘッダファイルをインクルードしてしまうことが多々あるのでこれを入れておくと事故を防げる。
#ifndef DEF_(大文字のヘッダファイル名)_H #define DEF_(大文字のヘッダファイル名)_H #endif
ヘッダーファイルを追加した際に最初にある#pragma onceも同じ効果がある。このままでもいいが他の環境で使えなくなる可能性があるのでこちらを推奨(特にチームで開発する場合は環境が異なるので)。
まとめ
要点をまとめると以下のようになる。
- cppファイルとヘッダファイルを追加する
- cppファイルに関数を書く
- ヘッダファイルに2で書いた関数のプロトタイプ宣言を書く(二重インクルード防止も忘れずに)
- 他のファイルに追加したヘッダファイルをインクルードする
- 関数を呼ぶ
ちなみに「ファイルを分ける単位」はキー入力やシーン管理、プレイヤー、敵、マップ、エフェクト、BGMといった具合に分けておくといいらしい。特にプレイヤーや敵、エフェクトなどゲーム中同時に複数存在するものは一括で処理できるようにしておくと便利。
参考にさせてもらったサイト:新ゲームプログラミングの館 複数のファイルに分けてコンパイルにする
コメント