6

我想我将在我的代码中使用一个案例研究作为我的意思的例子。

现在,我正在为基于组件的游戏系统开发行为/动作系统。GameObjects 可以有一个 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 的动作组件被检索为 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
   }
}

现在我在想那是唯一的方法,但我想我会看看木制品中是否隐藏着一种我不知道的图案,或者是否有人可以提出一种更好的方法来完成这一点。

谢谢!

4

2 回答 2

1

您展示的将 an 向下转换为 an 的代码IActionComponentITimedActionComponent据我所知)是不可避免的——您必须了解这些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>();
}
于 2012-09-11T22:24:55.313 回答
0

没错,通过转换为子类型来测试特定实现并不是一个好主意。从您提供的示例来看,您似乎有一个IGameObject要对其执行某些操作的示例。与其让您IActionComponent公开一个将 theIGameObject作为参数的方法,您还可以用另一种方式来做,即让IGameObject您可以执行一个或多个您可以执行的操作。

interface IActionComponent
{
    ExecuteAction();
}

interface IGameObject
{
    IActionComponent ActionComponent { get; }
}

然后在执行方法中做

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

根据您实现 IActionComponent 的方式,多态性将确保执行正确的代码。

有一种称为命令模式的模式似乎适合您的需要。

于 2012-09-11T22:22:57.527 回答