我想我会在代码中使用一些案例研究作为我的意思的一个例子。
现在,我正在为基于组件的游戏系统开发一个行为/动作系统。 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等)之外的确切内容。 / p>
是否有更简洁的方法来处理这个问题,这将允许我公开子类中定义的这些属性,而不必将检索到的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 :(得分:1)
您展示的将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>();
}
答案 1 :(得分:0)
你是对的,通过转换为子类型来测试特定的实现并不是一个好主意。从您提供的示例中可以看出,您希望对IGameObject
执行某些操作。不要让你的IActionComponent
公开一个以IGameObject
为参数的方法,你可以反过来做,也就是让IGameObject
取一个或多个,如果你愿意的话,您可以执行的操作。
interface IActionComponent
{
ExecuteAction();
}
interface IGameObject
{
IActionComponent ActionComponent { get; }
}
然后在执行方法中执行
public void IWantYouToAttackSuperSlow(IGameObject target)
{
target.ActionComponent.ExecuteAction();
}
根据您实现IActionComponent的方式,多态将确保执行正确的代码。
有一种名为Command Pattern的模式似乎符合您的需要。