C++でSTGを製作しているのですが、クラスで分からないことがあります

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
MNS

C++でSTGを製作しているのですが、クラスで分からないことがあります

#1

投稿記事 by MNS » 16年前

毎度、質問ばかりで申し訳ありません。

シューティングゲームをC++で製作しているのですが、
その際、この「龍神録プログラミングの館」を参考に、
csvファイルを読み込む関数を製作しています。
配列の苦い思い出もあり、できる限りSTLを使って製作しているので、
多少変更がありますが、基本的には同じです。

ただ、根本的に違うのが、キャラや弾を派生クラスとして作成して、
クラスの継承での多態性を利用して、ゲームを作ろうとしている点です。
たとえば、
class BaseVehicle
{
	float x,y;
	virtual void Move();
	void Draw();
}

class EnemyA :public BaseVehicle
{
	void Move();
}

class BulletA :public BaseVehicle
{
・・・・・
このような感じです。

問題なのは、読み込み用の関数なんです。
今、キャラ・弾・アイテムなどは、全て std::list で管理していますが、
多態性を実装するために、上にだした例に従うと、
std::list<BaseVehicle *> enemylist;
enemylist.push_back( new EnemyA() );
enemylist.push_back( new EnemyB() );
このように、派生クラスのオブジェクトを動的確保して渡しています。
csv形式のファイルには、座標・出現時間・速度などが記述されていますが、
敵の種類などを記述する手立てがありませんでした。(どの派生クラスのものか)
(要は、敵の情報はその敵に対応する派生クラスのオブジェクトに記録しなければいけないが、
その敵がどの派生クラスなのか分からないため、オブジェクトを作成できない)

いろいろと思考錯誤をしてみて考えた結果、std::mapを利用することを思いつきました。
たとえば、csvファイルに敵の種類を記述する列を設け(利用する派生クラス名)
std::map<std::string, BaseVehicle *> enemymap のようなstd::mapを作り、
そこに、make_pair("EnemyA", new EnemyA() ) のように、ペアを作成して、
読み込んだ文字列をfindで検索する、という形をとろうと思いました。
つまるところ、csvから読み込んだ、cx,cy,cstring(これが派生クラス名)のような変数を
std::map<std::string, BaseVehicle *>::iterator it;
it = enemymap.find(cstring);
(*it).second->x = cx;
(*it).second->y = cy;
enemylist.push_back( (*it).second );
このように、マップに登録されているオブジェクトそのものに値を代入し、
そのオブジェクトをstd::listへ登録する、というものです。

しかし、std::listは基底クラスのポインタなので、ポインタにstd::mapのオブジェクト
のアドレスを渡すことになり、結果的に最後に読み込んだ情報が全て上書きされてしまう、
ということを招いてしまいました(こう考えているんですが、もしかして間違ってますかね?)

私が知りたいのは、
・基底クラスを派生クラスにキャストする方法
・クラスの型が決まらない状態で、あとから決められる方法
・この問題を根本的に解決する手段
このどれかのことが分かれば、解決できると思っています(3番目は投げやりですが;)

ソースを添付しておきますので、
気が向きましたら、回答お願いします。

TOMONORI

Re:C++でSTGを製作しているのですが、クラスで分からないことがあります

#2

投稿記事 by TOMONORI » 16年前

"下方キャスト"、"ダウンキャスト"という
> ・基底クラスを派生クラスにキャストする方法 
があります。

が、これはコンパイラの庇護の外でのプログラミングですので
よくないとされています。あくまで参考までに。

class Base{/* ... */};
class SubA : public Base{/* ... */};

...

dynamic_cast<subA*>(*it)->(/*SubAのメンバ*/)

多態性のための継承は気をつけるべきことがたくさんあります。
有名なのは基底クラスに仮想デストラクタが必要、とか。
ピアソンの『Effective C++』などの参考書を読むと知りたいことがたくさん
書いてあると思いますよ。

あと、補足として(どちらもMNSさんが理解していることはわかっていますが)
> (*it).second
はit->secondですね?
それとclassの最後のセミコロンは忘れがちなので気をつけてください。

GPGA

Re:C++でSTGを製作しているのですが、クラスで分からないことがあります

#3

投稿記事 by GPGA » 16年前

敵の種類の識別をIDとし、csvファイルにそのIDを記述してある場合のサンプル
エラーチェックはなし。
class BaseVehicle {
public :
	virtual ~BaseVehicle(){}
	virtual bool Init(std::string s) = 0;
	virtual void Move() = 0;
};
class EnemyA : public BaseVehicle {
public :
	virtual bool Init(std::string s);
	virtual void Move();
};
class EnemyB : public BaseVehicle {
・・・

std::list<BaseVehicle *> enemylist;

// オブジェクト生成
bool Create(std::string objData) {
	// objDataにはcsvの内容の1行が入っているとする
	// またobjDataの最初の項目をIDとする

	// IDを取り出す
	char tmp[10] = { 0 };
	const char* p = objData.c_str();
	strncpy(tmp, p, strchr(p, ',') - p);
	int id = atoi(tmp);

	// ID対応するオブジェクトを生成する
	BaseVehicle* pBase;
	switch (id) {
		case 0 : pBase = new EnemyA();	break;
		case 1 : pBase = new EnemyB();	break;
		default : printf("該当なし\n");	return false;
	}

	// csvの内容をのものを初期化パラメータとして渡し、初期化は各クラスのInitメソッドにお任せする
	pBase->Init(objData);

	// リストに登録
	enemylist.push_back(pBase);

	return true;
}
 

MNS

Re:C++でSTGを製作しているのですが、クラスで分からないことがあります

#4

投稿記事 by MNS » 16年前

TOMONORI さん、 GPGA さん、回答ありがとうございます。
お二人方の回答を参考にして、無事実装することができました。
結局、GPGAさんの回答のように任意のIDを決め、対応するオブジェクトを生成することにしました。
クラスの最後の「;」については、完全なケアレスミスでしたね;
(*it). が it-> でも使える(こっちの方が良いのでしょうか?)とは知りませんでした。
ダウンキャストはいろんなところで役に立ちそうです。どうもありがとうございました。

Mikan

Re:C++でSTGを製作しているのですが、クラスで分からないことがあります

#5

投稿記事 by Mikan » 16年前

私のホームページで似たような作り方をしているSTGのソースファイル(作成途中w)を
公開しています。

参考にはならないかもしれませんが^^;
興味があったらごらんください♪

MNS

Re:C++でSTGを製作しているのですが、クラスで分からないことがあります

#6

投稿記事 by MNS » 16年前

C++で作ったSTGのソースファイルを見る機会はあまりないので、
このように、ソースを公開してくださるのはありがたいです!
量が多くて、読むのには時間がかかりそうですが、
似たような作りであることは、多いに参考になります。
mikanさん、どうもありがとうございます。

閉鎖

“C言語何でも質問掲示板” へ戻る