C ++でゲームエンジンを整理する方法 私の継承の使用は良い考えですか?


11

私はゲーム開発とプログラミングの両方の初心者です。

ゲームエンジンの構築に関する原則を学びたいと思っています。
シンプルなゲームを作成したいのですが、ゲームエンジンを実装しようとしているところです。

だから私は私のゲームエンジンがこれを制御する必要があると思った:

- Moving the objects in the scene
- Checking the collisions
- Adjusting movements based on collisions
- Passing the polygons to the rendering engine

私は私のオブジェクトを次のように設計しました:

class GlObject{
private:
    idEnum ObjId;
    //other identifiers
public:
    void move_obj(); //the movements are the same for all the objects (nextpos = pos + vel)
    void rotate_obj(); //rotations are the same for every objects too
    virtual Polygon generate_mesh() = 0; // the polygons are different
}

ゲームには4種類のオブジェクトがあります。飛行機、障害物、プレイヤー、弾丸で、次のように設計しました。

class Player : public GlObject{ 
private:
    std::string name;
public:
    Player();
    Bullet fire() const; //this method is unique to the class player
    void generate_mesh();
}

ここで、ゲームエンジンで、たとえば衝突やオブジェクトの移動などを確認できる一般的なオブジェクトリストを作成しますが、ゲームエンジンがユーザーコマンドを使用してプレーヤーを制御することも必要です...

これは良いアイデアですか?

class GameEngine{
private:
    std::vector<GlObject*> objects; //an array containg all the object present
    Player* hPlayer; //hPlayer is to be read as human player, and is a pointer to hold the reference to an object inside the array
public:
    GameEngine();
    //other stuff
}

GameEngineコンストラクターは次のようになります。

GameEngine::GameEngine(){
    hPlayer = new Player;
    objects.push_back(hPlayer);
}

ポインターを使用してfire()いるのは、Playerオブジェクトに固有のを呼び出す必要があるためです。

だから私の質問は:それは良いアイデアですか?私の継承の使用はここで間違っていますか?



投稿後に見たことがありますが、実際には作文は素晴らしいアイデアです。実際のc ++デザインではなく、クラスでcを使用することを常に恐れています。
Pella86 2012年

継承よりもコンポジションを使用する理由は他にもたくさんあると思います... 私の回答は、それが提供する柔軟性の利点に関するいくつかの例を示しています。実装の良い例については、私がリンクした記事をチェックしてください。
コヨーテ2012

回答:


12

あなたが「良い」とはどういう意味かによる。それは確かに仕事を成し遂げるでしょう。それには多くの利点と欠点があります。エンジンがはるかに複雑になるまで、それらは見つかりません。この件に関する議論については、SOこの質問を参照してください。

どちらの方法でも多くの意見がありますが、エンジンコードがまだその初期段階にある場合、なぜそれが悪いのかを説明するのは難しいでしょう。

一般的に、スコット・マイヤーズには、継承について素晴らしいことをいくつか述べています。まだ開発の段階にある場合は、続行する前にこの本(Effective C ++)をお読みください。それは素晴らしい本であり、私たちの会社で働いているどんなコーダーにとっても必要条件です。ただし、アドバイスを要約すると、クラスを作成して継承の使用を検討する場合は、クラスとその祖先の関係が「is a」関係であることを完全に明確にしてください。つまり、すべてのBはAです。Aが提供する機能へのアクセスをBに与える簡単な方法として継承を使用するだけでなく、その方法は深刻なアーキテクチャ上の問題につながるだけでなく、他の方法もあります。

簡単に言えば、任意のサイズのエンジンの場合、オブジェクトが継承が機能するきちんとした階層構造になることはほとんどありません。それを適切なときに使用しますが、すべてに使用するという罠に陥らないでください。オブジェクトを相互に関連付ける他の方法を学んでください。


相続についての意見です。私の個人的な経験則は次のとおりです。あなたする必要があるか、他の方法でそれをするのが愚かでない限り、それをしないでください。つまり、99%のケースで継承は悪い考えです(少なくとも:)。また、ゲームロジック(回転/移動)をレンダリング(generate_mesh())から分離することもできます。もう1つはfire()です-アクションのリストと一般的なaction(my_action)関数を用意することを検討してください-このようにすると、すべてのクラスに分散する膨大な数の異なる関数になってしまうことはありません。どちらの場合も、問題を見つけたときに単純にして、冷酷にリファクタリングしてください。
ギリアド、2012年

8

ゲームオブジェクト(エンティティ)のロジックデータと機能については、継承よりもコンポジションを使用することを強くお勧めします。良いスタートは、コンポーネントベースのアプローチでしょう。これはよくに記述されている。このカウボーイプログラミングの記事このアーキテクチャを実装する方法について説明し、どのようにそれはトニーホークのフランチャイズを恩恵を受けました。

また、エンティティシステムに関するこの記事は、最初に読んだときに私に影響を与えました。

継承は、ポリモーフィズムの優れたツールです。非常に単純なプロジェクトを除いて、エンティティとゲームロジックを構造化する方法として使用することは避けてください。継承を使用すると、共通の親から継承できない関数を実装するために、同じコードのコピーを複製して維持する必要が生じることになります。また、コードの複製を避けるために最も一般的に使用されるロジックとデータでクラスを汚染し始めるでしょう。

コンポーネントは多くの状況で非常に便利です。あなたはそれらを使用してもっと多くを達成することができます:

エンティティタイプの柔軟性:ゲーム開発では、一部のエンティティが突然進化し、ゲームデザイナが開発者がそれを実装する必要があると判断する場合があります。コードを複製せずに既存の動作を追加するには、ほんの数ステップしかかかりません。コンポーネントベースのシステムでは、プログラマ以外のユーザーが行う変更を増やすことができます。たとえば、クラスによって表されるのではなく、エンティティの各タイプは、タイプを構成するために必要なすべてのコンポーネント名とパラメーターを含む記述ファイルによって表すことができます。これにより、プログラマーではない人でも、ゲームを再コンパイルせずに変更をテストできます。記述ファイルを編集するだけで、各エンティティタイプで使用されるコンポーネントを追加、削除、または変更できます。

特定のインスタンスと特定のシナリオでの柔軟性:プレイヤーが武器やアイテムを装備できるようにする必要がある場合があります。プレイヤーにインベントリコンポーネントを追加できます。ゲームの開発中は、他のエンティティにインベントリ(たとえば敵)が必要になります。そして、ある時点で、あなたのシナリオでは、ツリーが何らかの種類の隠しアイテムを含むことになっているという状況があります。コンポーネントを使用すると、追加のプログラミング作業を行わずにインベントリコンポーネントを本来持つように設計されていないエンティティにも、インベントリコンポーネントを追加できます。

実行時の柔軟性:コンポーネントは、実行時のエンティティの動作を変更する非常に効果的な方法を提供します。実行時にコンポーネントを追加および削除できるため、必要に応じて動作やプロパティを作成または削除できます。また、複数のGPUで並列に個別に計算できる(場合によっては)異なるタイプのデータをグループに分けることもできます。

時間の柔軟性:バージョン間の互換性を維持するためにコンポーネントを使用できます。たとえば、ゲームのリリース間で変更が加えられた場合、(常にではありませんが)新しい動作と変更を実装する新しいコンポーネントを単に追加できます。次に、ゲームは、ロードされた保存ファイル、ピア接続、またはプレーヤーが参加するサーバーに応じて、エンティティにアタッチされた適切なコンポーネントをインスタンス化します。これは、すべてのプラットフォーム(Steam、Mac App Store、iPhone、Android ...)で新しいバージョンのリリース日を制御できないシナリオで非常に便利です。

より良い洞察を得るために、[コンポーネントベース][エンティティシステム]のタグ付き質問を見てください。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.