Myślę, że wykorzystam studium przypadku czegoś w moim kodzie jako przykład tego, co mam na myśli.Dziedziczenie interfejsu/hierarchie w .Net - czy istnieje lepszy sposób?
W tej chwili pracuję nad systemem zachowania/działania dla mojego opartego na komponentach systemu gry. GameObjects może mieć IBehaviorComponent oraz IActionComponent dołączony do nich, co pokazano tylko odpowiednie dane, wystawiać następujące:
public interface IBehaviorComponent : IBaseComponent
{
IBehavior Behavior { get; }
}
public interface IActionComponent : IBaseComponent
{
IAction Action { get; }
void ExecuteAction(IGameObject target = null);
}
Teraz to wszystko jest w porządku tak daleko (przynajmniej dla mnie!). Ale kłopoty zaczynają warzyć, gdy patrzę na implementacje IActionComponent.
Na przykład, prosta implementacja 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
}
Ale powiedzmy, że chcemy wprowadzić bardziej kompleksową realizację IActionComponent który pozwala na działania, które mają być wykonywane na czasowym harmonogramu:
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);
}
}
}
A teraz, powiedzmy, chcę wystawić IdleTime, aby mógł być zmieniony przez wpływy zewnętrzne. Moje myśli były początkowo, aby utworzyć nowy interfejs:
public interface ITimedActionComponent : IActionComponent
{
float IdleTime { get; set; }
float IdleTimeRemaining { get; set; }
}
Jednak problem jest to, że mój system komponent przechowuje wszystko w jednym-level-up z IBaseComponent. Tak więc składnik akcji dla GameObject jest pobierany jako IActionComponent, -nie-, powiedzmy, ITimedActionComponent, IRandomizedActionComponent, a nawet ICrashTheProgramActionComponent. Mam nadzieję, że przyczyny tego są oczywiste, ponieważ chcę, aby cokolwiek było w stanie przesłać zapytanie do GameObject dla jednego z jego komponentów bez konieczności dokładnego sprawdzenia, czego chce poza podstawowym typem komponentu (IActionComponent, IRenderableComponent, IPhysicsComponent, itp.)
Czy jest to czystszy sposób, który pozwoli mi na ujawnienie tych właściwości zdefiniowanych w klasach podrzędnych, bez konieczności wysyłania pobranego elementu IActionComponent do typu, który jest zainteresowany? Czy jest to po prostu jedyny/najlepszy sposób na osiągnięcie tego. Coś jak:
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
}
}
właśnie teraz myślę, że to The Only Way, ale pomyślałem, że zobaczyć, czy jest jakiś wzór ukrywa się w stolarkę, że jestem ignorantem, albo jeśli ktoś może zaproponować o wiele lepszy sposób na osiągnięcie tego celu.
Dzięki!
Moja druga myśl była użyć innego obiektu wewnątrz, jak IActionComponentExecutor, które zdecyduj, kiedy zadzwonić do Action.Execute(), ale myślę, że to doprowadza mnie z powrotem do kwadratu w sposób okrągły - obiekty zewnętrzne będą wtedy musiały wyśledzić IActionComponentTimedExecutor (oh ~ dostaniemy przedsiębiorstwo - y teraz !), aby zmienić IdleTime. – Terminal8
Ciekawe - czy stan akcjiComponent.IdleTime jest przechowywany w jakiejś ogólnej torbie? Jeśli tak, możesz użyć aplikacji 'Apply (BagData bag)' i 'Store (BagData bag)' w twoim podstawowym interfejsie. Powiedział, że być może trzeba również rozważyć system typu metadanych, za pomocą którego można wyszukiwać właściwości wymagające poznania typów pochodnych. 'TypeDescriptor' jest gotowym punktem początkowym używanym przez obiekty takie jak siatki własności itp. –
Nie, obecnie nie jest. Nie jestem do końca zaznajomiony z tym pojęciem - czy istnieje odniesienie, które mógłbyś podać? – Terminal8