2010-04-08 78 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