Ich denke, ich verwende eine Fallstudie von etwas in meinem Code als ein Beispiel dafür, was ich meine.Interface Vererbung/Hierarchien in .Net - gibt es einen besseren Weg?
Momentan arbeite ich an einem Verhaltens-/Aktionssystem für mein komponentenbasiertes Spielsystem. Gameobjects kann eine IBehaviorComponent haben und eine IActionComponent mit ihnen verbunden, die nur die relevanten Angaben zu entnehmen sind, setzen die folgenden:
public interface IBehaviorComponent : IBaseComponent
{
IBehavior Behavior { get; }
}
public interface IActionComponent : IBaseComponent
{
IAction Action { get; }
void ExecuteAction(IGameObject target = null);
}
Nun, dies so weit alles in Ordnung ist (zumindest für mich!). Aber Probleme beginnen zu brauen, wenn ich Implementierungen von IActionComponent ansehe.
Zum Beispiel eine einfache IActionComponent Implementierung:
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
}
Aber lassen Sie uns sagen, dass ich eine komplexere IActionComponent Implementierung einführen wollen, die für Aktionen können auf einem zeitlich abgestimmten Zeitplan ausgeführt werden:
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);
}
}
}
Und jetzt sagen wir, ich möchte IdleTime offen legen, so dass es durch äußere Einflüsse verändert werden kann. Meine Gedanken waren anfangs eine neue Schnittstelle zu erstellen:
public interface ITimedActionComponent : IActionComponent
{
float IdleTime { get; set; }
float IdleTimeRemaining { get; set; }
}
jedoch hier das Problem ist, dass meine Komponentensystem speichert alles auf einer Ebene-up von IBaseComponent. Die Aktionskomponente für ein GameObject wird also als IActionComponent, -not- abgerufen, z. B. als ITimedActionComponent oder IRandomizedActionComponent oder ICrashTheProgramActionComponent. Ich hoffe, die Gründe dafür sind offensichtlich, da ich möchte, dass das GameObject nach einer seiner Komponenten abgefragt werden kann, ohne genau wissen zu müssen, was es über den Basistyp der Komponente (IActionComponent, IRenderableComponent, IPhysicsComponent, etc.) hinaus möchte.
Gibt es eine sauberere Möglichkeit, dies zu behandeln, die es mir ermöglichen, diese in untergeordneten Klassen definierten Eigenschaften verfügbar zu machen, ohne dass alles die abgerufene IActionComponent zu dem Typ, an dem es interessiert ist, zu werfen? Oder ist das der einzige/beste Weg, dies zu erreichen? Etwas wie:
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
}
}
Im Moment ist ich denke, das ist Der einzige Weg, aber ich dachte, ich sehen würde, wenn es ein Muster in dem Holz versteckt ist, dass ich unwissend, oder wenn jemand ein vorschlagen viel bessere Art, dies zu erreichen, um damit zu beginnen.
Danke!
Mein anderer Gedanke war, in ein anderes Objekt zu verwenden, wie IActionComponentExecutor, was würde entscheiden, wann Action.Execute() aufgerufen wird, aber ich denke, dass bringt mich zurück auf Platz eins in einer runden-Art und Weise - externe Objekte müssen dann IActionComponentTimedExecutor (Oh ~ wir werden nun Enterprise-y bekommen) !) um IdleTime zu ändern. – Terminal8
Aus Interesse - ist der Zustand für actionComponent.IdleTime in einer Art generischer Tasche gespeichert? Wenn dies der Fall ist, können Sie die Methoden "Apply (BagData bag)" und "Store (BagData bag)" auf Ihrer Basisschnittstelle anwenden. Das heißt, Sie müssen möglicherweise auch ein System vom Typ Metadaten in Betracht ziehen, mit dem Sie Eigenschaften abfragen können, ohne die abgeleiteten Typen kennen zu müssen. 'TypeDescriptor' ist ein fertig gerollter Startpunkt, der von Dingen wie Eigenschaftenrastern usw. verwendet wird. –
Nein, momentan ist es nicht. Ich kenne das Konzept auch nicht ganz - gibt es einen Hinweis, den Sie vielleicht geben könnten? – Terminal8