STGで敵の種類ごとに処理を分ける方法について

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

STGで敵の種類ごとに処理を分ける方法について

#1

投稿記事 by ynym » 6年前

C言語+DXライブラリでシューティングゲームを作っているのですが、敵の種類ごとに処理を分けるにはどうしたらよいでしょうか。敵の情報についてはそれぞれ違う構造体を使おうと考えています。
具体的な方法としては、種類ごとに別々の関数をつくり、オーバーロードすることくらいしか思いつかないのですがこの方法でよいのでしょうか。ほかにも何かあれば教えていただけると有難いです。
  

アバター
usao
記事: 1565
登録日時: 6年前

Re: STGで敵の種類ごとに処理を分ける方法について

#2

投稿記事 by usao » 6年前

>敵の情報についてはそれぞれ違う構造体を使おうと考えています。
これは,敵の種類が3種類あったら,構造体も3種類作るということですか?
(だとしたら,種類ごとに全く保持すべきデータの種類や数が異なるということ?)

そこまで全く別々のデータ形式なのだとしたら,
種類ごとに分けたい処理 を個別の関数にするくらいしかないと思います.
その場合でも,関数の型をそろえておくと,関数ポインタを使うとかできると思います.

どうにかして関数を分けないでやりたいとかいう場合だと… うーん,例えば

コード:

struct EnemyInfo
{
   //共通の項目
    int HP;
    ....

    //この値で,敵の種類を表す
    int EnemyType;

    //共通の処理関数内で動きを異ならせるための何かしらのパラメタ
    int ParameterOfAlgorithm;
};
みたいな感じで済むようであれば,
EnemyType値で分岐するとか,
ParameterOfAlgorithm値を何らかの計算に用いることで結果として異なる動作結果になるようにする,とか?

ynym

Re: STGで敵の種類ごとに処理を分ける方法について

#3

投稿記事 by ynym » 6年前

回答ありがとうございます。

>種類ごとに分けたい処理 を個別の関数にするくらいしかないと思います.
やはり別々の構造体を作るとなるとこの方法が良いようですね。

しかし、この方法はあまり一般的ではないのでしょうか?
もし、全ての敵をひとつの構造体にまとめるとなると、敵の種類によって必要となるパラメータも異なるはずなので構造体自体がかなり複雑になってしまう気がします。
その辺はある程度目をつぶるということですか?

アバター
usao
記事: 1565
登録日時: 6年前

Re: STGで敵の種類ごとに処理を分ける方法について

#4

投稿記事 by usao » 6年前

私はSTGを本格的には作ったことないですけど,一般的?にはどうなんでしょう.
構造体の型自体を変えてしまうと,「複数の敵のインスタンス」を管理する方法がひどく面倒になりそうですが,どうなんでしょう.

敵の種類っていうのがどれだけのデータ多様性を生むのかわかりませんが
それほどに異なるデータ形式になるものなのでしょうか?
おそらく
{すべての敵で共通するデータ + 種類ごとの特殊なデータ} みたくなる気がします.

コード:

struct SpecialDataForEnemyA{ ... };
struct SpecialDataForEnemyB{ ... }:

struct EnemyData
{
    //共通データ
    int HP;
    ...
    //敵毎に異なるデータへのポインタ
    void *pSpecialData;  //このインスタンスの敵種類用のデータ.
  //種類ごとに分けたい処理
    void (*ProcFunc)( EnemyData * );  //このインスタンスの敵種類用の処理関数へのポインタ
};
のようにして,実際の処理関数の中では
pSpecialDataを実際の型にキャストして使うとかの方が,いくらかマシに思います.
for( すべての敵 ){ 処理 }
みたいなことが書けるので.

アバター
usao
記事: 1565
登録日時: 6年前

Re: STGで敵の種類ごとに処理を分ける方法について

#5

投稿記事 by usao » 6年前

うっかり送信したのでつづき.
こんな感じ.

コード:

//----------
//種類Aの敵の作成
    //(1)メモリ確保
EnemyData *pNewEnemy = (EnemyData*)malloc( sizeof(EnemyData) ); //※多分動的にmallocとかで用意する.
SpecialDataForEnemyA *pNewSPD = (SpacialDataForEnemyA)malloc( sizeof(SpecialDataForEnemyA) );
pNewEnemy->pSpecialData = pNewSPD;
    //(2)pNewSPDの内容をちゃんと準備する.
pNewSPD->XXX = ...;
    //(3)Aに合わせた処理関数を設定しておく
pNewEnemy->ProcFunc = MoveFuncOfEnemyA;
    //(4)例えば「今いる敵リスト」みたいなのにpNewEnemyを追加する.
AddToEnemyList( pNewEnemy );

//----------
//敵の移動処理をやるところ
for( すべての敵 pEnemyについて )  //例えば「今いる敵リスト」内のすべての敵について
{
    pEnemy->ProcFunc( pEnemy );  //インスタンスごとの処理関数を呼ぶ
}

//----------
//種類A用の移動処理関数
void MoveFuncOfEnemyA( EnemyData *pEnemy )
{
   //void*を適切な型にキャスト
    SpecialDataForEnemyA *pDataForA = ( SpecialDataForEnemyA* )( pEnemy->pSpecialData );
   //処理…
}

アバター
せんちゃ
記事: 50
登録日時: 9年前
住所: 江別市東野幌町
連絡を取る:

Re: STGで敵の種類ごとに処理を分ける方法について

#6

投稿記事 by せんちゃ » 6年前

クラスの機能を使うとまとめられるかなと思います。
具体的には敵の基底クラスを作ってあとは派生先で個別の実装処理を書いていく、という方法です。

コード:

class 敵基底 {
public :
	virtual void 生成時処理(){}
	virtual void あたり判定成立イベント(){}
	virtual void 死亡イベント(){}
	virtual void 更新処理実装(){}

	void 更新処理基底(){
		生成時処理();
		あたり判定成立イベント();
		死亡イベント();
		更新処理実装();
	}
	void 描画処理基底(){}
};



class 敵A : public 敵基底{
	敵A専用の処理があれば各関数に記述
};

class 敵B : public 敵基底{
	敵B専用の処理があれば各関数に記述
};

class 敵C : public 敵基底{
	敵C専用の処理があれば各関数に記述
};

class 敵D : public 敵基底{
	敵D専用の処理があれば各関数に記述
};


std::list<敵基底*> 敵生きてるリスト


敵生きてるリスト.push_back( new 敵A );
敵生きてるリスト.push_back( new 敵B );
敵生きてるリスト.push_back( new 敵C );
敵生きてるリスト.push_back( new 敵D );

for( std::list<敵基底*>::iterator 敵個別 = 敵生きてるリスト.begin() ; 敵個別 != 敵生きてるリスト.end() ; 敵個別++ ){
	(*敵個別)->更新処理()
	(*敵個別)->描画処理()
}

コード:

	virtual void 生成時処理(){}
	virtual void あたり判定成立イベント(){}
	virtual void 死亡イベント(){}
	virtual void 更新処理実装(){}
を継承先で実装する感じです。
どのタイミングで呼ぶべきかは考えてみましょう。

この辺の実装方法はアクションゲームなんかでも共通で、たとえばステージに配置されるギミックみたいなものも
このような方法で実装できます。
ほとんどデータみたいなものなのでスクリプトにしてあげることも可能らしいですが、
私は仕事でもプライベートでもスクリプトを使って実装したことがないのでその実装方法に関しては書けません。


この辺のロジックに対する考え方は「タスクシステム」で検索してみたり、
「弾幕 最強のシューティングゲームを作る(だったかな?)」
という技術書を買ってみると参考になるかもしれません。


P.S
日本語でクラス名とか関数名を書いたほうがわかりやすいかなと思ったのですが、
かえってわけわかんなくなった気がしております(;´・ω・)
ヽ(*゚д゚)ノ カイバー

ynym

Re: STGで敵の種類ごとに処理を分ける方法について

#7

投稿記事 by ynym » 6年前

>usaoさん
なるほど・・・。
{すべての敵で共通するデータ + 種類ごとの特殊なデータ} をこんな方法で実装できるんですね。私は関数へのポインタは使ったことが無いのでこんなスマートな方法があるとは思いませんでした。
C言語でもこんな処理ができるとはちょっと驚きです。これからしっかり関数へのポインタを勉強しようと思います。わかりやすい例をありがとうございました。

>せんちゃさん
回答ありがとうございます。
オブジェクト指向はまだ勉強中なので残念ながら今回はクラスを使う予定はありません。
ですがオブジェクト指向のトレーニングでそのうちまたシューティングを作ろうと思っているのでそこで参考にさせていただきます。やはりSTLも活用するべきですね。

アバター
usao
記事: 1565
登録日時: 6年前

Re: STGで敵の種類ごとに処理を分ける方法について

#8

投稿記事 by usao » 6年前

私が示した疑似コードは
>基底クラスを作ってあとは派生先で個別の実装処理を書いていく
…的な仕組みをCで自前で書いてみた感じのものです(C言語 とのことだったので).

 敵の情報をまとめるstruct → クラス
 敵の種類を問わない共通のデータ → 基底クラスのメンバ変数
 敵の種類ごとにことなるデータ(void *) → 派生クラスのメンバ変数
 敵の種類ごとに処理を分岐するための関数ポインタ → 仮想関数

みたいな感じで,C++ではこれを言語の機能でやってくれるわけですから,
「全てCで書く」とかいう縛りが特段ないのであれば
{クラス→継承→ポリフォリズム}あたりまで少し情報収集してみると今後の制作においてかなり楽ができると思います.

閉鎖

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