У меня возникли трудности с архитектурным решением для моей игры C# XNA.C#: Наследование, переопределение и скрытие
Основная сущность в мире, такая как дерево, зомби или игрок, представлена как объект 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
's overriden PlayerObjectModel
?
В ответ на РХ:
Это означает, что в настоящее время для PlayerModel р, p.Model не эквивалентно ((CombatantGameObject) р) .MODEL и также не эквивалентно ((GameObjectController) p) .Model.
Это именно то, чего я не хочу. Я хочу:
PlayerController p;
p.Model == ((CombatantGameObject)p).Model
p.Model == ((GameObjectController)p).Model
Как это сделать? override
?
Ответ: больше зомби. В игре никогда не бывает слишком много зомби. – slugster
Спасибо - я обновил свой ответ несколькими способами, которые вы можете продолжить. Переопределение не будет вашим лучшим выбором в этом случае, потому что вы действительно хотите вернуть другой _type_, что дает свойству другую подпись. –