変数のカプセル化を守りながらのデータの受け渡しについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
Tatu
記事: 445
登録日時: 14年前
住所: 北海道

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#101

投稿記事 by Tatu » 10年前

No:98のコードについて

get_next_enemy_order関数のorder_numは
order_numより前のenemy_orderを調べないことにより、
重複を避けるために追加したものだと思います。そのようになっていますか?

get_next_enemy_order関数に
if(order_num==n)とありますが
これではorder_num番目のenemy_orderのポインタしか返せないことになります。

enemy_enter関数に
static int order_num=0;
とありますが
後のオーダーの出現カウントが
前のオーダーの出現カウントよりも低いことがある場合は
どのように動きますか?

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#102

投稿記事 by SDD » 10年前

Tatuさん
仰るとおりでした。
順番に依存しているため、オーダーの前後が順番にならんでいないと、order_numとカウントに値するオーダーのカウントが
ずっとかみ合わないまま進んでしまいました。
やり直します。

ISLe()

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#103

投稿記事 by ISLe() » 10年前

期待した動作をするかしないかという以前の問題があります。
せっかくenemy_order配列を隠したのに、添字を要求してしまうのですか?
添字を使うということは、配列を直接参照していなくても、配列を使っていることに依存します。

enemy_enterに追加したコードも、抽象化したステートメントとしての体をなしていません。
実装に合わせて無理矢理押し込んだコードですね。
構造化の基本を忘れないで下さい。


実際問題としてこの部分の実装は、予備知識なしでは難しいと思います。
それでも抽象化したステートメントで表現することはできるのではないでしょうか。
考えてみてください。
実装コードは状況を見てこちらから提示したいと思いますがSDDさん自身でたどり着いて欲しいと思います。

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#104

投稿記事 by SDD » 10年前

ISLeさん
構造化の基本、ということは、あくまでその処理を関数化して、enemy_enter()に加える、ということですね。


わかりました。できる限り、自力でかんがえます。正直なところ、時間に余裕がなく、本音を言えばすぐにでも知りたいのですが、
やはりこういう土壇場で自分で考え出すことはこれからいくらでも問われる能力だと思うし、その度に人にいちいち聞くなんてあり得ないですしね・・・。



ただ、「せっかくenemy_order配列を隠したのに、添字を要求してしまうのですか?
添字を使うということは、配列を直接参照していなくても、配列を使っていることに依存します。」とのことですが、それは n=i;のことでしょうか。
また、enemy_order配列はget_next_enemy_order内では隠されていないのですが、それでも添え字を使うのはダメなのでしょうか・・・。
戻り値の return &enemy_order;も間違いなのでしょうか・・・。すみません、なにか勘違いをしているかもしれません。

アバター
Tatu
記事: 445
登録日時: 14年前
住所: 北海道

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#105

投稿記事 by Tatu » 10年前

ISLe()さんがNo:103で書いたように
「実装に合わせて無理やり押し込んだコード」を
抽象化したステートメントで表現してみてください。


ところで、配列aがあるとして&aを他にどう表現できるか知っていますか?

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#106

投稿記事 by SDD » 10年前

Tatuさん
&aの別の表現ですか・・・。
aが*(a+i)のシンタックスシュガーだということは知っているのですが、それとはまた別でしょうか。


Tatu さんが書きました:ISLe()さんがNo:103で書いたように
「実装に合わせて無理やり押し込んだコード」を
抽象化したステートメントで表現してみてください。


出来次第、コードを載せようと思います。

ISLe()

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#107

投稿記事 by ISLe() » 10年前

get_next_enemy_order関数の中身を知らないはずの(むしろ知らなくて良い)enemy_enter関数が、get_next_enemy_order関数に対して配列のどこまで進んだかを覚えておいたり教えてやったりするのはおかしいと思いませんか?
そこも隠さないといけないところです。

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#108

投稿記事 by SDD » 10年前

すみません、ISLeさんがログアウトされないうちに、と思い、急いで書いたコードなので、ところどころで整っていませんが、
少しは回答にちかづけたでしょうか。カウントの大小の順不同があっても関係なく動きます。

コード:

//enemymgr.cpp


static int order_num;



void set_order_num(){
	order_num=0;
}



int *get_order_num(){
	return &order_num;
}


enemy_order_t *get_next_enemy_order(int cnt,int *n){
	int i;
	for(i=0;i<ENEMY_ORDER_MAX;i++){
		if(cnt==enemy_order[i].cnt && *n==0){
			*n+=1;
			return &enemy_order[i];
		}
	}
	return NULL;

}


void enemy_enter() { //敵の行動を登録・制御する関数

	enemy_order_t *enemy_order_ptr;

	//order_numを初期化
	set_order_num();

	int *n=get_order_num();

	//敵キャラの出現オーダーからタイミングの一致するものを選択
	// 以下を選択されたオーダーごとに繰り返す
	while(enemy_order_ptr=get_next_enemy_order(global_stage_cnt,n)){

		enemy_t *enemy_ptr;
		//敵キャラ用のメモリ領域を新規に取得する
		enemy_ptr=get_enemy_buffer();
		if(enemy_ptr==NULL){
			continue;
		}
		//前段で取得した敵キャラ用のメモリ領域を初期化する
		init_enemy_buffer(enemy_ptr,enemy_order_ptr);
	}
}




SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#109

投稿記事 by SDD » 10年前

すみません。投稿した後に気付きましたが、肝心の「同じカウントの敵の出現」が実現できませんね・・・。
やり直します

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#110

投稿記事 by usao » 10年前

オフトピック
こんな形にしちゃえば order_num みたいなのが無くても済むかもしれない.
(若干使いにくいかもだけど)

コード:

//出現タイミング(?)がcntと合致するオーダー群をDstBuffに入れる.
//(ただし,条件に該当するオーダーの個数がBuffSizeよりも多いときは
// それらのうちのBuffSize個だけを入れる)
//
//条件に見合うオーダーの個数を戻り値として返す.
//(BuffSizeよりも大きい数が返る可能性がある)
//
int GetAppearableEnemyOrders(  //※うまい関数名が思いつかなかったのでてきとーです
  int cnt,  //現在のゲーム進行状況カウンタみたいなの
  enemy_order_t *DstBuff[],  //結果受取領域を指定
  int BuffSize  //DstBuffのサイズを指定する感じ
);

ISLe()

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#111

投稿記事 by ISLe() » 10年前

SDD さんが書きました:少しは回答にちかづけたでしょうか。カウントの大小の順不同があっても関係なく動きます。
近付いてないですね。
抽象化になってないです。
添字番号そのものを隠してください。

get_next_enemy_orderに必要でも、enemy_enter関数からしたら知る由もない(知るべきではない・知ってはいけない)ことです。
なので引数に(直接)現れてもいけません。

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#112

投稿記事 by SDD » 10年前

usaoさん
毎度ありがとうございます。
最終的にできたコードと教えていただいたやり方を比べて、自分に合う方を使いたいと思います。
まだ、なにが正解か分かっていないので、せっかく教えていただいたコードなのですが、もう少し
考えて見ます。すみません・・・。



ISLeさん
すみません、添字を隠せていないというのは、どこで隠せていないのでしょうか・・・。
nという情報を管理部が知ってはいけないのは分かりましたが、配列の添字は管理部では使っていないと思っていたのですが、
添字を隠すということが良く分かっていないようです。

アバター
Tatu
記事: 445
登録日時: 14年前
住所: 北海道

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#113

投稿記事 by Tatu » 10年前

No:106について
aと*(a+i)が同じことを指すことがわかっているのであれば
&aの別の表現方法もわかりませんか?

ISLe()

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#114

投稿記事 by ISLe() » 10年前

No.108では添字を使っていないからいいじゃないか、ということでしょうか。

抽象化というのは実装を隠すことです。
添字を使っている/使っていない、という点にこだわっている時点で実装にしか視線が向いていないのではないでしょうか。

ここはどうも堂々巡りになりそうなので、こちらからコードを提示したいと思います。

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#115

投稿記事 by SDD » 10年前

結局教えていただくことになってしまってすみません。
しばらく考えていたのですが、抽象化のこともそうですが、期待通りの振る舞いをするコードはかけませんでした。
提示したコードのように、順番や出現カウントのどちらかが犠牲になってしまうような感じになってしまいました。
同じカウントの敵の出現を実現するためにはループ内でなんとかしなきゃいけない、とかいろいろ思ったのですが・・・。

アバター
Tatu
記事: 445
登録日時: 14年前
住所: 北海道

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#116

投稿記事 by Tatu » 10年前

No:113の続きについては

aと*(a+i)がおなじなので&aと&(*(a+i))も同じです。
そして、&(*(a+i))はa+iと同じことです。
よって&aはa+iと書いても同じです。

&aに対してaの次の要素のポインタ&a[i+1]はa+(i+1)となります。
&aをp_aと書き直すと&a[i+1]はp_a+1と同じになります。

このようにaの要素を指すポインタp_aが与えられた時
次のaの要素を指すポインタを返す関数ができます。
p_aがaの最後の要素を指すポインタだった場合は
次のaの要素がないという事を示すため、NULLを返すことにします。

aの最初の要素を指すポインタを返す関数も作ることができます。

aをenemy_orderに置き換えてこれら2つの関数を追加し、
添字の指定に使っていたiを
enemy_orderのポインタに置き換えれば

条件に合うenemy_orderを探す処理について
添字を用いない形に書き換えることができませんかと書くつもりでした。

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#117

投稿記事 by SDD » 10年前

Tatuさん
すみません、No:113に対する返信が遅れてしまいました。
正直、Tatuさんに今教えていただいたことにはまったく至っておりませんでした・・・。

ISLeさんの仰るように、僕が理解が及んでいないせいで、堂々巡りになってしまっているのかもしれませんが、
*get_next_enemy_orderにて、enemy_orderという表現を使っているのは良くないのでしょうか・・・。
こちらの思っていたことなのですが、管理部は配列について知らないべきというのはよくわかったのですが、
末端である*get_next_enemy_orderでも使ってはいけないのでしょうか。

Tatu さんが書きました:No:113の続きについては

aと*(a+i)がおなじなので&aと&(*(a+i))も同じです。
そして、&(*(a+i))はa+iと同じことです。
よって&aはa+iと書いても同じです。




僕がもっとよく考えていれば、この部分はわかったのかも知れませんが、正直、目からうろこというか、おもしろいと思いました。

アバター
Tatu
記事: 445
登録日時: 14年前
住所: 北海道

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#118

投稿記事 by Tatu » 10年前

添字を用いない形に書き換えることについて書いた理由は
配列の操作に添字を使わなければならないと思っているのではないかと思い、
添字を使わなくても配列の操作が可能であるという事を示したかったからです。

enemy_orderが次の要素のポインタがわかるデータ構造ならば
配列でなくても条件に合うenemy_orderを探す処理を同様に書けるでしょう。

ISLe()さんが書いたNo:111を読む限り、
get_next_enemy_orderに添字番号を使っても問題ないでしょう。

ISLe()

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#119

投稿記事 by ISLe() » 10年前

No.108のコードから改変になります。
このコードだけでは不足している宣言等あります。

コード:

/*
 *  enemy_order.cpp
 */

struct enemy_order_enum_info_t {
	int next_index; // 次に検索対象となる添字番号
	int search_cnt; // 検索するカウンタ値
	bool in_use; // 使用中フラグ
};

static enemy_order_enum_info_t enemy_order_enum_info[1];

enemy_order_enum_info_t *prepare_enemy_order_enumeration(int cnt)
{
	enemy_order_enum_info_t *info = &enemy_order_enum_info[0];
	info->next_index = 0;
	info->search_cnt = cnt;
	info->in_use = true;
}

void finish_enemy_order_enumeration(enemy_order_enum_info_t *info)
{
	info->in_use = false;
}

enemy_order_t *get_next_enemy_order(enemy_order_enum_info_t *info)
{
	for (; info->next_index<ENEMY_ORDER_MAX; info->next_index++) {
		if (enemy_order[info->next_index].cnt == info->search_cnt) {
			return &enemy_order[info->next_index++];
		}
	}
	return NULL;
}

/*
 * enemy_manager.cpp
 */

struct enemy_order_enum_info_t;

void enemy_enter() // 敵の行動を登録・制御する関数
{
	enemy_order_t *enemy_order_ptr;
	enemy_order_enum_info_t *enemy_order_enum_info;

	// 指定のカウンタ値に合致する敵出現オーダーを列挙する準備手続き
	enemy_order_enum_info = prepare_enemy_order_enumeration(global_stage_cnt);

	// 敵キャラの出現オーダーから合致するものを列挙
	// 以下を列挙されたオーダーごとに繰り返す
	while (enemy_order_ptr = get_next_enemy_order(enemy_order_enum_info))
	{
		enemy_t *enemy_ptr;
		// 敵キャラ用のメモリ領域を新規に取得する
		enemy_ptr = get_enemy_buffer();
		if (enemy_ptr == NULL) {
			continue;
		}
		// 前段で取得した敵キャラ用のメモリ領域を初期化する
		init_enemy_buffer(enemy_ptr, enemy_order_ptr);
	}

	// 敵出現オーダー列挙の後始末手続き
	finish_enemy_order_enumeration(enemy_order_enum_info);
}
enemy_order_enum_info_t構造体の定義を隠すには、モジュールを分離が必要です。

ここでは仕様上不要なのですが、enemy_order_enum_infoは配列にして、構造体のメンバに使用中フラグを入れました。
多重ループに対応するためのヒントです。
別の場所で列挙のコードを書くときには必要になるかもしれないので。
テンポアップのために盛ってみました。

Tatuさんのおっしゃるように添字を使わない方法もありますが、ポインタはたらい回し的な使い方に限定しています。
実装は隠されて外界と分離されているので柔軟に対応できます。
今回はこれで良いのではないかと思います。
将来的にはより良い実装を身に付けるべく励んでください。

ISLe()

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#120

投稿記事 by ISLe() » 10年前

prepare_enemy_order_enumeration関数に戻り値を返すコードが抜けてました。

最後に
return info;
の一行を追加してください。

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#121

投稿記事 by SDD » 10年前

TatuさんとISLeさんの書き込みを拝見はしていましたが、こちらからの返信が遅れてしまいました。
すみません。

Tatuさん
Tatu さんが書きました:添字を用いない形に書き換えることについて書いた理由は
配列の操作に添字を使わなければならないと思っているのではないかと思い、
添字を使わなくても配列の操作が可能であるという事を示したかったからです。
仰るとおりで、そのように思っていました。今回教えていただいたことをまったく知っていませんでした。
配列の添え字を使わない表現があるということ、留意します。
一連のアドバイス、勉強になりました。ありがとうございます。


ISLeさん
コードを頂いてしまい、すみません。
Tatuさんのアドバイスに対してもそうでしたが、
今回も教えていただいたコードには僕の考えはぜんぜん至っていませんでした。
まだまだその場その場の本質を掴んだり考えたりする力が弱いみたいです。

いただいたコードをしばらく吟味し、せめて他人に書いてもらったコードを
よくよく理解することをしたいと思います。
また、ポインタの扱い、for文の工夫(頂いたコードのfor文の使い方。こういうスタティックな変数を使うこともまったく考えませんでした)、要素を列挙する仕組みも留意しておきます。
ISLe() さんが書きました:prepare_enemy_order_enumeration関数に戻り値を返すコードが抜けてました。

最後に
return info;
の一行を追加してください。
これにはさすがに自分で気付けました。穴埋めみたいな感じにしてもらったのかな・・・と思っていました汗
ご丁寧に教えていただいて本当に勉強になります。

Rittai_3D
記事: 525
登録日時: 12年前

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#122

投稿記事 by Rittai_3D » 10年前

オフトピック
このタイミングで投下するのは良いかどうかわかりませんが、以前書いたと言っていたコードを載せます。載せる載せると言っておいて載せないのはどうかと思いましたので。

いろいろなコードをみて混乱するかもしれませんので言っておきますと、わたしが書いたコードは拡張性が無く、変数を隠すことを主な目的とした物です。ISLe()さんのコードのように、これから先を見越した設計にはなっておりません。「ふーん、変数を隠しても動くんだ」程度にご覧ください。あくまでも、カプセル化を意識したサンプルです。ので、わたしのコードに囚われないようにしてください。

また、デバッグコードも残っていたり、色々とエラーチェックを省いています。ご了承下さい。(バグがあったらすいません)
► スポイラーを表示
(!)このコードは私の日記にもあります。
初心者です

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#123

投稿記事 by SDD » 10年前

3Dさん
昔のことなのに、覚えていただけてうれしいです。
よく読んで参考にさせていただきたいです。
ただ、実は皆さんの日記はちょくちょく読んでいて、八月の終わりごろに、うっかり3Dさんの日記に
書かれたコードを見てしまったので忘れるようにしていました。
ご親切に考えていただき、すみません。

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#124

投稿記事 by SDD » 10年前

現状としては、既存のコードを構造化する練習をしています。
まだまだなかなか慣れず、下手な感じになったり、試行錯誤していますが・・・。

今回のトピックはNO.93に記した問題の三つ目(ショット関係など)をこちらでまず、コードを構造化してみて、
それを見て頂き、改修し、それが終わったら解決にしたいと思います。

長引いてしまい、申し訳ないのですが、もう少しお付き合いしていただけたら幸いです。

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#125

投稿記事 by SDD » 10年前

また、教えていただいた「要素を順に列挙する」というものは、応用がよく効くのがよくわかりました。
雑魚敵がまったく同時に死んだときに発生する消滅エフェクトの管理などでもつかえそうですね。
よく覚えておきます。

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#126

投稿記事 by SDD » 10年前

問題が全て解決していないのに、更新が滞ってしまい、すみません。
大学などでなかなかまとまった時間がとれず、プログラムの勉強が滞ってしまってます。

また、今回の構造化や抽象化のことは、まだなかなか思い通りに慣れず、こちらとしてもじっくり取り掛かりたいし、やり方をしみこませたいので
問題は最後まで終わらせたいです。
これまでよりだいぶスローペースになってしまいますが、お世話になっているISLeさんをはじめ、引き続き皆さんにはお暇なときにお相手していただければと思います。
なかなかトピックを終わらせられず、ご迷惑をおかけします。

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#127

投稿記事 by SDD » 10年前

連投すみません。

以前「ステートメントとして見づらいコード」との評価をしていただきましたが、たしかにとおもったため、少し直してみたり
隠蔽できるところはしようと思ったのですが、多くのエラーがでてしまいました。
とりあえず現状のコードを貼ります。

コード:

#ifndef DEF_ENEMY_ORDER_H
#define DEF_ENEMY_ORDER_H

//enemy_order.h

void load_enemy_order();

enemy_order_enum_info_t *prepare_enemy_order_enumeration(int cnt);

enemy_order_t *get_next_enemy_order(enemy_order_enum_info_t *info);


#endif

コード:

#include "DxLib.h"
#include "define.h"


//enemy_order.cpp


//敵のオーダー用
typedef struct{
	//カウンタ、移動パターン、敵の種類
	int cnt,pattern,knd;
	//初期座標と移動スピード
	double x,y,sp;
	//弾幕開始時間、弾幕の種類、色、体力、弾の種類、停滞時間、アイテム(6種類)
	int bltime,blknd,col,hp,blknd2,wait,item_n[6];
}enemy_order_t;



//敵のオーダー列挙用
typedef struct{
    int next_index; // 次に検索対象となる添字番号
    int search_cnt; // 検索するカウンタ値
    bool in_use; // 使用中フラグ
}enemy_order_enum_info_t;
 




static enemy_order_t enemy_order[ENEMY_ORDER_MAX];//敵のパターン格納
static enemy_order_enum_info_t enemy_order_enum_info[1];




//敵の出現情報をエクセルから読み込んで格納する関数
void load_enemy_order(){



	//enemy_orderをゼロクリア
    memset(enemy_order,0,sizeof(enemy_order_t)*ENEMY_ORDER_MAX);



	int n,num,i,fp;
	char fname[]={"story1.csv"};
	int input[64];
	char inputc[64];

	fp = FileRead_open(fname);//ファイル読み込み
	if(fp == NULL){
		printfDx("read error\n");
		return;
	}
	for(i=0;i<2;i++)//最初の2行読み飛ばす
		while(FileRead_getc(fp)!='\n');

	n=0 , num=0;
	while(1){
		for(i=0;i<64;i++){
			inputc[i]=input[i]=FileRead_getc(fp);//1文字取得する
			if(inputc[i]=='/'){//スラッシュがあれば
				while(FileRead_getc(fp)!='\n');//改行までループ
				i=-1;//カウンタを最初に戻して
				continue;
			}
			if(input[i]==',' || input[i]=='\n'){//カンマか改行なら
				inputc[i]='\0';//そこまでを文字列とし
				break;
			}
			if(input[i]==EOF){//ファイルの終わりなら
				goto EXFILE;//終了
			}
		}
		switch(num){
						case 0: enemy_order[n].cnt      =atoi(inputc);break;
						case 1: enemy_order[n].pattern  =atoi(inputc);break;
						case 2: enemy_order[n].knd      =atoi(inputc);break;
						case 3: enemy_order[n].x        =atof(inputc);break;
						case 4: enemy_order[n].y        =atof(inputc);break;
						case 5: enemy_order[n].sp       =atof(inputc);break;
						case 6: enemy_order[n].bltime   =atoi(inputc);break;
						case 7: enemy_order[n].blknd    =atoi(inputc);break;
						case 8: enemy_order[n].col      =atoi(inputc);break;
						case 9: enemy_order[n].hp       =atoi(inputc);break;
						case 10:enemy_order[n].blknd2   =atoi(inputc);break;
						case 11:enemy_order[n].wait     =atoi(inputc);break;
						case 12:enemy_order[n].item_n[0]=atoi(inputc);break;
						case 13:enemy_order[n].item_n[1]=atoi(inputc);break;
						case 14:enemy_order[n].item_n[2]=atoi(inputc);break;
						case 15:enemy_order[n].item_n[3]=atoi(inputc);break;
						case 16:enemy_order[n].item_n[4]=atoi(inputc);break;
						case 17:enemy_order[n].item_n[5]=atoi(inputc);break;
		}
		num++;
		if(num==18){
			num=0;
			n++;
		}
	}
EXFILE:
	FileRead_close(fp);
}





 
enemy_order_enum_info_t *prepare_enemy_order_enumeration(int cnt){

    enemy_order_enum_info_t *info = &enemy_order_enum_info[0];
    info->next_index = 0;
    info->search_cnt = cnt;
    info->in_use = true;
	return info;
}
 

void finish_enemy_order_enumeration(enemy_order_enum_info_t *info){
    info->in_use = false;
}
 

enemy_order_t *get_next_enemy_order(enemy_order_enum_info_t *info){
    for (; info->next_index<ENEMY_ORDER_MAX; info->next_index++) {
        if (enemy_order[info->next_index].cnt == info->search_cnt) {
            return &enemy_order[info->next_index++];
        }
    }
    return NULL;
}
 


コード:

#ifndef DEF_ENEMY_H
#define DEF_ENEMY_H


//enemy.h


//空いている敵のポインタを返す
enemy_t *get_enemy_buffer();


//初期化、ロード
void enemy_init();
//自機の計算
void enemy_act_all();
//描画
void enemy_draw();
//終了処理
void Enemy_Final();


#endif

コード:

#include "DxLib.h"
#include "define.h"
#include "math.h"
#include "shot.h"

//enemy.cpp



//敵に関する構造体
typedef struct{
	//フラグ、カウンタ、移動パターン、向き、敵の種類、HP最大値、落とすアイテム、画像、背景色
	int flag,cnt,pattern,knd,hp,hp_max,item_n[6],img,back_col;
	//座標、速度x成分、速度y成分、スピード、角度
	double x,y,vx,vy,sp,ang;
	//弾幕開始時間、弾幕の種類、弾の種類、色、状態、待機時間、停滞時間
	int bltime,blknd,blknd2,col,state,wtime,wait;
	//画像ハンドル
	int img_enemy[3];
}enemy_t;





static enemy_t enemy[ENEMY_MAX];//敵の実態
static int m_img;//敵の画像ハンドル



//空いている敵のポインタを返す
enemy_t *get_enemy_buffer(){
	int i;
	for(i=0;i<ENEMY_MAX;i++){
		if(enemy[i].flag==0){
			return &enemy[i];
		}
	}
	return NULL;
}








//敵の行動制御
void enemy_act(enemy_t *enemy){
	if(enemy->flag==1){//その敵のフラグがオンになってたら
		if(0<=enemy->pattern && enemy->pattern<ENEMY_PATTERN_MAX){


			//とりあえずの移動制御パターン
			int t=enemy->cnt;
			if(t==0){
				enemy->vx=3;
				enemy->vy=2;//下がってくる
			}
			if(t==60){
				enemy->vy=0;//止まる
				enemy->vx=0;
			}
			if(t==60+enemy->wait){//登録された時間だけ停滞して
				enemy->vy=-2;//上がっていく
			}


			enemy->x+=cos(enemy->ang)*enemy->sp;
			enemy->y+=sin(enemy->ang)*enemy->sp;
			enemy->x+=enemy->vx;
			enemy->y+=enemy->vy;
			enemy->cnt++;
			//enemy->img=(enemy->cnt%30)/11;//アニメーション用

			//敵が画面から外れたら消す
			if(enemy->x<-50 || F_M_X+20<enemy->x || enemy->y<-50 || F_M_Y+20<enemy->y){
				enemy->flag=0;
			}
			if(enemy->bltime==enemy->cnt){
				//ショット登録
				enter_shot(enemy->knd);
			}
		}
		else
			printfDx("enemy[i].patternの%d値が不正です。",enemy->pattern);

	}
}




void enemy_init(){
	memset(enemy,0,sizeof(enemy_t)*ENEMY_MAX);
	m_img=LoadGraph("キャラクター/雑魚1.png");
	for(int i=0;i<ENEMY_MAX;i++){
		enemy[i].img_enemy[0]=m_img;
	}
}


void enemy_act_all(){
	for(int i=0;i<ENEMY_ORDER_MAX;i++){
		enemy_act(&enemy[i]);
	}
}


void enemy_draw(){
	for(int i=0;i<ENEMY_MAX;i++){
		if(enemy[i].flag==1){
			DrawGraph(enemy[i].x+F_X,enemy[i].y+F_Y,enemy[i].img_enemy[0],TRUE);
		}
	}
}


void Enemy_Final(){
}

コード:

#include "DxLib.h"
#include "enemy_order.h"
#include "enemy.h"
#include "define.h"
#include "GV.h"


//enemymgr.cpp


struct enemy_order_t;
struct enemy_order_enum_info_t;
struct enemy_t;



//敵のロード、最初の初期化
void Enemy_Init(){
	load_enemy_order();
	enemy_init();
}





//敵をオーダー情報で初期化
void init_enemy_buffer(enemy_t *enemy,enemy_order_t *enemy_order){
	int i;
	enemy->flag   =1;//フラグ
	enemy->cnt    =0;//カウンタ
	enemy->pattern=enemy_order->pattern;//移動パターン
	enemy->back_col=GetRand(4);
	enemy->knd    =enemy_order->knd;//敵の種類
	enemy->x      =enemy_order->x;//座標
	enemy->y      =enemy_order->y;
	enemy->sp     =enemy_order->sp;//スピード
	enemy->bltime =enemy_order->bltime;//弾の発射時間
	enemy->blknd  =enemy_order->blknd;//弾幕の種類
	enemy->blknd2 =enemy_order->blknd2;//弾の種類
	enemy->col    =enemy_order->col;//色
	enemy->wait   =enemy_order->wait;//停滞時間
	enemy->hp     =enemy_order->hp;//体力
	enemy->hp_max =enemy->hp;//体力最大値
	enemy->vx     =0;//水平成分の速度
	enemy->vy     =0;//鉛直成分の速度
	enemy->ang    =0;//角度
	for(i=0; i<6; i++){
		enemy->item_n[i]=enemy_order->item_n[i];//落とすアイテム
	}

}





 //敵の登録
void Enemy_Enter(){
    enemy_order_t *enemy_order_ptr;
    enemy_order_enum_info_t *enemy_order_enum_info;
 
    // 指定のカウンタ値に合致する敵出現オーダーを列挙する準備手続き
    enemy_order_enum_info = prepare_enemy_order_enumeration(global_stage_cnt);
 
    // 敵キャラの出現オーダーから合致するものを列挙
    // 以下を列挙されたオーダーごとに繰り返す
    while (enemy_order_ptr = get_next_enemy_order(enemy_order_enum_info))
    {
        enemy_t *enemy_ptr;
        // 敵キャラ用のメモリ領域を新規に取得する
        enemy_ptr = get_enemy_buffer();
        if (enemy_ptr == NULL) {
            continue;
        }
        // 前段で取得した敵キャラ用のメモリ領域を初期化する
        init_enemy_buffer(enemy_ptr, enemy_order_ptr);
    }
 
    // 敵出現オーダー列挙の後始末手続き
    finish_enemy_order_enumeration(enemy_order_enum_info);
}




//敵の行動を制御したり、死んだかどうか判断したり、もろもろ
void Enemy_Main_Control(){
	//敵をうごかす
	enemy_act_all();
}
		



void Enemy_Draw(){
	enemy_draw();
}







//以下、最上層
void EnemyMgr_Init(){
	Enemy_Init();
	
}


void EnemyMgr_Update(){
	Enemy_Enter();
	Enemy_Main_Control();
	

}


void EnemyMgr_Draw(){
	Enemy_Draw();


}



void EnemyMgr_Final(){
}


また、大変見難いとは思うのですが、以下にエラーを列挙します。

・enemy_order.hのenemy_order_enum_info_t *prepare_enemy_order_enumeration(int cnt);を指して「';' が '*' の前にありません。」、「型

指定子がありません - int と仮定しました。」
・enemy.hのenemy_t *get_enemy_buffer();を指して、上記の二つと同じエラー

・enemymgr.cppのinit_enemy_bufferを指して「'enemy' : 定義されていない識別子です」、「'enemy_order' : 定義されていない識別子です。」
「'enemy_order_t' : この型は演算子として使用できません」、「'init_enemy_buffer' : 関数の定義が間違っています。」

・同じくenemymgr.cppの、struct enemy_order_t;を指して「'enemy_order_t' の宣言を確認してください。」

・同じくenemymgr.cppのEnemy_Enterで、「'enemy_order_enum_info' : 定義されていない識別子です」


これらのエラーを修復できないため、コンパイルを中止するというメッセージもでたため、まず上記のエラーをどうにかしたいのですが、
あれを直すと今度はこっちが、みたいな感じになってしまっています。
前方宣言や、関数の宣言のような宣言に問題があることはわかるのですが・・・。
また、init_enemy_bufferに関しては、どのモジュールに置くか悩んでおり、現状では公開されていないメンバにアクセスしている
ためのエラーもあるということだけはわかっています。
アドバイスをいただきたく、投稿しました。

Rittai_3D
記事: 525
登録日時: 12年前

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#128

投稿記事 by Rittai_3D » 10年前

enemy_order.h の中に enemy_order_enum_info_t と enemy_order_t の前方宣言を記入してください。
enemy_order.h は何もインクルードされていないので、enemy_order_enum_info_t や enemy_order_t が何なのか分かりません。
初心者です

ISLe()

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#129

投稿記事 by ISLe() » 10年前

今回は前方宣言が足りないだけのようですが、コードが増えてモジュールの中でソースファイルを分割したくなったとき…

公開用のヘッダファイルの名前がenemy_order.hだったら、
モジュール間でインクルードする公開しないヘッダファイルの名前はenemy_order_impl.hとかにして分けると良いです。
#impl(=implement:実装)


init_enemy_buffer関数については、以前書いたような気がしますが、enemy_tのほうがenemy_order_tより重要度が高い(変更されたときの影響が大きい)ので、どちらに置くかというのであればenemy_order.cppが相応しいのではないかと思います。
あるいは、上記したように、どちらでもない(両方の実装をインクルードする)ソースファイルに分割するという選択肢もあります。

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#130

投稿記事 by SDD » 10年前

3Dさん、ISLeさん
いつもアドバイスをいただき、ありがとうございます。
確かに言われてみればそうですね。hファイルは何もインクルードしてないから当然ですね・・・。

また、ISLeさんが仰った
ISLe() さんが書きました:
公開用のヘッダファイルの名前がenemy_order.hだったら、
モジュール間でインクルードする公開しないヘッダファイルの名前はenemy_order_impl.hとかにして分けると良いです。
#impl(=implement:実装)


init_enemy_buffer関数については、以前書いたような気がしますが、enemy_tのほうがenemy_order_tより重要度が高い(変更されたときの影響が大きい)ので、どちらに置くかというのであればenemy_order.cppが相応しいのではないかと思います。
あるいは、上記したように、どちらでもない(両方の実装をインクルードする)ソースファイルに分割するという選択肢もあります。
というのも、実際にやってみようと思います。公開されるべきものは然るべきところでは公開されるのですね。


ただ、とりあえず、オーダー周りの前方宣言をつけたのですが、

コード:

#ifndef DEF_ENEMY_ORDER_H
#define DEF_ENEMY_ORDER_H

//enemy_order.h


struct enemy_order_t;
struct enemy_order_enum_info_t;


void load_enemy_order();

enemy_order_enum_info_t *prepare_enemy_order_enumeration(int cnt);

enemy_order_t *get_next_enemy_order(enemy_order_enum_info_t *info);

void finish_enemy_order_enumeration(enemy_order_enum_info_t *info);

#endif

とすると、enemy_order_tの部分を指して「'enemy_order_t' の宣言を確認してください」と言われてしまいます・・・。
enemy_order_enum_info_tも同じことが言われるなら、共通の何か間違いがあるのだろうと思うのですが、こちらはなにも言われません。
色々いじくっては見たのですが、どうしても分かりませんでした・・・。

Rittai_3D
記事: 525
登録日時: 12年前

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#131

投稿記事 by Rittai_3D » 10年前

想像ですが、cppファイルの構造体が無名構造体なのではないでしょうか?

コード:

// a.h
struct Hoge_t; //前方宣言

// a.c
struct Hoge_t
{
    int val;
};
は出来てますが、

コード:

// a.h
struct Hoge_t; //前方宣言

// a.c
typedef struct
{
    int val;
}
Hoge_t;
は出来てませんよ。

参考http://www7b.biglobe.ne.jp/~robe/cpphtm ... 03012.html

前方宣言は関数のプロトタイプ宣言の構造体verみたいなものです。

外出先からなので短いですがこれで。
初心者です

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#132

投稿記事 by SDD » 10年前

3Dさん
お返事が遅れてしまい、すみません。
仰るとおりでした。以後気をつけます。今まではtyepdefを使っていたので、せっかくなので今後はそれをしないようにします。

この問題はあとはちゃんとモジュール間での公開、非公開を決め、
initの関数を然るべき場所に置いたら解決できると思います。
その際コードを載せます。(他にしなければいけないことがあり、最近これまでのコードをいじくれていないのが現状です・・・)


また、場をややこしくしてしまうかもしれませんが、気になることができ、質問があります。
名指しで失礼なのですが、ISLeさんは過去に「抽象化を進めるというのは、言わば「どれだけ短い言葉で内容を表すことができるようにするか」ってことです。
もちろん表現する言葉を無理矢理削るってことではなく、プログラムの中身を表現に合わせます。」と仰っていましたが、
今回それについて考えていました。

今回、敵関係の登録部分を主に、構造化、抽象化しましたが、たとえば、敵とはまったく関係ないモジュールを作り、
同じように、CSVを読み込み→登録するという流れのとき、仮にこの程度の長さの関数だった場合

コード:

void enter_hoge(){
	for(int i=0;i<m_total_なんちゃら;i++){
		if(なんちゃら==hoge_order[i].なんちゃら){{
				hoge[i].flag=hoge_order[i].;
				hoge[i].x=hoge_order[i].x;
				hoge[i].y=hoge_order[i].y;
				hoge[i].knd=hoge_order[i].knd;
			
		}
	}
}
また、そのモジュールをさらに分割する必要もなく(敵のようにショット関係の座標なり自機の座標なりを知るとかもなく)
、そのモジュールの変数の一切をその中でのみ使うとき、また、そのモジュール一つで足りるような場合、このような場合、上記のenter_hogeを無理に抽象化する必要も、同じように構造化する必要はないかな?と思ったのですが、間違った考えでしょうか。此処のところ、なかなかコードに触れる時間が取れず、大学の空いた時間にオライリーの「C実践プログラミング」を読んでいるのですが、必要以上にきっちりすると上記のような簡単なコードが逆にわかりづらくなってしまうかなと思ったことがきっかけで質問いたしました。

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#133

投稿記事 by usao » 10年前

ご自身でも
>…する必要はないかな?
>必要以上にきっちりすると…
と書かれていますが,
「何故必要なのか」という理由があるがあるからこそ「必要」が生まれてくるわけで,
そしたら「必要な分だけ」やれば良いのではないでしょうか.
オフトピック
(私みたいな不真面目な人から言わせると,ですが)
「ある処理を書くのに,その中身をどこまで細かく関数に分けますか?」
みたいなのは,一言で言えば 「その時々でちがう」 です.

どこまで分割しますか?
何を公開して,何を隠ぺいするようにしますか?
この関数はどのファイルに書きますか?

といったことには,常に 【動機】 があってしかるべきではないでしょうか.
「○○だから ここの部分は一個の関数にしておこう」とか,
「××だから このデータは外部から見えなくしておこう」だとか.

【だから→やる】  のであって,  【やる→なんで?どこまで?】  という順序になっているならば,逆だと思います.


「抽象化」 とか 「構造化」 とかいう小難しい単語が飛び交っていますが,
理由(動機)が 今現在取り立てて無い のであれば, そういったことに「今は」そんなにこだわらなくてもいいのでは…?
#何か期限的なものがあるような話だった記憶がありますが,そっち側は大丈夫だったのでしょうか

ISLe()

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#134

投稿記事 by ISLe() » 10年前

いちいちメンバごとに代入すること自体が効率悪いです。
構造体がひとつのオブジェクトなのですから、バラバラに扱うのは本来おかしいのですよね。

enemy_tとenemy_order_tの関係においても、
enemy_tのメンバとしてenemy_order_tがあって、
そのまま代入(むしろテーブルをポインタ参照)するほうがシンプルかつ効率が良いです。
それをやるには変化が激しいのでモジュール化が落ち着いてから取り組むべきかと思います。


トピックの主旨として、分け方をどうするかというのがあるはずで、その辺はやり過ぎなくらいでも良いかと思ってます。

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#135

投稿記事 by SDD » 10年前

ISLeさん
なるほど・・・。
たしかに敵のオーダーも敵も同じ敵の情報だから敵構造体→敵オーダー構造体
のほうがしっくりきます。
また、確かに、良いロジックを学びたいというものだから、とりあえずでもよりよいコードに
近づけることはこのトピックの本質ですね。

usaoさん
たしかに、以前ソフト屋さんに指摘されたように、動機と手段が順序が逆になった考えをしているかもしれません・・・。
最終的に僕がどうやっていくかは別の話になるのかもしれませんが、とりあえずやるところまでやろうと思います。
でも、usaoさんが仰ったこともよく留意します。


期限はまだまだ大丈夫なのですが、それとは関係なく大学がちょっとあっぷあっぷな感じで・・・。
しばらく訂正したコードを挙げたり、プログラミング関係のことをするのは難しそうです・・・。

ISLe()

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#136

投稿記事 by ISLe() » 10年前

No.132で想定しているのが何かは分かりませんが、enemy_tとenemy_order_tの関係はさほど親密ではないかもしれません。

オブジェクト指向でいうところのhas aの関係ではないのなら、むしろ内包しないほうが良いでしょう。
既にやっているように、現状としては、enemy_order_tを使ってenemy_tを初期化するモジュールとして独立させるほうが良さげです。

もちろん構造体単位で繋がっているほうが美しいですが、そうなると設計まで立ち返らなければいけません。
現時点でそこまで求めるのは厳しいのではないでしょうかね。

enemy_order_tとenemy_tの関係性について現状分析を進めるにしても、もう少し知識と経験を得てからのほうが良いかもしれませんし。

ISLe()

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#137

投稿記事 by ISLe() » 10年前

例えば、構造体の定義を変更すると、その定義を使っているソースファイルはすべてコンパイルし直さなければいけません。
モジュールに分割するのは、行数が多いか少ないかだけで決まるわけではありません。

メンバがひとつしかなくても、構造体でラッピングしておけば、将来その対象モジュール以外をコンパイルし直すことなく改変できます。
hoge_order_tの中身を使っているのがhoge_order.cppだけであれば、hoge.cppをコンパイルせずに済みます。

十万行・百万行レベルのプロジェクトになると、ビルド時間が数十分から一時間を超えるようなこともありますから、その影響はとても大きいのです。
プロジェクト全体で見たらビルドだけで何日という単位の時間を消費していることになります。
余裕のあるうちは休憩代わりなのですが忙しくなると…。

こういうのはたいへんなときになって、分けておけば良かった、と後悔するものです。
分けておいて無駄だったというのは、あったとしても軽微なことです。

SDD

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#138

投稿記事 by SDD » 10年前

長いこと放置してしまい、すみませんでした。

卒業に向け色々あったり、就活のこともあったりで、プログラムに手を向けられなくなってしまいました。

このトピックをいつまでも放置するわけにもいかず、とりあえず閉じさせて頂きたいと思います。

プログラムをするほど暇がまったくないということではないのですが、
ほかのことと比べると優先度がどうしても低く、今現在はモチベーションが上がらないという
こともあります・・・。

色々な方にお世話になったし、特にISLeさん、3Dさんを利用するだけ利用しておいて・・・という感じに
なってしまい、もうしわけなく思っています。


このトピックで教わったことが今の自分でどこまで本質を理解しながら活かせるかは正直わからないし、
初心者らしく実際に失敗していったほうが、ISLeさんから教わったことのありがたみがよくわかるのかもしれません。
現状の私ではusaoさんの仰るとおり、「こっちが正しいのか、ならやろう」→「でも、なんでだ?」という風な感じが
否めないのです。勉強不足を実感しています。



時間とモチベーションが回復したときには、また勉強させていただきたいと思います。
そのときは続きとしてURLを貼り、トピックを立てます。

今回はookamiさん、usaoさん、Tatuさん、みけCATさん、ソフト屋さん、3Dさん、
特にISLeさんには本当にお世話になりました。ありがとうございました。
また、不完全でトピックを閉じてしまい、申し訳ありません。

ISLe()

Re: 変数のカプセル化を守りながらのデータの受け渡しについて

#139

投稿記事 by ISLe() » 10年前

SDD さんが書きました:色々な方にお世話になったし、特にISLeさん、3Dさんを利用するだけ利用しておいて・・・という感じに
なってしまい、もうしわけなく思っています。
自分の考えを改めて見直すきっかけになるので、回答者としても有益です。
好きでやっていることなのでお気になさらずに。

閉鎖

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