ページ 11

グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月14日(水) 23:11
by LisetteLander
ゲームのmainループファイルの作り方で躓いています。
どうしたらいいでしょうか?

具体的には
管理人さんの”ゲームプログラミングの館”での

コード:

#include"連結ファイル.h"
void main(){

   switch(status){
      case 0:
      読み込み関数
      status=1;
      break;
      case 1:
      各種計算関数
      描画関数
      break;
   }
}
上記のようなコードを作っていたのですがグローバル変数や構造体、クラスを使わないで作るとなると
この設計はすこしやりづらいと思いました。
例えば、読み込みメソッドにおいて読み込んだグラフィックハンドルは(グローバル変数を使わないので)
直接どこかのメソッドに送り、どこかに保持しておかなくてはなりません。
また、mainメソッドには当然値が送られないのでmainメソッドから呼び出す計算・描画メソッドはそれぞれ独立してしまいます。

これらの問題を解決し、グローバルな変数・宣言した構造体・宣言したクラスを使わず、静的変数(staticなど)を
使わずに設計するのは不可能なのでしょうか?

個人的には、「モード単位」でmainメソッド内のメソッドを分割し、ツリー状にして最後に描画メソッドを持ってくることで実現できなくもない気がするのですが・・・半グローバル?(つまりトップメニュー画面ではトップメニュー画面で読み込み・描画メソッドを持つ)
※モード単位というのは
読み込みメソッド・描画メソッド・計算メソッド単位ではなく、
トップメニュー画面・メインゲームのような単位


もしよろしければ皆様の考えをお聞かせください。


ちなみに上記のコードは、
・自由に再ロードしたい
・自由にメイン画面に戻りたい(本来はcase1:にメイン画面メソッドがあり、statusを書き換える)
・計算メソッドをジャンプすることで”ポーズ”機能を追加できる
などのような事をする前提でかいてます。

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月14日(水) 23:31
by softya(ソフト屋)
とりあえず、その方針を完全に貫くと変えってバグが出やすくメンテナイス性の悪いプログラムになると思います。[少し修正]
「グローバルな変数・宣言した構造体・宣言したクラスを使わず、静的変数(staticなど)」をできるだけ使わないのはバグを減らしす為ではないのですか?
それぞれのメリット・デメリットをまず書いてみましょう。
その上で良い対処法も考えてみるではどうでしょうか?

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 00:19
by LisetteLander
個人的な~
のくだりはこんな感じ(画像)です。

メリットとしては
・作業が分けやすくなる
・他の関係ないメソッドではデータを弄れない。
・プログラムをより細かく分けられる。
・メンテナンス性は良い
・一部の変更が用意にできるようになる

デメリットは
・実効速度が遅くなる?
・コード量・ステップ数が肥大化
・メニューに戻ったりするたびにロードしないといけない
・「グローバル変数を使わない」という目的に対して斜め横の方向に行ってないか

以上のようにまとめてみました。
でも結局は、例えばメインゲームメソッドの中ではデータがグローバルになるため何も解決していないのでは・・・

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 00:29
by softya(ソフト屋)
それこそクラスのカプセル化を使ったほうが良い気がします。
mainに返すのはenum定義した次の状態値だけで良いのではありませんか?

ちなみに
メリットとしては
・作業が分けやすくなる → 引数がややこしくなるとメリットが減少します。
・他の関係ないメソッドではデータを弄れない。 → クラスのカプセル化や継承でガードできます。
・プログラムをより細かく分けられる。 → クラス化と継承で。
・メンテナンス性は良い → 引数で管理するとメンテナンス性は悪いです。
・一部の変更が用意にできるようになる → クラス化で対処できます。

デメリットは
・実効速度が遅くなる? → 書き方次第です。
・コード量・ステップ数が肥大化 → それはあるかも。
・メニューに戻ったりするたびにロードしないといけない → インスタンスの生成タイミングやコンストラクタを上手く使って下さい。
・「グローバル変数を使わない」という目的に対して斜め横の方向に行ってないか → それを優先しすぎるとデメリットもあります。
とりあえず連結.hのような共通ヘッダ設計はやめたほうが良いと思います。 → メンテナンス性の悪化につながります。

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 01:28
by ISLe
グローバル変数は分かりますけど、構造体やクラスを使わないのは何故でしょう。

グローバル変数を使わないというのはざっくりこういうことです。

コード:

// .h
struct GlobalData {
	int status;
	int GraHandle;
	// ...
};
// .cpp
static GlobalData g_data;
int main()
{
	Initialize(&g_data);

	for(;;){
		switch(g_data.status){
		case 0:
			読み込み関数(&g_data);
			g_data.status=1;
			break;
		case 1:
			各種計算関数(&g_data);
			描画関数(&g_data);
			break;
		}
	}
}
こういうふうにすると、画面を半分に割って、それぞれ別々に動かしたりもできます。

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 09:22
by softya(ソフト屋)
こんなふうな書き方もあります。
構造体の中身を見せていないので書き換えられる可能性は少ないです。

コード:

//	char.h

//	構造体の前方宣言(不完全宣言)
struct CharData;

//	関数
struct CharData *CharInit();		//	キャラクタの初期化
void CharDraw(struct CharData *);	//	キャラクタの描画

//	char.cpp

//	構造体の宣言
struct CharData {
	int GraphHandle;	//画像ハンドル
		・
		・
		・
};

//	キャラクタの初期化
struct CharData *CharInit()
{
	struct CharData *pCharData = new struct CharData;
	
	//ロードなどの初期化処理
	
	return pCharData;
}
//	キャラクタの描画
void CharDraw(struct CharData *pCharData)
{
	//	描画処理
	 DrawGraph(0,0,pCharData->GraphHandle,TRUE);
}

//	main.cpp
#include"char.h"

void main(){
{
	struct CharData* pCharData;
	switch(status){
	case 0:
		//読み込み関数
		pCharData = CharInit();
		status=1;
		break;
	case 1:
		//各種計算関数
		//描画関数
		CharDraw(pCharData);
		break;
	}
}

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 09:53
by LisetteLander
ISLe さんが書きました:グローバル変数は分かりますけど、構造体やクラスを使わないのは何故でしょう。

グローバル変数を使わないというのはざっくりこういうことです。

こういうふうにすると、画面を半分に割って、それぞれ別々に動かしたりもできます。
返信ありがとうございます。
グローバル構造体のpublicなメンバ変数はグローバル変数ではないのでしょうか?

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 10:15
by beatle
グローバル構造体っていうのは、グローバル領域で定義されている構造体のことを言ってますか?
だとすると、グローバル構造体のpublicなメンバ変数は必ずしもグローバル変数とは限りません。
グローバル構造体のインスタンスをグローバル領域で定義すればグローバル変数になりますが、
何らかの関数内でインスタンスを定義すればローカル変数です。

コード:

struct Hoge {
public:
    int val;
};
Hoge g_hoge; // g_hogeはグローバル変数(すなわちg_hoge.valもグローバル変数)

void f()
{
    Hoge hoge; // hogeはローカル変数(すなわちhoge.valもローカル変数)
}

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 10:17
by LisetteLander
softya(ソフト屋) さんが書きました:それこそクラスのカプセル化を使ったほうが良い気がします。
mainに返すのはenum定義した次の状態値だけで良いのではありませんか?

ちなみに
メリットとしては
とりあえず連結.hのような共通ヘッダ設計はやめたほうが良いと思います。 → メンテナンス性の悪化につながります。
そうすると、main.cppやdraw.cppにはそのつど必要なヘッダーだけ#includeを使用したほうがいいのでしょうか?

コード:

#include draw.h
#include calc.h
#include char.h

void main(){
...

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 10:19
by softya(ソフト屋)
draw.cppやcalc.cppって存在もなにか変です。共通化する意味ってなんでしょうか?
そうすると、main.cppやdraw.cppにはそのつど必要なヘッダーだけ#includeを使用したほうがいいのでしょうか?
その通りです。

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 10:21
by LisetteLander
beatle さんが書きました:グローバル構造体っていうのは、グローバル領域で定義されている構造体のことを言ってますか?
だとすると、グローバル構造体のpublicなメンバ変数は必ずしもグローバル変数とは限りません。
グローバル構造体のインスタンスをグローバル領域で定義すればグローバル変数になりますが、
何らかの関数内でインスタンスを定義すればローカル変数です。

コード:

struct Hoge {
public:
    int val;
};
Hoge g_hoge; // g_hogeはグローバル変数(すなわちg_hoge.valもグローバル変数)

void f()
{
    Hoge hoge; // hogeはローカル変数(すなわちhoge.valもローカル変数)
}
すみません、ちょっと勘違いしてました^^;
mainメソッドのスコープ内でインスタンスを持つ構造体・クラスはなぜかグローバル構造体・クラスだと思ってました・・・

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 17:24
by LisetteLander
解決とさせて頂きます

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 17:30
by softya(ソフト屋)
sood さんが書きました:解決とさせて頂きます
解決でも良いのですが、私やISLeさんの書いたことは理解されたのでしょうか?
何も質問されなかったので理解されているのか分からないのですが。

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 20:46
by LisetteLander
グローバル領域で構造体やクラスを定義し
mainローカル領域で構造体やクラスの宣言をしてインスタンスを確保する

と考えています。
当初勘違いしていたデータがmainメソッドを経由しない設計はやめて
mainメソッドを通じて多量のデータをやり取りする設計に変えています。
ただ、今は
sood さんが書きました: 例えば、読み込みメソッドにおいて読み込んだグラフィックハンドルは(グローバル変数を使わないので)
直接どこかのメソッドに送り、どこかに保持しておかなくてはなりません。
という問題でメインメソッドで大量にデータを格納するクラス・もしくは変数を宣言して初期化し、また必要なメソッドにはその大きなデータを
うけわたさなければならないのでは・・・と悩んでます

言葉で表すの苦手なので画像を・・・(プログラマ目指すものとして致命的ですが

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 21:49
by ISLe
グローバル変数を使わないというのと、構造化はまた別の次元の話なのでいろいろ試行錯誤して経験を積むのは良いことだと思います。

データの受け渡しについては、ゲームプログラミングでよく話題になるタスクシステムにおけるタスクの考え方を参考にすると良いのではないでしょうか。
soodさんの言う、ロード、計算、描画、はイベントだと考えてみてください。

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 22:30
by softya(ソフト屋)
図を見てて思ったのですが、なぜクラスとロード・計算・描画のメソッドが別のように書かれているのでしょうか?
各クラスがロード・計算・描画のメソッドを持っているんですよね?

そういえば新しい龍神録(c++版)が途中まで公開されているので参考にされては?
「龍神録プログラミングの館」
http://dixq.net/rp/
いちばん下の方にあります。

【追記】
mainを通じで全部のクラスの情報にアクセス可能ならグローバル変数で公開しているのと何ら変わらないと思いますが大丈夫ですか?
そういう意味で私が例で書いた構造体の内部構造の隠蔽の意図は分かってますでしょうか。アクセスできる情報と範囲を必要最小限にするのが目的なんですよね?

【さらに追記】
状況を見て「解決!」を解除させて頂きました。
それと解決した場合、フォーラムルールに従い解決した方法を最後に書いていただきたいのでよろしくお願いします。

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 23:16
by LisetteLander
softya(ソフト屋) さんが書きました:図を見てて思ったのですが、なぜクラスとロード・計算・描画のメソッドが別のように書かれているのでしょうか?
各クラスがロード・計算・描画のメソッドを持っているんですよね?
確かにそうです。全く何も考えずに画像を作ってました。
softya(ソフト屋) さんが書きました: そういえば新しい龍神録(c++版)が途中まで公開されているので参考にされては?
「龍神録プログラミングの館」
http://dixq.net/rp/
いちばん下の方にあります。
ありがとうございます。見てみます!
softya(ソフト屋) さんが書きました: 【追記】
mainを通じで全部のクラスの情報にアクセス可能ならグローバル変数で公開しているのと何ら変わらないと思いますが大丈夫ですか?
相違意味で私が例で書いた構造体の内部構造の隠蔽の意図は分かってますでしょうか。アクセスできる情報と範囲を必要最小限にするのが目的なんですよね?
初期化処理から戻ってきた返り値を専用のポインタに入れてそれをそのまま描画メソッドに入れてるのですよね・・・
・・・ということは、mainで宣言・初期化するのではなく、
初期化メソッドで宣言・初期化したデータのポインタを戻り値としてmainメソッドに返すことで他からはアドレスしか見えず、
またクラス・カプセル化すればメンバメソッドのみがアクセスできるようになるということですか

あ、これ散々ソフト屋さんが仰っていたことまんまですね・・・
察しが悪くてすいません・・・

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 23:38
by softya(ソフト屋)
そうすると今は、draw.cppやcalc.cppを使わない設計に変わったって事で良いんですね?
各クラスにdraw()やcalc()があると言うことで。

Re: グローバル変数を使わないゲームのmainファイルの設計

Posted: 2011年12月15日(木) 23:43
by LisetteLander
そうなります
そうすると描画優先順位や、当たり判定などに気を使わないといけなくなりますね
(自分行動→敵行動→当たり判定)みたいに・・・

と思いましたがそれぞれが自分の関数だけ持つので整理しやすくなりますね