我正在开发一个游戏。游戏中的每个实体都是一个GameObject
. 每个GameObject
都由GameObjectController
、GameObjectModel
和组成GameObjectView
。(或其继承人。)
对于 NPC,GameObjectController
分为:
IThinkNPC
:读取当前状态并决定要做什么
IActNPC
:根据需要做的事情更新状态
ISenseNPC
:读取当前状态以回答世界查询(例如“我在阴影中吗?”)
我的问题:ISenseNPC
界面可以吗?
public interface ISenseNPC
{
// ...
/// <summary>
/// True if `dest` is a safe point to which to retreat.
/// </summary>
/// <param name="dest"></param>
/// <param name="angleToThreat"></param>
/// <param name="range"></param>
/// <returns></returns>
bool IsSafeToRetreat(Vector2 dest, float angleToThreat, float range);
/// <summary>
/// Finds a new location to which to retreat.
/// </summary>
/// <param name="angleToThreat"></param>
/// <returns></returns>
Vector2 newRetreatDest(float angleToThreat);
/// <summary>
/// Returns the closest LightSource that illuminates the NPC.
/// Null if the NPC is not illuminated.
/// </summary>
/// <returns></returns>
ILightSource ClosestIlluminatingLight();
/// <summary>
/// True if the NPC is sufficiently far away from target.
/// Assumes that target is the only entity it could ever run from.
/// </summary>
/// <returns></returns>
bool IsSafeFromTarget();
}
这些方法都不接受任何参数。相反,实现应该保持对相关的引用GameObjectController
并阅读它。
但是,我现在正在尝试为此编写单元测试。显然,有必要使用模拟,因为我不能直接传递参数。我这样做的方式感觉非常脆弱 - 如果出现另一个以不同方式使用世界查询实用程序的实现怎么办?真的,我不是在测试接口,而是在测试实现。较差的。
我首先使用这种模式的原因是为了保持IThinkNPC
实现代码的简洁:
public BehaviorState RetreatTransition(BehaviorState currentBehavior)
{
if (sense.IsCollidingWithTarget())
{
NPCUtils.TraceTransitionIfNeeded(ToString(), BehaviorState.ATTACK.ToString(), "is colliding with target");
return BehaviorState.ATTACK;
}
if (sense.IsSafeFromTarget() && sense.ClosestIlluminatingLight() == null)
{
return BehaviorState.WANDER;
}
if (sense.ClosestIlluminatingLight() != null && sense.SeesTarget())
{
NPCUtils.TraceTransitionIfNeeded(ToString(), BehaviorState.ATTACK.ToString(), "collides with target");
return BehaviorState.CHASE;
}
return currentBehavior;
}
然而,也许清洁不值得。
因此,如果ISenseNPC
每次都获取所需的所有参数,我可以将其设为静态。这有什么问题吗?