我想我将对代码中的某些内容进行案例研究,以说明我的意思。

现在,我正在为基于组件的游戏系统开发行为/ Action 系统。 GameObject可以具有附加的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向上存储了一层。因此,将GameObject的 Action 组件作为IActionComponent而不是ITimedActionComponent或IRandomizedActionComponent甚至ICrashTheProgramActionComponent进行检索。我希望其原因显而易见,因为我希望任何东西都能够查询GameObject的组件之一,而不必知道组件基本类型(IActionComponent,IRenderableComponent,IPhysicsComponent等)之外到底想要什么。

有没有更清洁的方法来处理此问题,从而使我可以公开在子类中定义的这些属性,而无需将所有检索到的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
   }
}

现在,我正在考虑这是唯一的方法,但我认为我会发现我是否不知道木器中是否藏有某种图案,或者是否有人可以提出一种更好的方法来实现这一目标。

谢谢!

最佳答案

您所展示的将IActionComponent转换为ITimedActionComponent的代码是(据我所知)是不可避免的-您必须知道IdleTime属性才能使用它们,对吗?

我认为这里的诀窍是隐藏不太好看的代码,使使用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>();
}

09-11 19:04