3

タイトルについて申し訳ありませんが、問題を説明するためのより良い方法を考えることができませんでした。基本的に、私はゲームに衝突システムを実装しようとしています。特定のタイプにキャストできる2つのオブジェクト(いずれかの順序で指定)の衝突を処理する「衝突ハンドラー」を登録できるようにしたいと思います。したがって、Player : Ship : EntityLaser : Particle : Entity、およびのハンドラーが(Ship, Particle)(Laser, Entity)衝突よりも登録されている場合は(Laser, Player)、両方のハンドラーに引数を正しい順序で通知する必要があり、の衝突で(Laser, Laser)は2番目のハンドラーのみに通知する必要があります。

コードスニペットは千の単語を言っているので、これが私が今していることです(ナイーブメソッド):

    public IObservable<Collision<T1, T2>> onCollisionsOf<T1, T2>()
        where T1 : Entity
        where T2 : Entity
    {
        Type t1 = typeof(T1);
        Type t2 = typeof(T2);
        Subject<Collision<T1, T2>> obs = new Subject<Collision<T1, T2>>();
        _onCollisionInternal += delegate(Entity obj1, Entity obj2)
        {
            if (t1.IsAssignableFrom(obj1.GetType()) && t2.IsAssignableFrom(obj2.GetType()))
                obs.OnNext(new Collision<T1, T2>((T1) obj1, (T2) obj2));
            else if (t1.IsAssignableFrom(obj2.GetType()) && t2.IsAssignableFrom(obj1.GetType()))
                obs.OnNext(new Collision<T1, T2>((T1) obj2, (T2) obj1));
        };
        return obs;
    }

ただし、この方法は非常に遅いため(測定可能です。これを実装した後、最大2 FPSを失いました)、これから数サイクル/割り当てを削減する方法を探しています。

私は(たとえば、1時間かけて実装し、そのようなばかであるために壁に頭をぶつけた)、ハッシュコードに基づいて型を並べ替え、辞書に入れて、各エントリを次のようにする方法を考えました。そのタイプのペアのハンドラーのリンクリストと、ハンドラーが引数の順序を逆にするかどうかをブール値で示します。残念ながら、これは派生型では機能しません。派生型が渡された場合、基本型についてサブスクライバーに通知されないためです。すべてのタイプペアを( 2回)チェックして一致するかどうかを確認するよりも良い方法を誰かが考えることができますか?

ありがとう、ロバート

4

3 に答える 3

3

そのすべての反射は速くなることはありません。衝突のたびに反射がどのように発生するかに注目してください。それが問題です。

いくつかの考え。

一番の考え:ビジターパターン。

オブジェクト指向言語で適度に高速な二重仮想ディスパッチを実装する標準的な方法は、ビジターパターンの実装を構築することです。

ビジターの各アクセプターが「この状況で行うこと」のリストを維持するビジターパターンを実装できますか?次に、adderメソッドは、新しい「やるべきこと」を書くための適切な場所を特定し、そのことにデリゲートを追加することで構成されます。衝突が発生すると、ビジターを開始してダブルディスパッチを実行し、実行することのデリゲートを見つけて呼び出します。

これまでに2回以上のディスパッチが必要ですか?効率的な複数仮想ディスパッチは実行可能ですが、それは正確には単純ではありません。

2番目の考え:動的ディスパッチ

C#4には動的ディスパッチがあります。それが機能する方法は、コールサイトに最初に遭遇したときにリフレクションを使用してコールサイトを分析することです。次に、新しいILを動的に生成して呼び出しを実行し、ILをキャッシュします。2番目の呼び出しでは、引数を振り返って、それらが以前とまったく同じタイプであるかどうかを確認します。そうである場合は、既存のILを再利用して、単にそれを呼び出します。そうでない場合は、分析を再度行います。引数が通常数種類しかない場合、キャッシュはすぐにヒットのみになり、ミスは発生しなくなります。実際、パフォーマンスはすべての点で非常に優れています。毎回たくさんの反射より確かに速い。毎回行う唯一の反映は、引数の実行時タイプの分析です。

3番目の考え:独自の動的ディスパッチを実装する

DLRが行っていることについて魔法のようなものは何もありません。一度分析を行い、ILを吐き出し、結果をキャッシュします。毎回分析をやり直しているので、痛みが起こっているのではないかと思います。

于 2010-03-03T00:25:56.773 に答える
1

それぞれEntityにエンティティタイプを識別するプロパティがあり、そのプロパティの値を取得しただけの場合、それは高速ではないでしょうか。

次に、ハンドラー内のエンティティの順序に規則がある場合、レーザーは常にプレーヤーの前に配置されます。

エンティティタイプはandenumであり、enumorderはハンドラオブジェクトorderである可能性があります。

public IObservable<Collision<T1, T2>> onCollisionsOf<T1, T2>()
    where T1 : Entity
    where T2 : Entity
{
    EntityTypeEnum t1 = T1.EntityType;
    EntityTypeEnum t2 = T2.EntityType;

    Subject<Collision<T1, T2>> obs = new Subject<Collision<T1, T2>>();
    _onCollisionInternal += delegate(Entity obj1, Entity obj2)
    {
       if (t1 < t2)
           obs.OnNext(new Collision<T1, T2>((T1) obj1, (T2) obj2));
       else
           obs.OnNext(new Collision<T1, T2>((T1) obj2, (T2) obj1));
    };
    return obs;
}
于 2010-03-03T00:18:59.727 に答える
0

私はあなたが追いかけている衝突の2つのセットがあると仮定しています:レーザー/プレーヤーとレーザー/レーザー。IObservable <Collision <T1、T2 >>をこれらの2つのケースに一致させる場合は、デリゲートを1つのチェックに減らし、比較のためにすべてを強く型付けすることができます。

_onCollisionInternal += delegate(T1 obj1, T2 obj2) {
  obs.OnNext(new Collision<T1, T2>( obj1, obj2));
};


public IObservable<Collision<T1, T2>> onCollisionsOf<T1, T2>()
        where T1 : Entity
        where T2 : Entity
    {
        Subject<Collision<T1, T2>> obs = new Subject<Collision<T1, T2>>();
        _onCollisionInternal += delegate(T1 obj1, T2 obj2) {
           obs.OnNext(new Collision<T1, T2>( obj1, obj2));
        };
        return obs;
    }

Observable<Collision<Laser, Player>> lp = onCollisionsOf<Laser, Player>();
Observable<Collision<Laser, Laser>> ll = onCollisionsOf<Laser, Laser>();
于 2010-03-03T00:27:35.400 に答える