【发布时间】:2011-02-05 12:52:27
【问题描述】:
我在为我的 C# XNA 游戏做出架构决策时遇到了困难。
世界中的基本实体,例如一棵树、僵尸或玩家,被表示为一个游戏对象。每个 GameObject 至少由 GameObjectController、GameObjectModel 和 GameObjectView 组成。
这三个对于简单的实体来说已经足够了,比如无生命的树木或岩石。然而,当我试图尽可能地保留功能时,继承开始变得笨拙。从语法上讲,我什至不确定如何最好地实现我的目标。
这里是GameObjectController:
public class GameObjectController
{
protected GameObjectModel model;
protected GameObjectView view;
public GameObjectController(GameObjectManager gameObjectManager)
{
this.gameObjectManager = gameObjectManager;
model = new GameObjectModel(this);
view = new GameObjectView(this);
}
public GameObjectManager GameObjectManager
{
get
{
return gameObjectManager;
}
}
public virtual GameObjectView View
{
get
{
return view;
}
}
public virtual GameObjectModel Model
{
get
{
return model;
}
}
public virtual void Update(long tick)
{
}
}
我想指定GameObjectController 的每个子类至少可以访问GameObjectView 和GameObjectModel。如果子类可以使用这些类,但可能会覆盖更复杂的Update() 方法,我不希望它们必须复制代码来生成这些依赖项。因此,GameObjectController 构造函数设置了这些对象。
但是,有些对象确实想要覆盖模型和视图。这就是问题所在。
有些物体需要战斗,所以是CombatantGameObjects:
public class CombatantGameObject : GameObjectController
{
protected new readonly CombatantGameModel model;
public new virtual CombatantGameModel Model
{
get { return model; }
}
protected readonly CombatEngine combatEngine;
public CombatantGameObject(GameObjectManager gameObjectManager, CombatEngine combatEngine)
: base(gameObjectManager)
{
model = new CombatantGameModel(this);
this.combatEngine = combatEngine;
}
public override void Update(long tick)
{
if (model.Health <= 0)
{
gameObjectManager.RemoveFromWorld(this);
}
base.Update(tick);
}
}
还是很简单的。我使用new 隐藏实例变量是否正确?请注意,我在这里分配了CombatantObjectController.model,即使GameObjectController.Model 已经设置。而且,战斗人员不需要任何特殊的视图功能,所以他们不理会GameObjectController.View。
然后我进入PlayerController,在那里发现了一个错误。
public class PlayerController : CombatantGameObject
{
private readonly IInputReader inputReader;
private new readonly PlayerModel model;
public new PlayerModel Model
{
get { return model; }
}
private float lastInventoryIndexAt;
private float lastThrowAt;
public PlayerController(GameObjectManager gameObjectManager, IInputReader inputReader, CombatEngine combatEngine)
: base(gameObjectManager, combatEngine)
{
this.inputReader = inputReader;
model = new PlayerModel(this);
Model.Health = Constants.PLAYER_HEALTH;
}
public override void Update(long tick)
{
if (Model.Health <= 0)
{
gameObjectManager.RemoveFromWorld(this);
for (int i = 0; i < 10; i++)
{
Debug.WriteLine("YOU DEAD SON!!!");
}
return;
}
UpdateFromInput(tick);
// ....
}
}
第一次执行此行时,我得到一个空引用异常:
model.Body.ApplyImpulse(movementImpulse, model.Position);
model.Position 查看model.Body,它为空。
这是一个在游戏对象部署到世界之前初始化游戏对象的函数:
public void Initialize(GameObjectController controller, IDictionary<string, string> data, WorldState worldState)
{
controller.View.read(data);
controller.View.createSpriteAnimations(data, _assets);
controller.Model.read(data);
SetUpPhysics(controller,
worldState,
controller.Model.BoundingCircleRadius,
Single.Parse(data["x"]),
Single.Parse(data["y"]), bool.Parse(data["isBullet"]));
}
每个对象都作为GameObjectController 传递。这是否意味着如果对象真的是PlayerController,controller.Model 将引用基类的GameObjectModel 而不是PlayerController 的覆盖PlayerObjectModel?
回应rh:
这意味着现在对于 PlayerModel p, p.Model 不等价于 ((CombatantGameObject)p).Model,以及 也不等于 ((GameObjectController)p).Model.
这正是我不想要的。我要:
PlayerController p;
p.Model == ((CombatantGameObject)p).Model
p.Model == ((GameObjectController)p).Model
我该怎么做? override?
【问题讨论】:
-
答案是:更多的僵尸。一个游戏永远不会有太多的僵尸。
-
谢谢 - 我已经更新了我的回复,提供了几种可以继续的方式。在这种情况下,覆盖不是您的最佳选择,因为您实际上想要返回不同的 type,这会为属性提供不同的签名。
标签: c# oop architecture inheritance syntax