我最近开始使用Microsoft XNA和C#开发一款适合自己娱乐的小游戏.我的问题是关于设计游戏对象和继承游戏对象的对象.我将把游戏对象定义为可以在屏幕上呈现的东西.所以为此,我决定创建一个基类,其中所有其他需要渲染的对象都将继承,称为GameObject.下面的代码是我制作的课程:
class GameObject { private Model model = null; private float scale = 1f; private Vector3 position = Vector3.Zero; private Vector3 rotation = Vector3.Zero; private Vector3 velocity = Vector3.Zero; private bool alive = false; protected ContentManager content; #region Constructors public GameObject(ContentManager content, string modelResource) { this.content = content; model = content.Load(modelResource); } public GameObject(ContentManager content, string modelResource, bool alive) : this(content, modelResource) { this.alive = alive; } public GameObject(ContentManager content, string modelResource, bool alive, float scale) : this(content, modelResource, alive) { this.scale = scale; } public GameObject(ContentManager content, string modelResource, bool alive, float scale, Vector3 position) : this(content, modelResource, alive, scale) { this.position = position; } public GameObject(ContentManager content, string modelResource, bool alive, float scale, Vector3 position, Vector3 rotation) : this(content, modelResource, alive, scale, position) { this.rotation = rotation; } public GameObject(ContentManager content, string modelResource, bool alive, float scale, Vector3 position, Vector3 rotation, Vector3 velocity) : this(content, modelResource, alive, scale, position, rotation) { this.velocity = velocity; } #endregion }
我遗漏了额外的方法,例如旋转,移动和绘制对象.现在,如果我想创建另一个对象,比如船,我会创建一个Ship类,它将继承GameObject.示例代码如下:
class Ship : GameObject { private int num_missiles = 20; // the number of missiles this ship can have alive at any given time private Missile[] missiles; private float max_missile_distance = 3000f; // max distance a missile can be from the ship before it dies #region Constructors public Ship(ContentManager content, string modelResource) : base(content, modelResource) { InitShip(); } public Ship(ContentManager content, string modelResource , bool alive) : base(content, modelResource, alive) { InitShip(); } public Ship(ContentManager content, string modelResource, bool alive, float scale) : base(content, modelResource, alive, scale) { InitShip(); } public Ship(ContentManager content, string modelResource, bool alive, float scale, Vector3 position) : base(content, modelResource, alive, scale, position) { InitShip(); } public Ship(ContentManager content, string modelResource, bool alive, float scale, Vector3 position, Vector3 rotation) : base(content, modelResource, alive, scale, position, rotation) { InitShip(); } public Ship(ContentManager content, string modelResource, bool alive, float scale, Vector3 position, Vector3 rotation, Vector3 velocity) : base(content, modelResource, alive, scale, position, rotation, velocity) { InitShip(); } #endregion }
同样,我遗漏了任何额外的船舶特定方法,比如发射导弹.你认为这种设计是好还是应该以某种方式改进或完全改变?看起来子类的构造函数很乱,但也许这是唯一的方法.我从来没有做过这样的事情,我想知道我是否偏离轨道.
感谢大家留下了答案.他们都非常有帮助.似乎普遍认为改变它以使用MVC模式是最好的.我将进一步研究如何做到这一点.我还将删除大多数构造函数,并且只有一个构造函数,因为modelResource之后的所有参数都不是创建对象所必需的,并且稍后可以通过方法调用来更改它们.
就个人而言,我发现大量的构造函数你有一点点输出,但这是你的选择,没有任何根本错误:)
关于从共同基础派生游戏对象的一般策略,这是一种非常常见的做事方式.标准,均匀.我倾向于开始使用更类似于MVC的东西,其中包含仅包含数据的非常轻量级的"模型"游戏对象.
我在XNA中看到的其他常见方法包括/您可能需要考虑的事项:
实现IRenderable接口,而不是在基类或派生类中执行任何呈现代码.例如,您可能想要从未渲染的游戏对象 - 航点或某些东西.
您可以使您的基类抽象,您不太可能想要实例化GameObject.
再说一点,小点.你的设计很好.
构造函数的数量不仅是一个潜在的问题(特别是必须为每个派生类重新定义它们) - 但构造函数的参数数量可能会成为一个问题.即使您将可选参数设置为可空,也很难在以后读取和维护代码.
如果我写:
new Ship(content, "resource", true, null, null, null, null);
第二个NULL做什么?
如果参数列表超出四个或五个参数,它会使代码更具可读性(但更详细)以保存参数:
GameObjectParams params(content, "resource"); params.IsAlive = true; new Ship(params);
有很多方法可以做到这一点.
既然你在问一个与设计相关的问题,我会尝试给出一些提示,而不是你发布的大量代码,因为其他人已经开始了.
游戏非常适合模型 - 视图 - 控制器模式.你明确地谈论了显示部分,但想一想:你在那里有矢量等,这通常是你建模的一部分.把游戏世界和那里的物体想象成模型.他们有一个位置,他们可能有速度和方向.这是MVC的模型部分.
我注意到你想让船开火.例如,您可能希望机械师在控制器类中进行发射,这需要键盘输入.此控制器类将消息发送到模型部分.一个声音很容易想到你可能想要的是在模型上有一些fireAction()方法.在您的基础模型上,这可能是一个可覆盖(虚拟,抽象)的方法.在船上它可能会给游戏世界添加一个"子弹".
看看我如何写了很多关于模型行为的文章,这里有另一件事给我带来了很多帮助:使用策略模式使类的行为可以交换.该策略模式,如果你认为AI和希望也很大行为在未来somewhen改变.你现在可能不知道,但是在一个月内你可能会想要弹道导弹作为默认导弹,如果你使用了策略模板,你可以在以后轻松改变它.
所以最终你可能想要真正做出类似于显示类的东西.它会有像你这样命名的原语:rotate,translate等.并且您希望从中派生它以添加更多功能或更改功能.但想想看,一旦你拥有的不仅仅是一艘船,一种特殊的船,另一种特殊的船,你将会遇到衍生地狱,你会复制很多代码.再次,使用策略模式.并记得保持简单.Displayble类需要具备什么?
意味着知道它相对于屏幕的位置.它可以,这要归功于它的in-world对象的模型以及类似gameworld的模型!
它必须知道要显示其模型及其尺寸的位图.
它必须知道是否有任何东西应该阻止它绘制,如果它不是由绘图引擎处理(即另一个对象遮挡它).