我对C#XNA游戏的架构决策有困难。
世界上的基本实体,例如树,僵尸或玩家,被表示为GameObject。每个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
,其为空。
这是一个在将GameObjects部署到世界之前初始化它的函数:
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)。型号
这正是我不想要的。我想要:
PlayerController p;
p.Model == ((CombatantGameObject)p).Model
p.Model == ((GameObjectController)p).Model
我该怎么做? override
?
答案 0 :(得分:2)
关键在于您在此处使用'new'关键字:
private new readonly PlayerModel model;
public new PlayerModel Model
{
get { return model; }
}
在这里:
protected new readonly CombatantGameModel model;
public new virtual CombatantGameModel Model
{
get { return model; }
}
你所说的是:“我知道我的基类已经定义了这些,但我想定义不同的碰巧具有相同名称的那些。”
这意味着现在对于PlayerModel p,p.Model 不等同于((CombatantGameObject)p).Model,并且 not 等效于((GameObjectController) )p)。型号
以下是您可以采取的几种方法。
1)不要提供强类型的子属性。 我知道这可能听起来很糟糕,但它实际上是一个非常强大的概念。如果基础模型定义了适用于所有子类的相应抽象/虚拟方法,则可以在基类中定义属性一次并完成。然后子类可以提供自己的实现。
以下是实现此目的的一种可能方法。
public class GameObjectController /* ... */
{
/* ... */
public GameObjectController()
{
Model = new GameObjectModel(this);
}
public GameObjectModel Model { get; protected set; }
}
public class CombatantGameObject : GameObjectController
{
/* ... */
public CombatantGameObject()
{
Model = new CombatantModel(this);
}
}
/* ... */
2)通过子类访问时提供强类型属性,但将字段存储在基类中一次。
这可行,但正确做法很棘手。这不是我的第一选择。
public class GameObjectController /* ... */
{
/* ... */
public GameObjectController()
{
Model = new GameObjectModel(this);
}
public GameObjectModel Model { get; protected set; }
}
public class CombatantGameObject : GameObjectController
{
/* ... */
public CombatantGameObject()
{
Model = new CombatantModel(this);
}
public new CombatantModel Model
{
get
{
return (CombatantModel)base.Model;
}
protected set
{
base.Model = value;
}
}
}
/* ... */
另外,要过早对过度提交复杂对象模型持谨慎态度。有时候开始简单,并且积极地进行重构,这是获得一个反映代码实际最终工作方式的好模型的最佳方式。