多くの小さなポリモーフィッククラス(プロパティ、メッセージ、イベントとして使用)の柔軟な代替手段C ++


9

私のゲームには本当に役立つ2つのクラスがありますが、徐々に面倒になっていきます。メッセージとプロパティ(プロパティは基本的にコンポーネントです)。

どちらも基本クラスから派生しており、静的IDが含まれているため、システムは必要なものにのみ注意を払うことができます。それは非常にうまく機能しています...を除いて...

ゲームを拡張するにつれて、常に新しいメッセージタイプとプロパティタイプを作成しています。2つのファイル(hppとcpp)と大量のボイラープレートを作成する必要があるたびに、本質的にclassIDと1つまたは2つの標準データ型またはポインターを取得します。

新しいアイデアを試したり試したりすることが実際の雑用になり始めています。新しいメッセージまたはプロパティタイプを作成したいときは、次のように入力できるようにしたい

ShootableProperty:  int gunType, float shotspeed;

ItemCollectedMessage:  int itemType;

ヘッダーとcppファイルを作成する代わりに、親クラスなどを含むコンストラクターを作成します。

私の心の中で論理的に1行か2行ということを行うには、20行から40行(ガードやその他すべてを含む)程度です。

これを回避するためのプログラミングパターンはありますか?

(私は何も知らない)スクリプトについてはどうですか?ほとんど同じである一連のクラスを定義する方法はありますか?


以下は、1つのクラスの外観です。

// Velocity.h

#ifndef VELOCITY_H_
#define VELOCITY_H_

#include "Properties.h"

#include <SFML/System/Vector2.hpp>

namespace LaB
{
namespace P
{

class Velocity: public LaB::Property
{
public:
    static const PropertyID id;

    Velocity(float vx = 0.0, float vy = 0.0)
    : velocity(vx,vy) {}
    Velocity(sf::Vector2f v) : velocity(v) {};

    sf::Vector2f velocity;
};

} /* namespace P */
} /* namespace LaB */
#endif /* LaB::P_VELOCITY_H_ */



// Velocity.cpp

#include "Properties/Velocity.h"

namespace LaB
{
namespace P
{

const PropertyID Velocity::id = Property::registerID();

} /* namespace P */
} /* namespace LaB */

これらすべては、2Dベクトルと、2Dベクトルを期待すると言っているIDのためのものです。(確かに、一部のプロパティにはより複雑な要素がありますが、同じ考え方です)


3
これを見てください、それはあなたを助けるかもしれません。gameprogrammingpatterns.com/type-object.html

1
これはすべて、静的な初期化ではなく、テンプレートが役立つように見えます。この場合は確かにはるかに簡単になりますが、これらのIDで何をしたいのか、そしてなぜ継承を使用しているのかについての詳細な情報がないとわかりません。パブリック継承を使用しても仮想関数を使用しないことは、コードの匂いです。これは多態性ではありません!
ltjax 2013

それを実装するサードパーティのライブラリを見つけましたか?
ボリス

回答:


1

C ++は強力ですが、冗長です。必要なものが、すべてが異なる小さなポリモーフィッククラスである場合は、はい、そうです。宣言と定義には多くのソースコードが必要になります。本当に何もする必要はありません。

さて、ltjaxが言ったように、ここで行っていることは、少なくとも提供したコードについては、厳密にはポリモーフィズムではありません。サブクラスの特定の実装を隠す共通のインターフェースが見えません。多分クラスIDを除いて、これは実際には実際のクラスIDと冗長です:名前です。これは、いくつかのデータを含むクラスの集まりのようであり、実際には複雑ではありません。

しかし、これで問題が解決するわけではありません。最小限のコードで多くのメッセージとプロパティを作成したいとします。コードが少ないほどバグが少ないので、これは賢明な決定です。残念ながら、単一の解決策はありません。これらのメッセージやプロパティで何をするつもりなのかを正確に把握していなければ、何があなたのニーズに最も適しているかを見分けるのは困難です。だから私はあなたのオプションを公開しておきましょう:

  1. C ++テンプレートを使用します。テンプレートは、コンパイラーに「コードを記述」させるための優れた方法です。C ++の世界では、言語が進化してそれらをよりよくサポートするようになっているため、この変数はより顕著になっています(たとえば、可変数のテンプレートパラメーターを使用できるようになりました)。テンプレートによるコードの自動生成に特化した専門分野がすべてあります:テンプレートメタプログラミングです。問題は、それが難しいことです。このような手法を使用する場合は、プログラマー以外が新しいプロパティを自分で追加できると期待しないでください。

  2. 旧式のCマクロを使用します。それは古い学校であり、乱用しやすく、エラーを起こしやすい。作成も非常に簡単です。マクロは、プリプロセッサによって実行される見栄えのよいコピーと貼り付けなので、実際にはほとんど同じで、バリエーションがほとんどないものを大量に作成するのに非常に適しています。ただし、マクロはプログラム設計全体の欠陥を隠すためによく使用されるため、他のプログラマーがそれらを使用することを愛することを期待しないでください。ただし、役立つ場合もあります。

  3. 別のオプションは、外部ツールを使用してコードを生成することです。これは前の回答ですでに言及さているので、ここでは詳しく説明しません。

  4. 冗長性が低く、より簡単にクラスを作成できる言語は他にもあります。そのため既にスクリプトバインディングがある場合(またはそれらを使用する予定の場合)、それらのクラスをスクリプトで定義することもできます。ただし、C ++からそれらにアクセスするのは非常に複雑であり、クラスを簡単に作成することの利点のほとんどが失われます。したがって、これはおそらく実行可能なのは、ゲームロジックのほとんどを別の言語で実行することを計画している場合のみです。

  5. 最後に重要なことですが、データドリブンデザインの使用を検討する必要があります。自分で提案したものと同様のレイアウトを使用して、これらの「クラス」を単純なテキストファイルで定義できます。カスタムフォーマットとパーサーを作成するか、すでに利用可能ないくつかのオプション(.ini、XML、JSONなど)のいずれかを使用する必要があります。次に、C ++側で、これらの異なる種類のオブジェクトのコレクションをサポートするシステムを作成する必要があります。これは、スクリプトアプローチとほぼ同じです(おそらく、さらに多くの作業が必要になります)。ただし、ニーズに合わせてより正確に調整できる点が異なります。そして、それを十分に単純にすると、プログラマーではない人が自分で新しいものを作成できる可能性があります。


0

あなたを助けるコード生成ツールはどうですか?

たとえば、メッセージタイプとメンバーを小さなテキストファイルで定義し、コード生成ツールで解析して、すべてのボイラープレートC ++ファイルをビルド前のステップとして書き込むことができます。

ANTLRやLEX / YACCなどの既存のソリューションがあり、プロパティとメッセージの複雑さによっては、独自のソリューションを導入することは難しくありません。


LEX / YACCアプローチの代わりに、ほんのわずかな変更で非常に類似したファイルを生成する必要がある場合、シンプルで小さなpythonスクリプトとタグを含むC ++コードテンプレートファイルを使用します。Pythonスクリプトは、テンプレート内でこれらのタグを検索し、作成される要素の代表的なトークンに置き換えます。
ビクター2013

0

Googleのプロトコルバッファ(http://code.google.com/p/protobuf/)について調べることをお勧めします。これらは一般的なメッセージを処理するための非常に賢い方法です。構造体のようなパターンでプロパティを指定するだけで、コードジェネレーターがクラス(Java、C ++、またはC#)を生成します。生成されたすべてのクラスにはテキストパーサーとバイナリパーサーがあり、テキストベースのメッセージの初期化とシリアル化の両方に適しています。


私のメッセージングセントラルは私のコアプログラムです-プロトコルバッファーが大きなオーバーヘッドを引き起こすかどうか知っていますか?
ブライアン

APIは各メッセージのビルダークラスであり、メッセージ自体のクラスであるため、大きなオーバーヘッドは発生しないと思います。Googleはそれらをインフラストラクチャのコアとして使用します。たとえば、Google App Engineエンティティはすべて、永続化される前にプロトコルバッファに変換されます。疑問がある場合は、現在の実装とプロトコルバッファーの比較テストを実装することをお勧めします。オーバーヘッドが許容できると思われる場合は、それらを使用してください。
dsilva.vinicius 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.