クラスメモ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を使う方法らしい。

でも静的なクラスの配列を作るのって使い道はあまりなさそう...