最初に前回と変更した所があります。
assert関数を使っていましたが、初心者の方にassertした処が分かり辛いって理由から
オリジナルのマクロをご用意しました。これはmain.hに追加します。
[main.h]
//----------------------------------------------------------------------
// デバッグ用のマクロ
//----------------------------------------------------------------------
// assert()の代わり。これの方が問題があった場所が分かりやいので。
#define MACRO_ASSERT(cond) { if( !(cond) ) DebugBreak(); }
#include も不要なので削除してください。
comsub.cppに4箇所。
stateMng.cppに4箇所。
の置き換えが必要です。
それでは、プログラム自体の説明に入ります。
まず、
[main.h]
に次の定数を追加してください。
●定数
// キャラクタの向き
enum CharacterMuki_t {
CHAR_MUKI_RIGHT, //右向き
CHAR_MUKI_UP, //上向き
CHAR_MUKI_DOWN, //下向き
CHAR_MUKI_LEFT, //左向き
};
// キャラクタのピクセルサイズ
#define CHAR_PIXEL_SIZEX 32
#define CHAR_PIXEL_SIZEY 32
では、本格的に始めましょう。
今回から実際のゲーム画面の表示が出来るようになります。
ただ、ちゃんとゲームにするためには後の事を考えて色々手続きをしてやる必要があります。
なので、まず下地を整えてその後にゲーム処理を構築していく事とします。
追加変更するのは次のモジュール(機能単位の部品)ファイルです。
・キャラクタの管理・表示 char.cpp/h
・マップの管理・表示 map.cpp/h
・マップの定義 data.cpp/h
・主人公の移動の制御 mapMove.cpp/h
・そしてメイン処理の変更 gameMain.cpp
この順番で解説していきますね。
それでは、キャラクタの管理・表示から解説していきます。
ここも状態管理と同様にオブジェクトと言う概念でキャラクタのデータを管理します。
今回の場合はキャラクタ画像は、次のような並びの前提になっています。 [右0][右1][右2]
[上0][上1][上2]
[下0][下1][下2]
[左0][左1][左2]
全部をこの並びに統一することで処理の共通化が可能になります。
ここの仕様の統一は、プログラマさんと絵を描く人の意思疎通が大事なポイントです。
覚えておいてください。
ここを適当にすると後々面倒というか、絵を描く人が修正するかプログラムがキャラ
ごとに違う並びを処理すると言う、どちらかが負担を背負うことになります。
バグを防ぐ意味でも行き当たりばったりを避けましょう。
[char.h]
特にヘッダは細かく解説しませんが、CHAR_OBJECTがキャラクタの管理オブジェクトです。
#ifndef INCLUDE_CHAR_H
#define INCLUDE_CHAR_H
//----------------------------------------------------------------------
// 構造体
//----------------------------------------------------------------------
// キャラ管理オブジェクト
typedef struct tag_CharObject *CHAR_OBJECT;
//----------------------------------------------------------------------
// 関数の宣言
//----------------------------------------------------------------------
// キャラクタのロード。キャラ管理オブジェクトを返す。
extern CHAR_OBJECT char_Load(char *FileName,int xnum,int ynum);
// キャラクタとキャラ管理オブジェクトの破棄
extern void char_Delete(CHAR_OBJECT CharObj);
// キャラクタの表示。座標は画面内のピクセル位置です。
extern void char_Draw(CHAR_OBJECT CharObj,int px,int py,int muki,int animPtn);
#endif /*INCLUDE_CHAR_H*/
[char.cpp]
●定数
まず先頭の部分で、キャラクタのアニメーションパターンを定義しています。
パターンは、停止(1)、右足(0)、停止(1)、左足(2)で一巡するので1,0,1,2の
4パターンとs_AnimeTableで定義します。
#include
#include "main.h"
#include "char.h"
//----------------------------------------------------------------------
// 定数
//----------------------------------------------------------------------
// アニメーションのパターン数
#define CHAR_ANIM_PATTERN 4
// アニメーションパターンのテーブル。
static const int s_AnimeTable[CHAR_ANIM_PATTERN] = {1,0,1,2};
次はキャラ管理オブジェクトの構造体です。
キャラクタの縦横パーツ数やアニメパターン向きを保持します。
imagesでは配列にxnum*ynumパターンをイメージを保持します。
//----------------------------------------------------------------------
// 構造体
//----------------------------------------------------------------------
// キャラ管理オブジェクトの構造体
struct tag_CharObject {
int xnum; //横方向の数。
int ynum; //縦方向の数。
int animPtn; //アニメーションのパターン
int muki; //向き
int *images; //プレーヤーキャラクタのイメージ配列
};
キャラクタをロードして、キャラ管理オブジェクトを返します。
状態管理と違って多用しますので、今度こそオブジェクトの本領発揮です。
メモリの確保にはmallocを使っています。
まず、キャラ管理オブジェクトのメモリを確保して、更にイメージ配列の
メモリを確保します。
そのイメージ配列にLoadDivGraphでグラフィックを分割ロードする仕組みに
なっています。このchar_Loadの部分は結構汎用的ですが、後で出てくる
char_Draw関数は用途が限定されます。
//----------------------------------------------------------------------
// キャラクタのロード。キャラ管理オブジェクトを返す。
//----------------------------------------------------------------------
CHAR_OBJECT char_Load(char *FileName,int xnum,int ynum)
{
CHAR_OBJECT CharObj = NULL;
MACRO_ASSERT(xnum>0);
MACRO_ASSERT(ynum>0);
// まずキャラ管理オブジェクトのメモリを確保する。
CharObj = (CHAR_OBJECT)malloc( sizeof(struct tag_CharObject) );
MACRO_ASSERT( CharObj!=NULL );
// キャラ管理オブジェクトの値を初期化する。
CharObj->xnum = xnum; //横方向の数。アニメのパターン数
CharObj->ynum = ynum; //縦方向の数。向きのパターン数
CharObj->animPtn = 0; //アニメーションのパターン
CharObj->muki = CHAR_MUKI_RIGHT; //向き
CharObj->images = NULL; //プレーヤーキャラクタのイメージ配列
// パターン分のイメージ配列を確保する。
CharObj->images = (int*)malloc( sizeof(int) * (xnum*ynum) );
MACRO_ASSERT( CharObj->images!=NULL );
// 画像ファイルを分割ロードする。
int Ret = LoadDivGraph(FileName,(xnum*ynum),xnum,ynum,
CHAR_PIXEL_SIZEX,CHAR_PIXEL_SIZEY, CharObj->images );
MACRO_ASSERT( Ret==0);
// キャラ管理オブジェクトを返す。
return CharObj;
}
キャラ管理オブジェクトを破棄するための関数です。
ちゃんと破棄しないとメモリリーク(破棄し忘れたメモリが溜まっていくこと)
しちゃいますので、破棄を忘れないでください。
破棄の順番は、ロード時の逆でキャラクタのイメージ(分割ロードしたので一個づつ)と
イメージ配列、オブジェクト自体の順番です。
//----------------------------------------------------------------------
// キャラクタとキャラ管理オブジェクトの破棄
//----------------------------------------------------------------------
void char_Delete(CHAR_OBJECT CharObj)
{
if( CharObj!=NULL ) {
// キャラクタのグラフィック破棄
for( int gn=0 ; gnxnum*CharObj->ynum) ; gn++ ) {
DeleteGraph( CharObj->images[gn] );
}
// パターン分のイメージ配列をを破棄
if( CharObj->images!=NULL ) {
free( CharObj->images );
}
// オブジェクト自体を破棄
free( CharObj );
}
}
この関数は指定した座標に指定した向きで、指定したアニメパターンで表示します。
アニメパターンがキャラのパターン数を超えたいたら超えないようにCHAR_ANIM_PATTERN
で循環するようにしています。
//----------------------------------------------------------------------
// キャラクタの表示。座標は画面内のピクセル位置です。
//----------------------------------------------------------------------
void char_Draw(CHAR_OBJECT CharObj,int px,int py,int muki,int animPtn)
{
MACRO_ASSERT( CharObj!=NULL );
MACRO_ASSERT( muki>=0 );
MACRO_ASSERT( CharObj->ynum>muki );
// 向きの設定
CharObj->muki = muki;
// アニメーション。パターン数の余りを使用する。
CharObj->animPtn = animPtn % CHAR_ANIM_PATTERN;
// 向きとアニメパターンから表示するイメージの番号を決めて表示する。
int gn = CharObj->muki*CharObj->xnum + s_AnimeTable[CharObj->animPtn];
DrawGraph( px , py , CharObj->images[gn] , TRUE ) ;
}
次は、マップの管理・表示ですが、複雑なので2分割するかも知れません。