クラスメモ4-1

クラスを新しく組むときは、まず、コンストラクタ、デストラクタ、もっとも関係しそうなメンバ変数から先に与えていく。
組んだら、テストして動作するか確かめる。(特に,デストラクタのdelete忘れに気を配る.)
で、もう一段複雑にする(関数を入れたり、さらに必要なメンバ変数を入れたり…)

特に、あるクラスが別のクラスを参照したり、使っているような場合、いっぺんにやってしまうと、どれがどういう風に間違っているのかよくわからなくなる。(必ず、ちょっと組む-->動かしてみる-->ちょっと組む-->動かしてみる、の繰り返しをすること。)

//Image.h
#ifndef INCLUDED_IMAGE_H__
#define INCLUDED_IMAGE_H__

class Image {
public:
	Image(const char* filename);
	~Image();
	const int getWidth() const;
	const int getHeight() const;
	void draw(int srcX,int srcY,int dstX,int dstY,int width,int height); //ウィンドウに書きだす。
private:
	int mWidth;
	int mHeight;
	unsigned* mImageData;
};
#endif


//Image.cpp
#include "Image.h"
#include "File.h"

#include "GameLib/Framework.h"
using namespace GameLib;


Image::Image(const char* filename) {
	File imageFile(filename); //dds形式
	mHeight = imageFile.getUnsigned(8+4);
	mWidth = imageFile.getUnsigned(12+4);
	
	mImageData = new unsigned[mHeight*mWidth];
	for(int i=0;i<mHeight*mWidth;i++) {
		mImageData[i] = imageFile.getUnsigned(128+i*4); //128から画像開始
	}


}


Image::~Image() {
}

const int Image::getHeight() const {
	return mHeight;
}

const int Image::getWidth() const {
	return mWidth;
}

void Image::draw(int srcX,int srcY, int dstX, int dstY, int width, int height) {
	Framework f;
	unsigned* vram = f.instance().videoMemory();
	int windowWidth = f.width();
	for(int y=0; y<height; y++) {
		for(int x=0;x<width;x++) {
			unsigned& e = mImageData[(srcY+y)*mWidth + (x+srcX)];
			vram[(dstY+y) * windowWidth +(x+dstX)] = e;
		}
	}


}

Imageクラスは参考書のコードと似たり寄ったり。
あとはmaptipをウィンドウに描画するコードをMapクラス内にメンバ関数を置いて、書く。

void Map::drawMap(){
	for(int y=0;y<mHeight; y++) {
		for(int x=0; x<mWidth; x++) {
			int size = 16; //マップチップのサイズ
			drawCell(x,y,size); //16*16のマスを描画

		}
	}
}

void Map::drawCell(int x, int y, int size) {
	Object &tmp = mStageData(y,x);
//	if(tmp.checkFlag(Object::OBJ_WALL) ) {
//		mImage->draw(0,0,x*size,y*size,size,size);
//	}
//	if(tmp.checkFlag(Object::OBJ_FLOOR) ) {
//		mImage->draw(size*1,0,x*size,y*size,size,size);
//	}
	for(int i=0;i<2;i++) {
		if(tmp.checkFlag(1<<i) ) {
			mImage->draw(size*i,0,x*size,y*size,size,size);
		}
	}
}

汚いコードだと自分でも思うが、とにかく完成させることが今は大事だ。

                                                                                                                                                                                • -

つづいて、Playerを動かしたりしたいので、新たに関数を作る。

//Player.h
#ifndef INCLUDED_PLAYER_H__
#define INCLUDED_PLAYER_H__

#include <utility>
using namespace std;

class Image;
class Map;

//Playerはいつでも一人なので、シングルトンにする。
class Player {
public:
	static Player* instance();
	void draw();
	//staticメンバ関数はインスタンスが存在する前から呼び出せる。
	static void create(const char* filename, Map* map);
	static void destroy();

	void changeMap(Map* map); //マップ変更
private:
	int mCounter; //Playerが動いて見えるようにするためのカウンター
	static Player* mPlayer; //唯一のインスタンス

	Player(const char* filename, Map* map);

	////Playerのインスタンスを複製させないためにコピーコンストラクタを何もしない処理にする。
	Player( Player& );
	~Player();

	typedef pair<int,int> pii;
	pii mDir;
	Image* mImage; //これはnewする
	Map* mMap; //ポインタを替えたりするだけ。newしない。

};


#endif


//Player.cpp
#include <utility>
using namespace std;

#include "Map.h"
#include "Image.h"

#include "Player.h"

#include "GameLib/GameLib.h"
#include "GameLib/Framework.h"
using namespace GameLib;

//staticメンバ変数の実体化
Player* Player::mPlayer = 0; 

Player::Player(const char* filename, Map* map) : mMap(0), mImage(0) ,mCounter(0) {
	mMap = map;
	mImage = new Image(filename);
}

Player::~Player() {
	delete mImage;
	mImage = 0;
}


Player* Player::instance() {
	return mPlayer;
}

void Player::create(const char* filename, Map* map) {
	STRONG_ASSERT(!mPlayer && "Player::create() called twice!");

	//コンストラクタがよばれる
	mPlayer = new Player(filename, map);
}

void Player::destroy() {
	delete mPlayer;
	mPlayer = 0;
}

Playerは二つ存在することはまぁあり得ないので、(練習もかねて)シングルトンパターンで組んでみた。
(まだ,Player::draw関数は実装していないがそれは後で)
一番はまったのが、

namespace GameLib{
	void Framework::update(){
		Map map("mTen.txt");
		map.drawMap();
		//createは一回しか呼び出せない
		if( !Player::instance() ) {
			Player::create("player.dds", &map);
		}
		Player* p = Player::instance();

	}
}

と組むべきところをif文を除いて書いていたところだ。シングルトンパターンのクラスでは、最初にcreateを一回しか呼び出せない。
(当たり前)update()がループするのをすっかり忘れてしまい、間違いを特定するのにものすごく時間がかかった。
(まぁ、いいべんきょうになったんじゃないかな、と思う。)
コピーコンストラクタを明示的に書くのは、値渡しされない対策かな?

Map* mapを置くのはなんでかというと、Playerが動く際に動けない部分というのはあるはずだ。
具体的には床を進むことはできるが、壁の中を進むことや壁に入ることは普通はできない。ということで、
動けるかどうかを調べるためにmapを置いた。
(ちなみに、Map::Objectの中身に

bool mIsStep;

を追加した。文字通り、進めることが可能ならtrueを返すことにしている。

ポインタとしてのmapをメンバ変数に置くことで一応、マップの切り替えにも対応できる仕様にしている。
Playerが動けるまであともう少しかな。敵も(ランダムで)動かしてみたいな。

std::pairくらいなら自分で実装したほうが(stdを読み込まないで済むというメリットがあるので)いいような気がするが、せっかくだし。