クラスメモ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を読み込まないで済むというメリットがあるので)いいような気がするが、せっかくだし。