ここがC言語の交流サイトということでC++の技術であるクラスを遣うというのはあまり受け入れられないかもしれませんが、あえてそれでもクラスで作るのは、そっちのほうがあとあとずっと楽だという理由があるためです。
細かいことは一切説明せず、実践的なところのみに絞って解説をするので、クラスを敬遠してる人も是非がんばって読んでみてください。
では、今回変更したファイルです。
◆ファイル構成
/Project
Project_SH.sln …VC++の場合のみ
/codeフォルダ …ここにプログラムを入れる
Main.cpp …これがメインのファイル
GameFrame.cpp …今回作成するGameFrameファイル
GameFrame.h …GameFrameファイルのヘッダ
/includeフォルダ…ここにヘッダとかを入れる
Globalpack.h …主なヘッダファイル
/dataフォルダ …ここにデータを入れる
この3つです。
まずは、GameFrame.hというファイルから。
ここではGameFrameクラスを作ります。
GameFrameクラスは何をするものかというと、GameFrame、つまりゲームの枠のことです。
今回のシューティングに限らず、いろいろなゲームを作るときにはいつも初めは同じような処理を書かなければなりません。
しかし、GameFrameクラスを作ってすべてのゲームで必要な処理を書いてしまえば、他にも違うジャンルのゲームをDXライブラリで作るときに同じ処理なのでファイルをコピーするだけで済みます。
というわけで、GameFrameクラスを作って、そこに様々なジャンルで使う共通の処理を書いてしまうのです。
というわけで、早速解説にGO!↓
// -------------------------------------
// 全体のフレーム←親玉クラスになる
// GameFrame.h
#include "../include/Globalpack.h"
class GameFrame
{
private:
protected:
public:
int Key[ 256 ] ;
int Frame_ini() ;
void doMainLoop() {}
void doDrawObj() {}
void doObjInit() {}
void doKeyCheck() {}
};
解説をします。
1~2行目、および最終行の、 という部分が、クラスの宣言ということになります。
宣言のやり方は見たままで、class+[クラスの名前]という風に宣言します。
このとき、最後の}(中カッコ)のあとの;(セミコロン)を忘れやすいので注意してください!
また、その下の、 は、今はよく分からなくてもいいです。すこししたら分かるようになります。まぁ、おまじないのようなものと考えてください。
そして、それ以外の、
int Key[ 256 ] ;
int Frame_ini() ;
void doMainLoop() {}
void doDrawObj() {}
void doObjInit() {}
void doKeyCheck() {}
Key[ 256 ]という配列と、その他はすべて関数の宣言です。
そして、実際の関数の中身はGameFrame.cppに書いていくことになります。
// -------------------------------------
// 全体フレームの中身
// GameFrame.cpp
#include "GameFrame.h"
// フレーム全体の初期化
int GameFrame::Frame_ini()
{
// ウインドウモードに変更
if( ChangeWindowMode( TRUE ) != DX_CHANGESCREEN_OK ) return -1 ;
// 画面モードの変更
if( SetGraphMode( 640, 480, 32 ) != DX_CHANGESCREEN_OK ) return -1 ;
// タイトルを変更
if( SetMainWindowText( "Project_SH" ) != 0 ) return -1 ;
// ログの出力を行わない
if( SetOutApplicationLogValidFlag( FALSE ) != 0 ) return -1 ;
// DXライブラリ初期化処理
if( DxLib_Init() == -1 ) return -1;
// フォントの初期化
SetFontSize( 20 ) ;
ChangeFont( "メイリオ" ) ;
ChangeFontType( DX_FONTTYPE_ANTIALIASING ) ;
// 描画先画面を裏にする
SetDrawScreen( DX_SCREEN_BACK ) ;
return 0 ;
}
解説をします。
初めの部分の、 で、さっき作ったヘッダを読み込みます。
で、int Frame_ini()という関数を作るのですが、GameFrame::という余計なものが付いていますね?
これは、GameFrameクラスに属する関数ですよ、という印です。クラス名が変われば、もちろん違う名前にしなければなりません。とりあえずxxというクラスの関数を宣言するときは、
xx::[関数の名前]
という形になることが分かればおっけーです。
そして、Frame_iniという関数でゲームの初期化を行いました。
これは、この処理はほとんどすべてのゲームの初期化の際に必要になるので、ここに書いておくと便利だからです。
((他のゲームを作るときに丸々流用できることになる
そして、Frame_iniに書いた処理はMainから消すことになります。
また、このままでは初期化処理ができないので、Mainで初期化を行うことにします。
/*=============================================*
*
* Main.cpp
* provided by C++
*
*=============================================*/
#include "../include/Globalpack.h"
#include "GameFrame.h"
// ここでクラスの継承を行う
class MainGameFrame :public GameFrame
{};
// WinMain関数
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
// ここにメインの処理
MainGameFrame *mf = new MainGameFrame ;
mf->Frame_ini() ;
// いまはキーのウェイトでもかけておく
WaitKey() ;
// DXライブラリ使用の終了処理
DxLib_End() ;
// ソフトの終了
return 0 ;
}
変更したのはまず、以下のコードです。 これは、さっき作ったGameFrameを読み込まないと使えないので読み込みの処理です。
このとき、"GameFrame.cpp"は読み込まないということに注意してください。
ここは、クラスの継承といって、かなり特殊な処理になります。
クラスはそもそも、勝手に作ってきましたが、これは要は関数とか変数がどやどやと塊になってるものと考えてください。
それを使うのですが、GameFrameは、あらゆるゲームで使える処理を書くためのものです。
なので、シューティングでしか使えないけれどゲームの中枢にかかわることを書きたい、という時にはちょっと困ってしまいますね。
そんな時には、「クラスの継承」を行います。
クラスを継承すると、中身はほとんど同じクラスが生まれます。これはもとのクラスの子供みたいなものです。
これはもとのクラスの関数や変数を丸ごと受け継ぐので、宣言なしで元のクラスの関数を使ったりすることができます。
また、もとのクラスの関数を書き換えたり、あたらしい関数を追加したりできるので、非常に便利なのです!
というわけで、GameFrameクラスにはすべてのゲーム共通の処理しか書かないことにして、シューティングのための関数などは継承した子供のクラスで追加したりすることにします。
で、 というのは、:public xxxの部分が「xxxのクラスを継承します」という印になっています。つまり、「xxxのクラスを継承します」と言いながらあたらしいクラスを宣言すると、それで継承されるのです。
また、MainGameFrameはGameFrameを継承しているので、GameFrameのすべての変数&関数が宣言されていることになります。
// WinMain関数
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
// ここにメインの処理
MainGameFrame *mf = new MainGameFrame ;
mf->Frame_ini() ;
なんか難しそうですが、ぶっちゃけ、あんまり分からなくても問題ないです。
ただ、MainGameFrameクラスの変数を用意して、->で、それに属する関数を呼び出してるんだ、ということだけ理解してください。
つまり、
MainGameFrame *mf = new MainGameFrame ;
の、*mfが、MainGameFrameの変数で、*mfの中にはMainGameFrameの関数や変数(=GameFrameの関数や変数)が入っている、みたいに考えてください。
で、*mf->[関数名]で、MainGameFrameの関数にアクセスできる、ということです。
はい、お疲れ様でした。
((私もかなり疲れました
正直かなり分かりにくかったかと思います。説明下手ですいません。
クラスの概念や継承についてはググったほうがよい説明がいっぱい出てくるので、そっちを参照してください。
で、こんな面倒な処理をしておくのだから、あとで桃源郷が待っているということを意識してもらえればいいと思います。((言いすぎ
今回の実行結果は前回と全く同じなので面白くありませんが、
次回は本格的に、メインループを作って、せめてESCキーで画面が消えるようにしたいと思います。