C++のクラスについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
またみふ
記事: 21
登録日時: 9年前

C++のクラスについて

#1

投稿記事 by またみふ » 9年前

こんにちは、いつもお世話になっています、またみふです。

今RPGを作成しています。日記ではRPGをC言語で作ると言ってましたが、
「新・ゲームプログラミングの館」でC++のファイル分割などが載っていましたので、C++で作ることにしました。(変わりやすい性格なんです・・。)

そこでクラスについて質問があります。
例えば

プレイヤークラス、キーボードクラスがあります
中身はこんな感じです

コード:

//プレイヤークラス
class Player{
private:
	
	enum MUKI{
	DOWN,
	LEFT,
	RIGHT,
	UP
	};

	MUKI muki;

	int img[12];
	int GraphX;//描画用X座標
	int GraphY;//描画用Y座標
	int MapX;//マップX座標
	int MapY;//マップY座標
	int HitX;//当たり判定用X座標
	int HitY;//当たり判定用Y座標
	int DivX;//画像のxサイズ
	int DivY;//画像のyサイズ
	int walkFlag;//歩けるかどうかの判定
public:
	void Player_Init(int x,int y);//初期化関数
	void Player_Calc();//計算関数
	void Player_Graph();//描画関数
	void Player_End();//終了関数
};

コード:

//キーボードクラス
class Keyboard{
private:
	int key[256];
public:
	void Key_Update();
	int Key_Get(int KeyCode);
};
こんな感じのクラスがあったとします。
自分は、プレイヤークラスのメンバ関数のvoid Player_Calc()の中に
キーボードクラスのメンバ関数を使いたいんです。
C言語だったらそのまま関数を呼べると思うんですが、C++だとオブジェトを作成してからそこからメンバ関数を呼べなくちゃいけないと思います。
最初はキーボードクラスをプレイヤークラスで継承すればいいんじゃないかな?と思いましたが、
これだと自分も訳が分からなくなり、これから処理が増えるとともになおさら意味不明になると思い、やめました。

そこで、自分はPlayer.cpp(メンバ関数を定義してるところです)にキーボードクラスのオブジェクトを作成し、
そして、main.cppにプレイヤークラスのオブジェクトを作成しました。

しかし、なんかいや~な感じがします。

キーボードクラスのKet_Get()関数はいっぱい使うと思うのに、いちいちオブジェクトを作成するのもなんか変だし
(たとえば・・タイトルの選択肢処理のときキーボードを使うと思うんですが、Player.cppにオブジェクトがあるのにいちいちTitle.cppにキーボードクラスのオブジェクトを作成するということです)
main.cppにプレイヤークラスのオブジェクトを作成するのもなんか変な感じがします。
すいません、わかりませんよね・・・。

そこで聞きたいことがあります。
①みなさんは、オブジェクトの管理をどうしていますか。
②↑のとは関係ないですが、どこからクラス化したほうがいいのか教えて下さい。(例えば描画処理ならマップの描画などもまとめて描画クラスにいれたほうがいいのか、
もしくはマップ描画というクラスを作ったほうがいいのか)
③ゲームのクラス管理のコツなどを教えてください(こうやってやったほうがわかりやすいよなど・・・)
④ゲームのクラス管理に詳しいサイトさんなどを教えてください。

質問多くてすいません・・・。どうかお願いしますm(__)m

※一応ソースを貼ります。
添付ファイル
rpg.zip
(21.45 MiB) ダウンロード数: 90 回

アバター
h2so5
副管理人
記事: 2212
登録日時: 9年前
住所: 東京
連絡を取る:

Re: C++のクラスについて

#2

投稿記事 by h2so5 » 9年前

勧めていいものか分かりませんが、
私はキー入力の管理にモノステートパターンを使用しています。

コード:

//キーボードクラス
namespace Keyboard{
    namespace{
        int key[256];
    }
    void Key_Update();
    int Key_Get(int KeyCode);
}
このようにすると、どこからでも
Keyboard::Key_Get();
のように関数を呼ぶことができ、インスタンスを作る必要がなくなります。

その他にもシングルトンパターンを使う方法もあります。

アバター
a5ua
記事: 199
登録日時: 10年前

Re: C++のクラスについて

#3

投稿記事 by a5ua » 9年前

いくつか設計案を示します。

案1:キーボード情報をコンストラクタで渡しておく
Playerクラスは、メンバー関数内で、キーボードを情報を使用できる

コード:

class Player
{
public:
	Player(Keyboard &keyboard) : key(keyboard){}

private:
	Keyboard &key;
};
案2:プレイヤー情報更新関数にキーボード情報を渡す。
Playerクラスは、Player_Calcでのみ、キーボード情報を使用できる。

コード:

class Player
{
public:
	void Player_Calc(Keyboard &key);
};
案3:プレイヤー情報更新関数を分割し、
どのキーによってどの行動をするかは、Playerクラスとは別の場所で処理する

コード:

class Player
{
public:
	void move(int x, int y);	// 移動する
	void attack();				// 攻撃する
};

// キー入力を行動に反映する
void PlayerAction(Keyboard &key, Player &player)
{
	if (key.Key_Get(/* 略 */)) {
		player.attack();
	}
	if (key.Key_Get(/* 略 */)) {
		player.move(4, -4);
	}
}
案4:Keyboardクラスをシングルトンにする
Keyboardクラスのオブジェクトを引数で渡して回す必要がなくなる。


設計に絶対の正解はないと思いますが、よいクラス設計をするには、経験が必要だと思います。
一度、ゲームを完成させた後、設計を見直してみて、良くないと思うところを見つけて、
反省点を踏まえて、何度も、作り直すことで、よりよい設計が生まれてくると思います。

アバター
lriki
記事: 88
登録日時: 9年前

Re: C++のクラスについて

#4

投稿記事 by lriki » 9年前

キーボードは普通1つしかないはずなので、
それを管理するクラスはシングルトン(またはモノステート)で実装した方が混乱はないと思います。



ゲームのクラス管理についてですが、
RPGツクールVXのスクリプトがオブジェクト指向の言語(Ruby)で書かれているので、
これが参考になると思います。

難しいフレームワークとか使ってるわけじゃないので、それなりに読んでいけると思います。
コメント読むだけでも結構イメージできますし。

体験版では編集はできませんが、ソースを見ることはできるので
興味があったら一度目を通してみてください。
ため息が出るほどいい感じにオブジェクト指向をゲームに生かしています。

UN
記事: 18
登録日時: 9年前
住所: 神奈川県

Re: C++のクラスについて

#5

投稿記事 by UN » 9年前

こんにちは。
私はシングルトンでやっちゃってますね。
下記のコードでは厳密にはシングルトンではないですが、それっぽく

コード:

 
//キーボードクラス
class Keyboard{
private:
    int key[256];
public:
	static void Keyboard* getInstance()
	{
		static Keyboard s_KeyBoard;
		return &s_KeyBoard;
	}

    void Key_Update();
    int Key_Get(int KeyCode);
};
例えばこんな方法でもいいかと思います。
重要なのはキーパッドのように、例えプレイヤーがいなくても
キー入力を取得したいというケースはどうしてもあると思うので、
(例えばメニューとか)
どこからでもアクセスできるようにしといたほうがいいとは思いました。

>①みなさんは、オブジェクトの管理をどうしていますか。
これはケースバイケースだったりするので、答えにくいですね。
一例としてあげるなら、
オブジェクトを管理するものを1つ作成して、そこに全てのオブジェクトを登録しておくなど
すると楽とは思いますけど。
(BaseのObjectクラスを全てのオブジェクトで敬称させておくと管理しやすかったりします)

>②↑のとは関係ないですが、どこからクラス化したほうがいいのか教えて下さい。(例えば描画処理ならマップの描画などもまとめて描画クラスにいれたほうがいいのか、
描画はあくまで描画で、マップ描画というのは、"描画クラスを使用してマップを描画する"という
作りにしたほうがいいとは思います。描画クラスにプレイヤー描画、マップ描画・・・etcなど
オブジェクトが増えるごとに描画クラスに関数を増やさなくてはいけないというのは好ましくないと思いますので。

>③ゲームのクラス管理のコツなどを教えてください(こうやってやったほうがわかりやすいよなど・・・)
うーん、これは人それぞれだと思うのですが難しいですね。
ただ、いえるのは、上記でも述べたように
"オブジェクトが増えるごとに描画クラスに関数を増やさなくてはいけない"
このようなことに陥らないように設計しておくと、使いまわしがきくんじゃないかなぁとは思います。

>④ゲームのクラス管理に詳しいサイトさんなどを教えてください。
これも人それぞれだったりしますので、なんとも・・
サンプルソースを様々な人が配布してますが、いわゆるそれが
ゲームクラス管理のサンプルの貴重なパターンであることは
間違いないと思うので、それを自分なりに吸収するというのが
一番いいんじゃないかなぁと思いました。

すいません、アバウトな答えとなりまして。

アバター
h2so5
副管理人
記事: 2212
登録日時: 9年前
住所: 東京
連絡を取る:

Re: C++のクラスについて

#6

投稿記事 by h2so5 » 9年前

梨樹 さんが書きました: ゲームのクラス管理についてですが、
RPGツクールVXのスクリプトがオブジェクト指向の言語(Ruby)で書かれているので、
これが参考になると思います。
RPGツクールXP,VXのスクリプトはとても良いですよね。
私はオブジェクト指向をRGSSから学んだようなものです。

モノステートパターンを使うと、
RGSSのInputモジュール風に使えるので自分は気に入ってます。

アバター
またみふ
記事: 21
登録日時: 9年前

Re: C++のクラスについて

#7

投稿記事 by またみふ » 9年前

h2so5さん、a5uaさん、梨樹さん、UN さん、ありがとうございます!

なるほど、キーボードクラスや、ゲーム全体で必要なクラスはシングルトン、モノステートにすればいいんですね!
ちゃんと調べてみます!
貴重なご意見ありがとうございます!
RPGツクールVXのスクリプトがオブジェクト指向の言語(Ruby)で書かれているので、
これが参考になると思います。
RPGツクールですね!ソースは読める自信がありませんが、とても参考になると思うので見てみます。
オブジェクトを管理するものを1つ作成して、そこに全てのオブジェクトを登録しておくなど
すると楽とは思いますけど。
(BaseのObjectクラスを全てのオブジェクトで敬称させておくと管理しやすかったりします)
これは、共通クラスというものですか?(前どこかで見たような気がします)
例えばゲーム管理クラスというものがあってそこに全クラスのオブジェクトを入れるということですか?(違っていたらすいません。)

ソースで書くと

コード:

class GameManage{
	test test;
}

class test : public GameManage{
}
こんな感じですか?この方法の利点などを出来れば教えて欲しいです。
わがまま言ってすいません。よろしくお願いしますm(__)m

UN
記事: 18
登録日時: 9年前
住所: 神奈川県

Re: C++のクラスについて

#8

投稿記事 by UN » 9年前

そうですね、簡単に書くと(あくまで一例として受け取っていただきたいのですが)

コード:

 
enum
{
	OBJTYPE_PLAYER	= 0,
};

class BasObj
{
public:
	BasObj()
	{
	}
	virtual ~BasObj()
	{
	}
	
	virtual int			GetPositionX()
	{
		return m_PosX;
	}
	
	virtual int 		GetType()		= 0;
	
private
	int					m_PosX;
};

class Player : public BasObj
{
public:
	Player()
	{
	}
	virtual ~Player()
	{
	}
	
	virtual int GetType()
	{
		return OBJTYPE_PLAYER;
	}
	
	// プレイヤー独自のパラメーター
	int			GetHP()
	{
		return m_HP;
	}
	
private:
	int		m_HP;
};

class ObjManager
{
public:
	BasObj* GetObj( int objtype, int id )
	{
		BasObj* res = NULL;
		int 	Cnt = 0;
		vector< BasObj* >::iterator It = m_ObjList.begin();
		while( It != m_ObjList.end() )
		{
			BasObj* pObj = ( *It );
			if( pObj->GetType() == objtype )
			{
				if( Cnt == id )
				{
					res = pObj;
					break;
				}
				else
				{
					Cnt += 1;
				}
			}
			It++;
		}
		return res;
	}
	
	BasObj* CreateObj( int objtype )
	{
		BasObj* res = NULL;
		switch( objtype )
		{
			case OBJTYPE_PLAYER: res = new Player;	break;
		}
		m_ObjList.push_back( res );
		return res;
	}
	
private:
	vector< BasObj* >		m_ObjList;
};

/*
	実際にコードから使うときには
*/
ObjManager ObjMgr;
// あらかじめ登録しておく
ObjMgr.CreateObj( OBJTYPE_PLAYER );

/*
	プレイヤーの情報がほしいとき
	
	座標だけほしい、なんてときには
	BasObj* pPlayer = ObjMgr.GetObj( OBJTYPE_PLAYER, 0 );
	pPlayer->GetPositionX();
	これでもok
*/
Player* pPlayer = ( Player* )ObjMgr.GetObj( OBJTYPE_PLAYER, 0 );
pPlayer->GetPositionX();
pPlayer->GetHP();
ちょっと適当に実装してしまいましたが、このようにベースクラス、つまりフォーマットをある程度決めとくと、
オブジェクトを一つの箇所に纏めることができます。
あと、オブジェクトとは基本的にはこの情報をもっていて、オブジェクトの情報が欲しいときには
この関数にアクセスするということを定義にすることにもなるので、一々各オブジェクトクラスごとに
似たような関数を入れるということはなくなると思います。
プレイヤークラスの座標を受け取るときはこういう方法、敵クラスの座標を受け取るときはこういう方法、
と方法が違ったりするとめんどくさいですからね。

ただ、この方法だと無用なイテレーションを回すこととなり、速度が遅くなったり、
キャスト部分がわかっていないと、どれにキャストしていいのかわからないとかデメリットとかもあります。
この部分は工夫次第でどうにでもできますが。

長々と書いてしまいましたがこのへんの管理の仕方は人それぞれになってしまうものだと思うので、あくまで
自分に見合った実装をするのがいいと思います。

アバター
またみふ
記事: 21
登録日時: 9年前

Re: C++のクラスについて

#9

投稿記事 by またみふ » 9年前

UN さんが書きました:そうですね、簡単に書くと(あくまで一例として受け取っていただきたいのですが)

コード:

 
enum
{
	OBJTYPE_PLAYER	= 0,
};

class BasObj
{
public:
	BasObj()
	{
	}
	virtual ~BasObj()
	{
	}
	
	virtual int			GetPositionX()
	{
		return m_PosX;
	}
	
	virtual int 		GetType()		= 0;
	
private
	int					m_PosX;
};

class Player : public BasObj
{
public:
	Player()
	{
	}
	virtual ~Player()
	{
	}
	
	virtual int GetType()
	{
		return OBJTYPE_PLAYER;
	}
	
	// プレイヤー独自のパラメーター
	int			GetHP()
	{
		return m_HP;
	}
	
private:
	int		m_HP;
};

class ObjManager
{
public:
	BasObj* GetObj( int objtype, int id )
	{
		BasObj* res = NULL;
		int 	Cnt = 0;
		vector< BasObj* >::iterator It = m_ObjList.begin();
		while( It != m_ObjList.end() )
		{
			BasObj* pObj = ( *It );
			if( pObj->GetType() == objtype )
			{
				if( Cnt == id )
				{
					res = pObj;
					break;
				}
				else
				{
					Cnt += 1;
				}
			}
			It++;
		}
		return res;
	}
	
	BasObj* CreateObj( int objtype )
	{
		BasObj* res = NULL;
		switch( objtype )
		{
			case OBJTYPE_PLAYER: res = new Player;	break;
		}
		m_ObjList.push_back( res );
		return res;
	}
	
private:
	vector< BasObj* >		m_ObjList;
};

/*
	実際にコードから使うときには
*/
ObjManager ObjMgr;
// あらかじめ登録しておく
ObjMgr.CreateObj( OBJTYPE_PLAYER );

/*
	プレイヤーの情報がほしいとき
	
	座標だけほしい、なんてときには
	BasObj* pPlayer = ObjMgr.GetObj( OBJTYPE_PLAYER, 0 );
	pPlayer->GetPositionX();
	これでもok
*/
Player* pPlayer = ( Player* )ObjMgr.GetObj( OBJTYPE_PLAYER, 0 );
pPlayer->GetPositionX();
pPlayer->GetHP();
ちょっと適当に実装してしまいましたが、このようにベースクラス、つまりフォーマットをある程度決めとくと、
オブジェクトを一つの箇所に纏めることができます。
あと、オブジェクトとは基本的にはこの情報をもっていて、オブジェクトの情報が欲しいときには
この関数にアクセスするということを定義にすることにもなるので、一々各オブジェクトクラスごとに
似たような関数を入れるということはなくなると思います。
プレイヤークラスの座標を受け取るときはこういう方法、敵クラスの座標を受け取るときはこういう方法、
と方法が違ったりするとめんどくさいですからね。

ただ、この方法だと無用なイテレーションを回すこととなり、速度が遅くなったり、
キャスト部分がわかっていないと、どれにキャストしていいのかわからないとかデメリットとかもあります。
この部分は工夫次第でどうにでもできますが。

長々と書いてしまいましたがこのへんの管理の仕方は人それぞれになってしまうものだと思うので、あくまで
自分に見合った実装をするのがいいと思います。
>UNさんありがとうございます
返信遅れてすいません。
なるほど!ベースクラスの長所短所が分かりました!本当にありがとうございます!


自分はゲームのクラス管理は定石(?)みたいなのがあると思ってましたが、自分で1から組み立てないといけないということも分かりました。
本当に皆さんありがとうございました!

ISLe
記事: 2648
登録日時: 10年前
連絡を取る:

Re: C++のクラスについて

#10

投稿記事 by ISLe » 9年前

キーボードクラスから直接キー入力を得るのではなく、脳内設定した理想コントローラークラスを定義して使うようにすると良いですよ。
で、理想コントローラークラスを継承したクラスでキーボード入力から変換するような実装にすれば、キーボード以外の入力装置に差し替えるのも簡単です。

キーボードで作っておいて、あとからゲームパッド入力もキーボード扱いに変換するように実装していくのでも良いのですけど、ゲームは抽象化できるハードウェアがいろいろあります。

8方向入力のコントローラークラス
4方向(斜め入力無し)のコントローラークラス
アナログ方向入力のコントローラークラス
とか一度作ったものを使い回せるようにしておくと何度も同じものを作らなくて済むので楽です。

閉鎖

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