2010-04-08 76 views
2

我对于C#XNA游戏的架构决策有困难。C#:继承,重写和隐藏

世界中的基本实体,例如树,僵尸或玩家被表示为游戏对象。每个游戏对象至少由一个GameObjectController,GameObjectModelGameObjectView组成。

这三个足够简单的实体,如无生命的树木或岩石。然而,当我试图尽可能地保持功能性时,继承开始变得笨拙。在句法上,我甚至不确定如何最好地实现我的目标。

这里是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每个子类都将有至少访问的GameObjectViewGameObjectModel。如果子类使用这些类很好,但也许重写更复杂的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.Positionmodel.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)。型号,和 也不等同于 ((GameObjectController)p).Model。

这正是我不想要的。我想要:

PlayerController p; 
p.Model == ((CombatantGameObject)p).Model 
p.Model == ((GameObjectController)p).Model 

我该怎么做? override

+1

答案是:更多的僵尸。一场比赛永远不会有太多的僵尸。 – slugster 2010-04-08 03:32:33

+0

谢谢 - 我已经用几种方法更新了我的回复,您可以继续。在这种情况下,覆盖不会是您的最佳选择,因为您实际上希望返回不同的_type_,从而赋予该属性不同的签名。 – 2010-04-08 18:26:43

回答

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)。型号,也等价于((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; 
     } 
    } 
} 
/* ... */ 

此外,请谨慎对待过早提交到复杂对象模型。有时候开始简单并积极重构是获得反映代码实际工作结果的良好模型的最佳方式。

+0

谢谢。我用这个回复更新了我的OP。 – 2010-04-08 14:22:30