Je développe un jeu. Chaque entité dans le jeu est un GameObject
. Chaque GameObject
est composé d'un GameObjectController
, GameObjectModel
et GameObjectView
. (Ou héritiers de celui-ci.)Jeu AI: Modèle pour la mise en œuvre des composants Sense-Think-Act?
Pour les PNJ, le GameObjectController
est divisé en:
IThinkNPC
: lit l'état actuel et rend une décision sur ce qu'il faut faire
IActNPC
: mises à jour état basé sur ce qui doit être fait
ISenseNPC
: lit l'état actuel pour répondre aux questions du monde (par exemple, « suis-je dans l'ombre? »)
Ma question: est-ce ok pour la ISenseNPC
interface?
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();
}
Aucune des méthodes ne prend de paramètres. Au lieu de cela, l'implémentation doit conserver une référence à la GameObjectController
pertinente et lire cela.
Cependant, j'essaie maintenant d'écrire des tests unitaires pour cela. De toute évidence, il est nécessaire d'utiliser le moqueur, car je ne peux pas passer directement les arguments. La façon dont je le fais se sent vraiment fragile - et si une autre implémentation se présente qui utilise les utilitaires de requête du monde d'une manière différente? Vraiment, je ne teste pas l'interface, je suis en train de tester l'implémentation. Pauvre.
La raison pour laquelle je ce modèle en premier lieu était de garder IThinkNPC
code d'implémentation propre:
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;
}
Peut-être que la propreté ne vaut pas, cependant. Donc, si ISenseNPC
prend tous les paramètres dont il a besoin à chaque fois, je pourrais le rendre statique. Y a-t-il un problème avec ça?