ページ 11

武器クラスへのプレイヤー位置情報の渡し方

Posted: 2014年10月06日(月) 15:01
by あまさん
こんにちは。
皆さんから意見を聞きたくて、このトピックを立てました。

現在、c++でシューティングゲームを制作しています。
そこで、以下の様なクラスがあります。(簡素にしています)

コード:

class Character
{
public:

	Character(void);
	virtual ~Character(void);

	virtual void update(void) = 0;
	virtual void draw(void);

	//位置アクセサ
	int getX(void);
	int getY(void);
	void setX(int x);
	void setY(int y);
private:
	//左上座標
	int x, y;
};


class Player :
	public Character
{
public:
	Player(void);
	~Player(void);

	void update(void);
	void draw(void);
private:
	Weapon *weapon;
};

コード:

class Weapon
{
public:
	Weapon(void);
	~Weapon(void);

	//弾などの描画、更新関数
	virtual void draw(void) = 0;
	virtual void update(void) = 0;
};


class RapidShotWeapon :
	public Weapon
{
public:
	RapidShotWeapon(void);
	~RapidShotWeapon(void);

	void update(void);
	void draw(void);
};
今現在、上記のWeaponクラス、RapidShotWeaponクラスを作っています。
作成している途中で、RapidShotWeaponクラスのupdate()関数にて、Playerクラスの座標情報を知る必要が出てきました。
そこで、
  • Weaponクラスのupdate()関数の引数で座標を持ってくるようにする
  • RapidShotWeaponクラスのコンストラクタの引数でPlayerクラスのポインタを持ってくるようにする
の2つの方法が自分の中でパッと思いつきましたが、
前者については、他の武器を実装する場合に、Playerクラスの座標情報を知る必要がない場合がすでに考えられているという点であまり好ましくなさそうだと感じています。
後者については、Playerクラスが持つRapidShotWeaponクラスがPlayerクラスを参照できるという点が、これも好ましくなさそうだと感じます。
自分では他に納得できそうな案が思いつかず、困り果てています。

皆さんは、以上のような問題の例ならば、どのような実装を行いますか?

Re: 武器クラスへのプレイヤー位置情報の渡し方

Posted: 2014年10月06日(月) 16:03
by h2so5
updateの引数で渡すのが自然だと思います。Playerクラスの座標が必要でないならば使わなければいいだけなので。

Re: 武器クラスへのプレイヤー位置情報の渡し方

Posted: 2014年10月06日(月) 16:05
by softya(ソフト屋)
コントローラクラスが座標などを中継してはどうしょう。
そうすれば、Weaponクラス側がPlayerクラスの直接参照を必要としなくなります。
コントローラクラスは各クラスの依存ベタベタになりますが、その他のクラスの独立性は高まります。

Re: 武器クラスへのプレイヤー位置情報の渡し方

Posted: 2014年10月06日(月) 16:29
by usao
最も簡単には,
プレイヤーの座標を取得するための関数 を用意すれば良いのではないでしょうか.

Re: 武器クラスへのプレイヤー位置情報の渡し方

Posted: 2014年10月06日(月) 23:58
by あまさん
h2so5 さんが書きました:updateの引数で渡すのが自然だと思います。Playerクラスの座標が必要でないならば使わなければいいだけなので。
なるほど。確かに必要ないならば使わないという考え方もありますね…。
softya(ソフト屋) さんが書きました:コントローラクラスが座標などを中継してはどうしょう。
そうすれば、Weaponクラス側がPlayerクラスの直接参照を必要としなくなります。
コントローラクラスは各クラスの依存ベタベタになりますが、その他のクラスの独立性は高まります。
外部のクラスを使う手はあまり考えていませんでした。
Playerクラスを参照しないけど、情報は取得することができるのは良いですね。
usao さんが書きました:最も簡単には,
プレイヤーの座標を取得するための関数 を用意すれば良いのではないでしょうか.
それはグローバル関数か、softyaさんのように外部クラスを用意するということでしょうか?


色々意見があって参考になります。
しかしやはり、どこか妥協するという感じになるのでしょうか。
これはそもそもクラスの設計が間違いということなんでしょうか?

Re: 武器クラスへのプレイヤー位置情報の渡し方

Posted: 2014年10月07日(火) 00:47
by softya(ソフト屋)
何故クラス同士を疎結合にするか、カプセル化は何のためかを常に考えて作れば自然と収まる形に近づくと思います。
疎結合やカプセル化はバグを出さない・メンテナンス性を上げる手段にすぎないのです。何時でも綺麗で完璧な答えは難問です。
試行錯誤は一生続きますよ。失敗もあるし。

Re: 武器クラスへのプレイヤー位置情報の渡し方

Posted: 2014年10月07日(火) 09:25
by usao
>それはグローバル関数か、softyaさんのように外部クラスを用意するということでしょうか?

どちらでも良いと思います.
ともかくその時点でのプレイヤの位置という情報をどこかからどうにかして引っ張ってきてくれさえすれば.

(>RapidShotWeaponクラスがPlayerクラスを参照
 というのを避けたいということであれば間に何かしらワンクッション挟めば良い
 という意味においては同じ)

Re: 武器クラスへのプレイヤー位置情報の渡し方

Posted: 2014年10月07日(火) 16:29
by あまさん
softya(ソフト屋) さんが書きました:何故クラス同士を疎結合にするか、カプセル化は何のためかを常に考えて作れば自然と収まる形に近づくと思います。
疎結合やカプセル化はバグを出さない・メンテナンス性を上げる手段にすぎないのです。何時でも綺麗で完璧な答えは難問です。
試行錯誤は一生続きますよ。失敗もあるし。
言われてみればそうですね…。なんでも上手くいくように作ろうと考えてしまって答えがでなくては良くないですし。
usao さんが書きました:>それはグローバル関数か、softyaさんのように外部クラスを用意するということでしょうか?

どちらでも良いと思います.
ともかくその時点でのプレイヤの位置という情報をどこかからどうにかして引っ張ってきてくれさえすれば.

(>RapidShotWeaponクラスがPlayerクラスを参照
 というのを避けたいということであれば間に何かしらワンクッション挟めば良い
 という意味においては同じ)
そうですね。直接参照を持たないという点ではどれも大差ないですね

皆さん、様々な意見ありがとうございました。
完璧にするには難しいことがよく分かりました。
それでも、それなりに納得がいきそうな案ができそうです。なのでこのトピックは解決としますね。

Re: 武器クラスへのプレイヤー位置情報の渡し方

Posted: 2014年10月08日(水) 23:53
by ISLe()
解決しているようですが、わたしがふだん使っている手法を書いておきます。
この掲示板で過去に同じ話題で書いたこともあるのですが。

RapidShotWeaponにはあらかじめゲームコンテキストというオブジェクトへのポインタを渡しておきます。
ゲームコンテキストは、直接関連を持たないオブジェクトの情報が必要な処理へのインターフェースです。

例えばゲームコンテキストへのポインタがpContextだとして、
pContext->GetShotTarget()->GetPosition()
というふうにショットが向かう先の座標を取得します。

GetShotTargetでショットが向かう先を対象にしたインターフェースを取得します。
直接Playerクラスとはしないで抽象化し、インターフェースを実装する先で柔軟に対応できるようにします。
インターフェース(へのポインタ)を挟むことで、直接の関係を持たないソースファイルには定義を非公開とすることができます。

コンテキストからどのようなインターフェースを取得できるようにすると良いかは設計によりますが、インターフェースを取得する際に引数も使えますし、けっこう少ない数にまとめることができるので複雑にはなりません。
インターフェースからさらにインターフェースを取得するような構造にすればソースファイルの依存関係をさらに絞り込めます。
あらかじめ必要なインターフェースを全部用意できれば良いのですが、なかなか難しいことなのですよね。

Re: 武器クラスへのプレイヤー位置情報の渡し方

Posted: 2014年10月09日(木) 01:52
by あまさん
ISLe() さんが書きました:解決しているようですが、わたしがふだん使っている手法を書いておきます。
この掲示板で過去に同じ話題で書いたこともあるのですが。

RapidShotWeaponにはあらかじめゲームコンテキストというオブジェクトへのポインタを渡しておきます。
ゲームコンテキストは、直接関連を持たないオブジェクトの情報が必要な処理へのインターフェースです。

例えばゲームコンテキストへのポインタがpContextだとして、
pContext->GetShotTarget()->GetPosition()
というふうにショットが向かう先の座標を取得します。

GetShotTargetでショットが向かう先を対象にしたインターフェースを取得します。
直接Playerクラスとはしないで抽象化し、インターフェースを実装する先で柔軟に対応できるようにします。
インターフェース(へのポインタ)を挟むことで、直接の関係を持たないソースファイルには定義を非公開とすることができます。

コンテキストからどのようなインターフェースを取得できるようにすると良いかは設計によりますが、インターフェースを取得する際に引数も使えますし、けっこう少ない数にまとめることができるので複雑にはなりません。
インターフェースからさらにインターフェースを取得するような構造にすればソースファイルの依存関係をさらに絞り込めます。
あらかじめ必要なインターフェースを全部用意できれば良いのですが、なかなか難しいことなのですよね。
閉じたのにわざわざ有り難うございます。
結局はそれと似たような実装の仕方をしました。
実装してみると以外にもスッキリした感じになり、此処で意見を聞いて良かったと思います。
構造については経験でうまいことしていく必要がありそうだなと、実装しながら感じました。難しいですね・・・。

Re: 武器クラスへのプレイヤー位置情報の渡し方

Posted: 2014年10月09日(木) 18:57
by ISLe()
ゲーム内オブジェクトの基底クラスにコンテキストをリレーする仕組みを隠し、どこでもGetContext()できるようにするとさらに便利です。
#プラットフォームがコンテキストを持つような場合でも内包できるのでプラットフォームに依存することはありません。

常にファクトリーメソッド経由でインスタンスを構築することになりますが、リストに登録したりを同時に処理できる(隠せる)ので、むしろメリットのほうが大きいと思います。

ふつうにシングルトンとか使える環境ならそこまでしなくてもいいかもしれませんが、あらかじめそうしておくことで移植性は高くなります。