変数の扱いについて

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

変数の扱いについて

#1

投稿記事 by libra » 16年前

雑談と質問、どちらともつかずですが(^^);


画像ハンドルを変数に格納する時なのですが、

int image[100]
それぞれ対応した画像データを列挙体などで入れる方法と、

int playerdot[8]
int enemyshot[40]
というように、対応した画像データの数だけ変数を用意してやる方式のどちらの方がいいのでしょうか?
今自分はステージ毎異なる画像は前者、共通部画像(プレイヤー画像等)後者の方を使っています。

前者の方法を全部に適応させると、ループで画像を表示させるのが、非常に難しくなる、わかりずらいというのがあり、後者の方法を全部に適応するとメニュー画面等でゲームの画像を使わないときはゲーム画面の画像のために用意した変数を全く使わない状態になってしまいます。(使わない画像はInit関数でメモリを開放してますが)

龍神録は多分後者のやり方だと思うのですが・・・。

kazuoni

Re:変数の扱いについて

#2

投稿記事 by kazuoni » 16年前

ゲームの規模がわからないのでなんとも言えませんが、
前者はゲーム画像が多い場合には厳しい気がします。
添え字計算に突っ掛かってしまうこともありそうです。


やはり後者でしょうか。
ただ、画像数がものすごく多いなら、
必要に応じて動的確保を用いてもいい気がします。
が、基本的には、
int playerdot[8]
int enemyshot[40]
で良いと思います。

Dixq (管理人)

Re:変数の扱いについて

#3

投稿記事 by Dixq (管理人) » 16年前

私は画像に関するデータには全て最初にImageとかimg_とか付けています。

int ImageChara[10];
int ImageEnemy[10];

など・・。SE系なら

int SoundShot[10];
int SoundEffect[10];

みたいに、決まった単語を前につけたら解り易いと思います。
もし仰っている前者の方法でやりたいなら仕様書を作ることオススメします。

>後者の方法を全部に適応するとメニュー画面等でゲームの画像を使わないときはゲーム画面の画像のために用意した変数を全く使わない状態になってしまいます。

int型の変数を10個や100個使用しなくても全く問題ないと思いますが、どうでしょう?
画像を使用しないに読み込むのは非効率ですが、変数が存在しているだけなら特に支障ないと思います。
仰っている意味の理解が違っていたらすみません。

dic

Re:変数の扱いについて

#4

投稿記事 by dic » 16年前

私も悩んだことがありますが
構造体を使いました
この構造体で使用中フラグ変数を作りこれがアクティブな場合のみ処理するという方法です

ソースにすると以下のようになります
#include <stdio.h>

typedef struct
{
	bool	active;
	double	x, y;
} Obj_t;

void	Printf( Obj_t *obj )
{
	if( obj->active )
	{
		printf( "x:%f y:%f\n", obj->x, obj->y );
	}
}

void	main()
{
	Obj_t	obj;

	obj.x = 200.0;
	obj.y = 300.0;
	obj.active = true;

	Printf( &obj );

	//	100個のイメージを用意し、未初期化状態にする
	Obj_t	img[100];
	int	i;
	for( i=0; i<100; i++ )
		img.active = false;
}

dic

Re:変数の扱いについて

#5

投稿記事 by dic » 16年前

続き
//	50個初期化する
	for( i=0; i<50; i++ ) {
		img.x = rand()%3000;
		img.y = rand()%5000;
		img.active = true;
	}

	//	全部呼び出すが未初期化されていないものは
	//	呼び出し先関数では実行されない
	for( i=0; i<100; i++ )
		Printf( &img );

Libra

Re:変数の扱いについて

#6

投稿記事 by Libra » 16年前

皆様、丁寧な回答ありがとうございます。


>>kazuoni様
ゲームの規模・・・結構大きい物になってると思います。
画像数だけで分割してロードしたのを1つと数えると300以上使ってます。

>>管理人様
今回説明を省略してたのですが、宣言している変数は全て構造体に入れています。
typedef struct{
float player_x,player_y;
.
.
.
}Player_t;

typedef struct{
int playerdot[8];
int enemyshot[40];
.
.
.
}Img_t;
>>dic様
このソースの考え方は、シューティングの弾を出す時と同じアルゴリズムですよね。弾のフラグを管理するのであれば、今弾が発射されてない状態の変数があるのはいいのですが、"画像を使うとき"のフラグ変数がfalseのときのx,yの変数は全く使われません。
自分のソースを引き合いに出すと、menu[100] とメニュー画面で使うために宣言した変数がメニューでは使ってもゲームプレイ中では全く使われない、ということです。


ここで、これに関連した問題を提起します。
管理人様の回答より、int型変数が100個使用しなくても全く問題ないとのことですが、これが5000個、10000個となるとどうでしょうか?

敵の弾の情報で shot_flag(弾を出現させるフラグ) shot_x,shot_y(表示する座標位置)shot_time等は弾を出すときに絶対に使う変数ですが、それ以外の弾の動かし方によって使ったり使わなかったりする変数もあります。例えば、角速度を一定にして回転運動をさせたい時は、回転の中心座標と角度の変数が必要ですが、(多分^^;)ミシャグジ様弾幕はベクトルとかの考え方を使っていると思うので、中心座標の変数は使わない事になります。弾の個数を5000宣言したとき、中心座標の変数だけで10000個の変数が使われない状態になります。


自分が今考えているのは、常に使う変数以外で、ショットを出すときに同時に使う型の最大種類分だけ変数の種類を宣言するという方法です。
float f_shot1,f_shot2,f_shot3;

そして、制御する関数毎に「○型変数1は速度、○型変数2は角度・・・」というようにコメントを細かくつければ変数の無駄が一番少なくなると考えているのですが、この宣言の仕方はどうでしょう?shot_speed等、変数名で使用方法が判るのもいいのですが、色々な種類のショットを作ると使わない変数がどうしても大量に出てきてしまいます。


※弾の変数の扱いをリスト構造等で動的確保・・・とかはまだ上手く組み込む事が出来ないので最初に宣言をする形を取ってます。

Mist

Re:変数の扱いについて

#7

投稿記事 by Mist » 16年前

> 管理人様の回答より、int型変数が100個使用しなくても全く問題ないとのことですが、これが5000個、10000個となるとどうでしょうか?

int型10000個でも約40KBですよ?
メモリに制限のあるマイコンならいざ知らず、ロースペックノートPCでも500MBオーバーのメモリを搭載しているWindows環境なら微々たるものです。
MByteクラスの画像ファイルをバンバンロードできる環境でそういったことにこだわるのはナンセンスです。

dic

Re:変数の扱いについて

#8

投稿記事 by dic » 16年前

int img[100]
と宣言し
ゲーム内では50しか使わないので残りの50の未使用領域が問題だ
ということでしょうか?

それでしたら Mist さんもおっしゃるとおり
最近のパソコンのスペックを考えるとナンセンスですね

また、別の問題で、一度作ってしまってアップグレードする時など
保守的な面からみて、サイズに多少の余裕があった方が
変更箇所も減り、効率もアップするかと思います

Mist

Re:変数の扱いについて

#9

投稿記事 by Mist » 16年前

追記ですが

こういう問題を解決する場合、アプリケーション全体を見てメリットとデメリットのトレードオフを考えなければなりません。
問題を局地的に考えると後日思わぬところで落とし穴にはまることがあります。

あるポイントでしか使わない変数を静的変数として確保しておくのはもったいないというのはそのとおりなのですが、それを解決するために

・余分な処理が増えて実行速度が落ちる・・・微々たるものでしょうが
・ソースコードが複雑になりメンテナンス性(デバッグのしやすさ)が落ちる・・・こちらは重要
・対策自体が不具合の元になる・・・これも重要
 動的確保した場合の開放忘れとか

といったデメリットは少なくとも考慮する必要があると思います。

例えば、それらの余分なメモリを何とかしなければアプリケーションの実行に問題が出ると判明しているのであれば対策を打つのは当然ですが、現時点の問題もないのになんとなくもったいないという感じで取り組むのは私には労多くして益少なしと思います。

Libra

Re:変数の扱いについて

#10

投稿記事 by Libra » 16年前

メモリに関して⇒
パソコンのスペックを考えるとそうですね。プログラムの見易さ、デバックのしやすさを優先すると、その点に関しては変数を静的変数として確保するのが無難ですね。


変数の種類に関して⇒
プログラムを作成しているときに変数が必要になるたびに、変数どんどん追加していったのですが、今までやっていく中で、全体仕様が5回ほど変わってしまい、そのたびにプログラムを書き換えるということになってしまいました。今まで、数百~千くらいのプログラムの経験しか無いのが、万単位のプログラムに挑戦するとなるとどうしても仕様書の作り等も甘くなってしまいます。「この変数追加したらこの変数必要なくなったな」等・・・。
どうすれば、仕様変更にも柔軟に対応出来るか、と考えた結果が上で述べた

float f_shot1,f_shot2,f_shot3; ←コレ

なのですが、この点に関してはこの方法どうでしょうか?


追加して別の質問なのですが、構造体の中に変数を入れ、関数でその変数を扱いたいときに、その構造体のポインタを渡したのですが、プレイヤーに関する変数はプレイヤーの構造体、敵に関する変数なら敵の構造体という様にすると、関数にいくつものポインタを渡さなくてはなりません。最大8つとなりました。結局は構造体の中身の変数を使うなら、1個の構造体を作り、その中に全ての変数を入れてしまい、変数の名前の方を工夫すれば良いと考えたのですが、この方法に関してはどうでしょうか?

Justy

Re:変数の扱いについて

#11

投稿記事 by Justy » 16年前


>float f_shot1,f_shot2,f_shot3; ←コレ
>なのですが、この点に関してはこの方法どうでしょうか?

 前提として、構造体の配列で大量に確保する場合、種類によって常に使われたり
使われなかったりする変数があり、きちんと命名して変数を追加していくと
無駄にふくれあがっていってしまう、と。
 で、そのように書けば、どの種類でも汎用的に使えていい、とそういうことでしょうか?

 根本的に異なる種類なのに1つの構造体で賄おうとすることにはちょっと疑義を感じますが、
無理矢理1つに押し込めるにはそういう手段も仕方がないかとは思います。

 でも、どうせその方式でいくなら、例えば
[color=#d0d0ff" face="monospace]
enum TypeA
{
TypeA_CenterPosX,
TypeA_CenterPosY,
TypeA_Angle、
TypeA_Max
};
enum TypeB
{
TypeB_VectorX,
TypeB_VectorY,
TypeB_Max
};

float hogehoge[10]
[/color]

 と種類毎に enum定数を定義し、変数は配列にして、アクセスする時は必ず
[color=#d0d0ff" face="sans-serif">hogehoge[TypeA_CenterPosX][/color] とか [color=#d0d0ff" face="sans-serif]hogehoge[TypeB_VectorY][/color] とかのように
enumを使うようにすればいいのでしょうか。

 見た目何の役割を持った変数なのか判りやすくなりますし、
TypeA_Angleが要らなくなったら、enumから消すだけで済みます。



>追加して別の質問なのですが
>~
>1個の構造体を作り、その中に全ての変数を入れてしまい、
>変数の名前の方を工夫すれば良いと考えたのですが

 どうでしょう、と訊かれてもそれだけでは判断はちょっと難しいのですが、
結局は状況次第でその方がやりやすいというのであれば別にいいと思いますよ。

 ところで、ちょっと疑問なのですがその8つの情報とは何で、
それを必要とする関数って何なのでしょうか?

Libra

Re:変数の扱いについて

#12

投稿記事 by Libra » 16年前

変数に関しては列挙体を使う方式で進めていきたいと思います。

あと、判りにくい説明ですみません。
プログラムの構造は画像のような感じになっています。(結構省略してます)

WinMain関数内で構造体を宣言、そのポインタをそれぞれのゲーム関数に渡し、さらにゲーム関数内にあるそれぞれの処理、ショットの操作、キーの扱い等含めポインタを渡す形にしています。

龍神録サンプルを少し見た感じでは、構造体宣言部がグローバル領域にあるので、画像のように引数を渡さず構造体内のメンバにアクセスできるようになってると読み取りました。


龍神録はグローバル変数を使用し、自分はローカル変数を使っているわけですが、自分の場合も演算するときに、引数を指定して渡すのではなく、関数に構造体のポインタを渡しているので、龍神録と同じような変数の使い方が出来るようにしています。

>>根本的に異なる種類なのに1つの構造体で賄おうとすること
多分このグローバル変数のような使い方が影響していると思います・・・。


C言語を学びながら作成しているので、その時点での覚えたことのみで仕様書を作って作成、新たに覚えては作成、を繰り返していたので、色々ちぐはぐな事になっている部分が多々出来てしまいました。確かに仕様通り動くのですが。。。書き方的には間違ってないし、ちゃんと動くけど、この書き方実際どうなんだろう・・・という疑念も。
>>根本的に異なる種類なのに1つの構造体で賄おうとすることにはちょっと疑義を感じますが
のレスの通りです。

>>ところで、ちょっと疑問なのですがその8つの情報とは何で、 それを必要とする関数って何なのでしょうか?
⇒構造体のポインタでした。あと7つでした^^;プレイヤー、敵、ボス、画像、音、キー、3D背景使用時ポリゴン情報

Justy

Re:変数の扱いについて

#13

投稿記事 by Justy » 16年前


>プログラムの構造は画像のような感じになっています。(結構省略してます)

 なるほど、こうなっているわけですね。
 この状況では、確かに全部ポインタで渡すしかないですね。
 又今の規模はそれほど大きくないようなので、それらの構造体を1つの構造体に
まとめてしまってそのポインタを渡すのも有りです。

 ちなみにまとめ方にも2通りあり、複数の構造体をそのまま1つの構造体にしてしまう方法と、
複数の構造体のそれぞれのポインタを持った構造体を作ることです。

 後者の場合は、ただ GameStage1の関数を呼びやすくする為にだけに存在するようなものですが、
GameStage2、GameStage3と続くようなら効果的かもしれません。



>ちゃんと動くけど、この書き方実際どうなんだろう・・・という疑念も

 規模が十分に小さければ(図の通りエネミー1匹とか)、この書き方で全然問題ないです。
 逆に言えば、規模が大きくなるに連れ、この書き方では辛くなってくるかもしれない、
ということでもあります。

 今はまだ学習中とのことで、状況に合った書き方を模索してみると、
それぞれのメリット・デメリットが見えてくると思うので、いろいろ
試してみるのがいいかと思います。

Libra

Re:変数の扱いについて

#14

投稿記事 by Libra » 16年前

回答して下さった皆様、ありがとうございます。

一応、今のプログラムの規模が2万行くらいです。
D○COMOの携帯電話のF○MAのプログラムが32万行というのを聞いたことがあるので、大規模なプログラムとはいかなくても、1年経つか経たないかの初心者プログラマーが作るプログラムとしては一応中規模のプログラムになるのでしょうか。
以前はファイル出入力が出来なかったので、敵の出現データ、キャラクターの会話部分など全てプログラム内に打ち込んでいました。勉強してファイル出入力が出来るようになったので、全てそれに対応するよう書き換えたところ、プログラム量がかなり減った、といった感じに自分なりにプログラムの書き方を模索している最中です。

変数の構造体ポインタによる渡し方等は根本的なプログラムの構造なので、状況に合った書き方を模索するにしても万単位のプログラムになってしまったので、なかなか直せないのが現状です・・・。直すなら手遅れにならない早いうちにと思い、掲示板にスレ立てた次第です。

独習Cを2~3周したので、初歩的な部分の一通りの書き方は覚えました。ただ、やはり
「ちゃんと動くけど、この書き方実際どうなんだろう・・・」というのがあります。ここら辺の問題は文法や基本的な書き方ではなく、データの構造やら、アルゴリズムの話になってくるので、独習Cだけでは・・・・。他のゲーム作成の参考書のプログラムを参考にしようとしても、まだ勉強していないC++で書いてあったりでなかなか参考に出来ません。C++だとクラスとかがあるので、また構造が根本的に変わってしまうのでまたなんとも・・・。


明日あたり、去年の夏の時点でのゲームアップロードしてみたいと思います。(^^);
その時点でのソース、無駄が多く、機能も今よりもっと少ないくせに多分3万行位ありますが・・・。

dic

Windowsのメッセージが処理されない

#15

投稿記事 by dic » 16年前

int	WINAPI	WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, PSTR szCmdLine, int iCmdShow)
{
         ...
	DWORD	id;
	gH = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)Idle_test, NULL, 0, &id );
	gActive = true;

         Loop();

         ...
}

//------------------------
bool	Loop()
{
	while(gActive) {
		if( PeekMessage(&msg, 0, 0, 0, PM_REMOVE) ) {
			if( msg.message == WM_QUIT ) {
				return false;
			}
		}
		else
		{
			Sleep(100);
		}
	}
	return true;
}
上記のようにスレッドを作成し、別スレッドでゲームの処理をし、
メインスレッドでは普通のWindowsメッセージを処理しようとしているのですが、
xボタンが効かなかったり、Alt+F4が効かなかったりと Windowsのメッセージが
処理されていないようです
ゲームの処理はうまくいっているのですが、Windowsのメッセージの処理は
どこが悪いのでしょうか?

toyo

Re:Windowsのメッセージが処理されない

#16

投稿記事 by toyo » 16年前

×ボタンをクリックして送られるメッセージは
WM_NCLBUTTONDOWN

WM_NCLBUTTONUP
です
素直にDefWindowProc( )関数にメッセージ処理してもらったほうがいいと思います。
処理後はWM_DESTROYでPostQuitMessage( )を呼べばいいでしょう。

dic

Re:Windowsのメッセージが処理されない

#17

投稿記事 by dic » 16年前

ウィンドウメッセージが処理されないというより
指定したウィンドウプロシージャに制御が流れてないのです
ソースを上げておきます
WM_TIMER などもメッセージを受け取ることなく流れていってしまうのです

Mist

Re:Windowsのメッセージが処理されない

#18

投稿記事 by Mist » 16年前

ざくっとしか見てないけどPeekMessageでPM_REMOVEを指定しているからメッセージキューからメッセージが消えちゃってるよ。
チェックしたいだけならPM_NOREMOVEでは?

toyo

Re:Windowsのメッセージが処理されない

#19

投稿記事 by toyo » 16年前

DefWindowProc( )を呼びたくないなら
while(gActive) {
		if( PeekMessage(&msg, 0, 0, 0, PM_REMOVE) ) {
			if( msg.message == WM_NCLBUTTONUP ) {
				if (msg.wParam == HTCLOSE) {
					DestroyWindow(hWnd);
					gActive = FALSE;
				}
			}
		}
		else
		{
			Sleep(100);
		}
	}
で閉じるボタンのクリックが取得できます

dic

Re:Windowsのメッセージが処理されない

#20

投稿記事 by dic » 16年前

>Mistさん
PM_NOREMOVE でも PM_REMOVE でもダメみたいです

>toyoさん
閉じるは処理できますがウィンドウの最大化、最小化、ウィンドウ枠の変更などの処理が
複雑になるのでデフォルトの処理を用いたいのです

回答ありがとうございました

toyo

Re:Windowsのメッセージが処理されない

#21

投稿記事 by toyo » 16年前

dicさんのプログラムでは
bool	Loop()
{
	while(GetMessage(&msg, NULL, 0, 0) ) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return true;
}
にすればいいと思いますよ。

toyo

Re:Windowsのメッセージが処理されない

#22

投稿記事 by toyo » 16年前

上の投稿を編集したので削除しました

dic

Re:Windowsのメッセージが処理されない

#23

投稿記事 by dic » 16年前

>toyoさん
ありがとうございます
無事解決しました

閉鎖

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