2012-09-11 8 views
6

私は、私が意味するものの例として、私のコードで何かの事例研究を使用します。.NETのインターフェイス継承/階層 - より良い方法がありますか?

今、コンポーネントベースのゲームシステムの動作/アクションシステムに取り組んでいます。ゲームオブジェクトにのみ関連する詳細情報を示し、IBehaviorComponent及びそれらに添付IActionComponentを持つことができ、以下の公開:

public interface IBehaviorComponent : IBaseComponent 
{ 
    IBehavior Behavior { get; } 
} 

public interface IActionComponent : IBaseComponent 
{ 
    IAction Action { get; } 
    void ExecuteAction(IGameObject target = null); 
} 

は今、これはこれまでのところ、すべての罰金です(少なくとも私には!)。しかし、IActionComponentの実装を見ると、問題が発生し始めます。例えば

、シンプルIActionComponent実装:

public class SimpleActionComponent : IActionComponent 
{ 
    public IAction Action { get; protected set; } 

    public void ExecuteAction(IGameObject target = null) 
    { 
     Action.Execute(target); 
    } 

    public void Update(float deltaTime) { } //from IBaseComponent 
} 

しかし、のは、私はアクションは、時限スケジュール通りに実行されるようにすることができます、より複雑なIActionComponentの実装を紹介したいとしましょう:

public class TimedActionComponent : IActionComponent 
{ 
    public IAction Action { get; protected set; } 

    public float IdleTime { get; set; } 

    public float IdleTimeRemaining { get; protected set; } 

    public void ExecuteAction(IGameObject target = null) 
    { 
     IdleTimeRemaining = IdleTime; 
    } 

    public void Update(float deltaTime) 
    { 
     if (IdleTimeRemaining > 0f) 
     { 
     IdleTimeRemaining -= deltaTime; 
     } 
     else 
     { 
     Action.Execute(target); 
     } 
    } 
} 

今、IdleTimeを外部の影響によって変更できるように公開したいとします。

public interface ITimedActionComponent : IActionComponent 
{ 
    float IdleTime { get; set; } 

    float IdleTimeRemaining { get; set; } 
} 

しかし、ここでの問題は私のコンポーネントシステムはIBaseComponentから1レベルアップ時にすべてを保存することです:最初に私の考えは、新しいインターフェイスを作成していました。そのため、GameObjectのアクションコンポーネントは、ITimedActionComponent、IRandomizedActionComponent、ICrashTheProgramActionComponentなど、IActionComponentとして取得されます。基本的なタイプのコンポーネント(IActionComponent、IRenderableComponent、IPhysicsComponentなど)を超えて、必要なものを正確に知らなくても、そのコンポーネントの1つに対してGameObjectを照会することができるようにするために、理由は明らかです。

これを処理するよりクリーンな方法があります。これにより、取得したIActionComponentを興味のあるタイプにキャストすることなく、子クラスで定義されたこれらのプロパティを公開することができます。それとも、これを達成するための唯一の/最良の方法です。ような何か:

public void IWantYouToAttackSuperSlow(IGameObject target) 
{ 
    //Let's get the action component for the gameobject and test if it's an ITimedActionComponent... 
    ITimedActionComponent actionComponent = target.GetComponent<IActionComponent>() as ITimedActionComponent; 

    if (actionComponent != null) //We're okay! 
    { 
     actionComponent.IdleTime = int.MaxValue; //Mwhaha 
    } 
} 

は、今私はそれが唯一の方法だ考えていますが、私は無知だ木工品の中に隠れたパターンがあります場合、私は見たい、または誰でも提案することができた場合に考え出しこれを始めるにはもっと良い方法です。

ありがとうございます!

+0

私の他の思想は、これでしょう、IActionComponentExecutorとして、内部に別のオブジェクトを使用していましたAction.Execute()を呼び出すタイミングを決定しますが、私は丸いa-boutの方法で正方形に戻ってきます - 外部オブジェクトはIActionComponentTimedExecutorを追跡する必要があります(ああ〜私たちは今エンターテイメントを取得しています!)IdleTimeを変更します。 – Terminal8

+0

興味のある - 何らかの一般的なバッグに格納されているactionComponent.IdleTimeの状態ですか?もしそうなら、基本インターフェースに 'Apply(BagData bag)'と 'Store(BagData bag)'メソッドを持たせることができます。つまり、派生型を知る必要があるときにプロパティを照会するために使用できるメタデータ型システムを検討する必要があるかもしれません。 'TypeDescriptor'は、プロパティグリッドなどのようなものによって使用される既定の開始点です。 –

+0

いいえ、現在はありません。私は概念に完全に精通していません - おそらくあなたが提供できる参考資料はありますか? – Terminal8

答えて

0

サブタイトルにキャストすることで特定の実装をテストするのは良い考えではありません。あなたが提供している例から、何かアクションを実行したいと思っているIGameObjectがあるようです。 IActionComponentには、IGameObjectを引数とするメソッドが公開されているのではなく、別の方法で並べ替えることができます。すなわち、IGameObjectを実行するか、実行可能なアクションを好むことができます。あなたがIActionComponentの実装方法に応じて

public void IWantYouToAttackSuperSlow(IGameObject target) 
{ 
    target.ActionComponent.ExecuteAction(); 
} 

を行う

interface IActionComponent 
{ 
    ExecuteAction(); 
} 

interface IGameObject 
{ 
    IActionComponent ActionComponent { get; } 
} 

は、実行中のメソッドでは、多型は、右のコードが実行されていることを確認します。

Command Patternというパターンがあり、必要に応じているようです。

+0

私がここで提供したケーススタディは、私が実行しているこのインタフェース継承の問題の1つのインスタンスに過ぎません。私はコンポーネントベースのシステムを開発しているので、IGameObjectはコンポーネントの非常にシンプルなコンテナであり、実際のコンポーネントに関する論理や知識はまったくありません。基本的には、特定の「エンティティ」をカバーするコンポーネントをその「エンティティ」に結びつけることを可能にするGUIDです(しかし、簡単なコンポーネント取得を提供するために、そのコンセプトをインターフェイス/クラスにカプセル化しています)。 – Terminal8

+0

@Icelight:私はあなたのアーキテクチャを完全に理解すると主張することはできません。しかし、あなたの文章*「IGameObjectは、コンポーネントのための非常にシンプルなコンテナであり、絶対にロジックはありません。私は「プロパティのバッグでなければなりません」というクラスを聞くたびに、オブジェクト指向プログラミングについて最初に学んだことを思い出しています。 **データ**です。だから、私の意見では、論理のないクラスは少しコード臭です。 –

+0

@sJhonny:あなたが何を得ているのか分かります。しかし、この場合、私の便宜のためにGameObjectが必要ではなく配置されました。 「真の」コンポーネントベースのゲームシステムでは、すべてのコンポーネントが大きなコンポーネントのクラウドに浮かんでいる(または、コンポーネントタイプ専用のマネージャに座っている)場合があります。一緒に同じエンティティに。このアプローチをとるのではなく、同じエンティティのすべてのコンポーネントを保持するGameObjectを使用しています。このアプローチは自分で視覚化する方が簡単です。 – Terminal8

1

あなたはそれがキャストダウンIActionComponentITimedActionComponentに示したコードは右、あなたがそれらを使用するためにIdleTimeの特性を認識する必要がありunavoidable-(私の知る限り)ですか?

あなたのIActionComponentを使用するクラスではそれを処理する必要がないところで、あまり見栄えのないコードを隠すことがここのトリックだと思います。これを行う方法の
私が最初に考えたのは使用することですFactory

public void IWantYouToAttackSuperSlow(IGameObject target) 
{ 
    //Let's get the action component for the gameobject 
    IActionComponent actionComponent = ActionComponentsFactory.GetTimedActionComponentIfAvailable(int.MaxValue); 
} 

とあなたの工場の方法:

public IActionComponent GetTimedActionComponentIfAvailable(IGameObject target, int idleTime) 
{ 
var actionComponent = target.GetComponent<IActionComponent>() as ITimedActionComponent; 

    if (actionComponent != null) //We're okay! 
    { 
     actionComponent.IdleTime = int.MaxValue; //Mwhaha 
    } 
return (actionComponent != null)? actionComponent : target.GetComponent<IActionComponent>(); 
} 
+0

あなたの最初の文章に関して、私は同意します、そして、私はそれのまわりに本当の方法を見ることができません。私はすべてを修正するいくつかの奇跡的な建築のために釣りをしたり祈っていたと思う。しかし、私たちはまだあまりいないと思います;)ファクトリメソッドについては、私はそれが好きですし、少なくとも少しきれいなことを助けるでしょう。私は今これがどれだけうまくいくかを見ていきます。 – Terminal8