2

以前にも同様の質問をいくつかしたと思いますが、私は茂みの周りを叩いていました。これは、私が完全に休むことができない本当の問題だと思います。

サード パーティのライブラリを扱っていますが、それ自体を作成できないオブジェクトがありますb2Bodyインスタンス化する必要b2Worldあります。個人的には、このデザイン パターンはあまり好きではありません。は世界から独立して存在し、必要に応じて世界に追加できるようにすべきだと思います。とにかく、何か余分なものを追加する必要があるため、独自のクラス class でラップしました。同様に、ラッパーがあります。今、私には3つのオプションがあると思います:b2Bodyb2BodyBodyWorld

  1. Haveのコンストラクターは、完全にインスタンス化できる (内部のどこかを呼び出す) ためBodyのポインターを受け取ります。つまり、次のようなコンストラクターがあります。Worldb2World::CreateBodyBody *b = new Body(world_ptr)
  2. ライブラリがすでに行っているように、Bodyいくつかのメソッドに渡します-つまりWorld::CreateBodyBody *b = world.CreateBody(params);
  3. すべてのデータを複製して、b2Body必要に応じて使用できるようにします。ワールドに追加した後、データを使用するように「切り替え」b2Bodyます。Body bworld.addBody(b)

Body(1) と (2) は、 aなしでは a を持てないことを意味しますがWorld、これはおそらく必要ないでしょうが、そのオプションがあるといいかもしれません [他のオブジェクトなどのテンプレートとして使用できるように] ]。他にどんな長所と短所があるのか​​ わかりません。(3) の方が優れているように見えますが、実装するのに手間がかかります。これは、既に に含まれているほとんどのデータを複製する必要があることを意味しb2Bodyます。

あなたの考えは何ですか?CW誰も心配しないように、私はこれをします。


私はまだこれを休ませることができません。各オプションは次のようになります。

オプション 1: (私が好むもの)

World w;
Body b;
Fixture f;
b.addFixture(f);
w.addBody(b);

オプション 2: (中間のどこか)

World w;
Body b(w);
Fixture f(b);

オプション 3: (Box2D の方法)

World *w = new World;
Body *b = w->CreateBody(args);
Fixture *f = b->CreateFixture(args);

オプション 2 と 3 はそれほど違いはありませんが、オブジェクトの作成を制御できる人が変わります。

オプション3を実際に実装するにはどうすればよいですか?World::CreateBody()whichをb2World::CreateBody(args)呼び出しb2Body::b2Body()て返す必要b2Bodyがありますが、Body::Body(args)これは問題です。はb2Body完全に初期化されますが、ラッパーにはそれを行う場所がありません...より具体的には、どのように記述しWorld::CreateBody(const BodyDef &bd)ますか? BodyDef は b2BodyDef から、Body は b2Body から、World は b2World から、などと仮定します。

4

5 に答える 5

6

サードパーティのライブラリを使用する場合は、その設計パターンがあまり好きではない. あなたのライブラリには、どうやらファクトリ オブジェクトを使用して処理を行う方法があり、コードの複雑さを大幅に増加させる可能性があります。

于 2009-09-27T08:07:50.943 に答える
2

b2World オブジェクトは b2Body のファクトリのように聞こえるため、作成者は b2Body はそのワールドなしでは意味がないと判断しました。

私の最初の反応は、これがインターフェースなので、それと一緒に暮らすということです。World オブジェクトを Body のファクトリにします。これはアプローチ (1) に近いですが、パブリック コンストラクターがないことを除けば、World オブジェクトには makeBody() メソッドがあります。

世界のない身体は理にかなっていると思いますか? もしそうなら、おそらくあなたが見つけたのは、BodyメソッドのいくつかのサブセットがWorldなしで役立つ可能性があるということです。それらをどのように実装するかはわかりません.b2Worldなしでは存在できないため、b2Bodyでは明らかに実装できません. . したがって、1つの可能性は、一連の構成情報があることです

 class Body {
        int howBig;
        String name;
        Flavour flavour;
        // and getter/setters
 } 

現在、これら (または少なくとも bgetter) は明らかに、World の有無にかかわらず意味を成す可能性があります。

それを念頭に置いて、実際には Body の 2 つの「状態」があることに気付くと思います。1 つは World に関連付けられていないとき、もう 1 つは関連付けられているときです。実際の機能は異なります。したがって、実際には 2 つのインターフェイスがあります。

したがって、IndependentBody クラスと Body クラスがあります。World ファクトリ メソッドには署名がある場合があります

World {

    Body makeBody(IndependentBody);

}
于 2009-09-27T08:01:44.930 に答える
1

box2DがbodyDefオブジェクトを使用してb2Bodyオブジェクトを作成する理由の1つは、defを再利用して複数のボディを作成できるようにするためです。次のようなコード:

b2BodyDef myDef;
// fill out def

for (int i=0; i < 100; ++i) {
   for (int j=0; j < 100; ++j) {
      myDef.x = i;
      myDef.y = j
      b2Body* body = world.CreateBody(myDef)
   }
}

同じ特性を持つ多くのオブジェクトを作成するための非常に効率的でコンパクトな方法です。同じループ内にある必要はありません。defオブジェクトをメタデータとして保持し、必要に応じてそれらからボディを作成できます。

それは理由があるのでそれと戦わないでください。

于 2009-10-08T17:38:32.473 に答える
1

使用しているサードパーティのライブラリの設計と戦うべきではないことに同意します。そのような道を進むことは、将来多くの問題を引き起こす可能性があります。

「裏で」見てラッパーを作成することで、サードパーティライブラリの動作を現在の実装の動作に固定している可能性があります。

APIの将来のバージョンが同じままで、基になるセマンティクスが変更された場合はどうなりますか?

突然、ラッパーの観点からすべてが壊れます。

ちょうど私の0.02。

于 2009-09-27T09:02:42.487 に答える
1

あなたのリンクをcreateBodyたどると、それはb2Bodyを返さないが、1つへのポインタを返すことがわかります。

 b2Body* b2World::CreateBody  ( const b2BodyDef*  def );     

これはおそらくb2Worldが原因です

  1. b2Bodyの存続期間を管理します(つまり、B2Bodyの存続期間と、B2Worldがスコープ外になる/それ自体が削除されたときに使用するメモリを削除します)。

  2. B2Wsorldはb2Bodiesへのポインターを維持する必要があるため、たとえば、B2Worldの機能を実行するためにそれらを反復処理する必要があります。

b2Worldまた、を作成するために必要なのは(以外)、へb2Bodyのポインタだけであることに注意してくださいb2BodyDef

したがって、b2Worldにアタッチされていないが後でアタッチできるb2Bodyが必要な場合は、b2BodyDefsまたはそれらへのポインターを渡してみませんか?

b2BodyDefの薄いラッパーを作成する場合があります。

 class b2BodyDefWrapper {
   public const b2BodyDef& b2bodyDef;
   public b2BodyDefWrapper( const b2BodyDef& bodydef ) : b2bodyDef(bodydef) {}
   public const b2Body* reifyOn( b2World& world) const { 
     return world.CreateBody( b2bodyDef ) ;
   }
 }

このb2BodyDefWrapperを複数のワールドにアタッチしたり、同じワールドに複数回アタッチしたりできることに注意してください。

これで、b2BodyDefに対しては実行できないことを、b2Bodyに対して実行できる可能性があります。そのため、b2BodyDefsを渡す(ラップされている可能性があります)ことが目的に合わない場合があります。この場合、コマンドパターンを使用して、関数のリストをに「アタッチ」するb2BodyDefWrapperことができます。これは、各reifiedb2Bodyで「再生」されます。

 class b2BodyDefWrapper {
   private std::vector<Command&> commandStack;
   public const b2BodyDef& b2bodyDef;
   public b2BodyDefWrapper( const b2BodyDef& bodydef ) : b2bodyDef(bodydef) {}
   public const b2Body* reify( b2World& world) const { 
     b2body* ret = world.CreateBody( &b2bodyDef ) ;
     for (int i=0; i< commandStack.size(); i++) {
        v[i].applyTo( ret ) ;
     }
     return ret;
   }

   public void addCommand( const Command& command ) {
      commandStack.push_back( command );
   }
 }

Command次のようなFunctorsの抽象基本クラスはどこにありますか。

  class Command {
     virtual ~Command() {}
     virtual void applyTo( b2Body* body ) = 0 ;
  }

具体的なサブクラスの場合:

 class ApplyForce : public Command {
   private const b2Vec2& force;
   private const b2Vec2& point;
   ApplyForce(const b2Vec2& f, const b2Vec2& p) : force(f), point(p) {}
   virtual void applyTo( b2Body* body ) {
      body->ApplyForce( force, point ) ;
   }
 }

次に、次のようにラッパーを使用できます。

extern b2BodyDef& makeb2BodyDef();
b2BodyDefWrapper w( makeb2BodyDef()  ) ; 
ApplyForce a( ..., ... );
w.addCommand( a ) ;
...
b2World myworld;
b2World hisWorld;
w.reifyOn( myWorld ) ;
w.reifyOn( hisWorld) ;

主にオブジェクトの所有権とメモリ管理、およびCommandStacksでdeleteを呼び出す人に関する詳細をいくつか省略していることに注意してください。また、クラスのスケッチでは3のルールに従わなかった。あなたは好きなようにこれらを記入することができます。

また、コマンドから、void以外を返すb2Body関数を呼び出して、それらの値を返すためのプロビジョニングも省略しました。ApplyToにある種のユニオンを返すようにすることで、おそらくこれをカバーできます(必要な場合)。

より基本的には、ある具体的なコマンドがその戻り値(存在する場合)を別の具体的なコマンドに提供する方法については説明していません。完全な解決策は、コマンドのベクトルではなく、子コマンドが最初に適用され、それらの戻り値(存在する場合)が親コマンドに提供される、それらのn- aryツリーを持つことです。そのような複雑さが必要かどうかは、私が明らかに答えられない質問です。(そして、コミュニティWikiがこの質問をしたので、私はこれに対して報酬を受け取っておらず、評判ポイントも得ていないと考えて、すでにかなり詳細な回答をしました。)

于 2009-09-27T09:32:40.913 に答える