クラスメモPart3-3
Mapのstructをclassに替えたもの。
Array2D<Object> mStageData;
の配列がsetSizeで動的に生成されるが、この時もコンストラクタは呼ばれている。Objectクラスのコンストラクタは
mFlagを0に初期化するようにしたので、邪魔くさいZeroClearは呼び出す必要がなくなった。
また、cpp側に書けるようになったので、使いやすくなった。
ファイルの形は.txtで
####### # # # ### # # # ### # # # # #######
という形で実験してみた。最後にEnterキー(\r\n)を押しても押さなくても読み込めるようにすべきだったが
めんどくさくてやってない。
//Map.h #ifndef INCLUDED_MAP_H__ #define INCLUDED_MAP_H__ #include "Array2D.h" //宣言だけではだめで、インクルードする必要がある。 class Map { public: Map(const char* filename); ~Map(); int getHeight() const; int getWidth() const; void Show(); private: class Object; void zeroClear(); int mHeight; int mWidth; Array2D<Object> mStageData; }; #endif //Map.cpp #include "Map.h" #include "File.h" #include "GameLib/Framework.h" //coutも using namespace GameLib; /*-----------------------*/ //一番最初に定義書く。 class Map::Object { public: Object(); enum Obj{ OBJ_WALL = 0, OBJ_FLOOR = (1<<0), OBJ_ITEM = (1<<1), OBJ_UNKNOWN = (1<<3), }; unsigned mFlags; //引数をObj型にしないのは、(OBJ_WALL | OBJ_ITEMのような、複数のflagを引数にしたいときに対応するため。 bool checkFlag(unsigned o); void setFlag(unsigned o); void resetFlag(unsigned o); void switchFlag(unsigned o); //オフならオン、オンならオフ }; /*----------------------*/ namespace { //Mapコンストラクタ用 unsigned GetMapSize(const char* stageData) { int x=0; int y=0; int tmp =0; for(int i=0; stageData[i] != '\0' ;i++) { char ch = stageData[i]; if(ch == '\n') { x = (tmp>x) ? tmp : x; y++; tmp =0; } else if(ch >= ' ' && ch <= '~') { tmp++; } } return ((x<<16) | y); } } Map::Map(const char* filename) { File file(filename); unsigned siz = GetMapSize(file.getData()); mWidth = (siz >> 16); mHeight = (siz & 0x0000ffff); mStageData.setSize(mHeight,mWidth); //zeroClear(); //mStageDataのflagをすべて0に。クラスのコンストラクタで呼び出されるのでいらない //dataの解析 //ここも無名名前空間の中に入れればいいかも。 const char* str = file.getData(); int x=0; int y=0; for(int i=0;str[i] != '\0';i++) { Object o; o.mFlags = Object::OBJ_UNKNOWN; switch(str[i]) { case '#': o.mFlags = Object::OBJ_WALL; break; case ' ' : o.mFlags = Object::OBJ_FLOOR; break; default: break; } if(o.mFlags != Object::OBJ_UNKNOWN) { mStageData(y,x).mFlags = o.mFlags; x++; } else if(str[i] == '\n') { y++; x=0; } } } //いらん void Map::zeroClear() { for(int i=0;i<mHeight;i++) { for(int j=0;j<mWidth;j++) { mStageData(i,j).mFlags = Object::OBJ_WALL; //定義していなければすべて壁とみなす。 } } } Map::~Map() { } int Map::getHeight() const { return mHeight; } int Map::getWidth() const { return mWidth; } /*--------------------*/ void Map::Show() { int h=getHeight(); int w = getWidth(); for(int i=0;i<h;i++) { for(int j=0;j<w;j++) { cout << mStageData(i,j).mFlags; } cout << endl; } } /*------------------------- Object型 -----------------------*/ Map::Object::Object() : mFlags(0) { } bool Map::Object::checkFlag(unsigned o) { if(mFlags&o) { return true; } return false; } void Map::Object::setFlag(unsigned objs) { mFlags |= objs; } void Map::Object::resetFlag(unsigned objs) { mFlags = mFlags&(~objs); } void Map::Object::switchFlag(unsigned objs) { //例:mFlags=111000,objs=001100のとき、110100となり所要のflagが得られる。 mFlags ^= objs; //eXclusive OR }
フラグ操作の勉強になりました!XORなんて初めて使った。
とはいえ、クラスを配列のtypeにするのは、速度的に心配ではある。大丈夫なんだろうか?
(どうしても気になるなら、Objectクラスの変数だけstructとして外に出しちゃっていじくるのもいいかもしれないけど…
あと、Mapクラスのprivateないにさらにclassを置いている。いまのところは大丈夫だが、いずれ、publicに置くかもしれない。
ところで静的なクラスの配列を作るにはどうすればよいのかな?
//例 //test.h #include "GameLib/Framework.h" using namespace GameLib; class Test { public: Test(); Test(int num); void Show(); private: int a; }; Test::Test() :a(0){ } Test::Test(int num) :a(num) { } void Test::Show() { cout << a << endl; } //main.cpp #include "GameLib/Framework.h" #include "test.h" using namespace GameLib; namespace GameLib{ void Framework::update(){ // Map test("mTen.txt"); // test.Show(); Test samp[5] = { Test(1),Test(2),Test(3),Test(4),Test(5) }; samp[4].Show(); //しっかり4が呼び出される。 Test samp2[5]; //この場合、デフォルトコンストラクタか、自作の引数なしコンストラクタが呼び出される。 samp2[3].Show(); //しっかり0が呼び出される。 } }
それぞれの要素でコンストラクタが呼ばれている。あとはvector使うとか、placement new使うとかの方法があるらしい。
一番いいのが、stl::vectorを使う方法らしい。
でも静的なクラスの配列を作るのって使い道はあまりなさそう...