设计游戏对象

Kob*_*bol 10 c# oop xna

我最近开始使用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<Model>(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
}
Run Code Online (Sandbox Code Playgroud)

我遗漏了额外的方法,例如旋转,移动和绘制对象.现在,如果我想创建另一个对象,比如船,我会创建一个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
}
Run Code Online (Sandbox Code Playgroud)

同样,我遗漏了任何额外的船舶特定方法,比如发射导弹.你认为这种设计是好还是应该以某种方式改进或完全改变?看起来子类的构造函数很乱,但也许这是唯一的方法.我从来没有做过这样的事情,我想知道我是否偏离轨道.


感谢大家留下了答案.他们都非常有帮助.似乎普遍认为改变它以使用MVC模式是最好的.我将进一步研究如何做到这一点.我还将删除大多数构造函数,并且只有一个构造函数,因为modelResource之后的所有参数都不是创建对象所必需的,并且稍后可以通过方法调用来更改它们.

NM.*_*NM. 7

就个人而言,我发现大量的构造函数你有一点点输出,但这是你的选择,没有任何根本错误:)

关于从共同基础派生游戏对象的一般策略,这是一种非常常见的做事方式.标准,均匀.我倾向于开始使用更类似于MVC的东西,其中包含仅包含数据的非常轻量级的"模型"游戏对象.

我在XNA中看到的其他常见方法包括/您可能需要考虑的事项:

  • 实现IRenderable接口,而不是在基类或派生类中执行任何呈现代码.例如,您可能想要从未渲染的游戏对象 - 航点或某些东西.
  • 您可以使您的基类抽象,您不太可能想要实例化GameObject.

再说一点,小点.你的设计很好.


Jef*_*f B 5

构造函数的数量不仅是一个潜在的问题(特别是必须为每个派生类重新定义它们) - 但构造函数的参数数量可能会成为一个问题.即使您将可选参数设置为可空,也很难在以后读取和维护代码.

如果我写:

new Ship(content, "resource", true, null, null, null, null);
Run Code Online (Sandbox Code Playgroud)

第二个NULL做什么?

如果参数列表超出四个或五个参数,它会使代码更具可读性(但更详细)以保存参数:

GameObjectParams params(content, "resource");
params.IsAlive = true;
new Ship(params);
Run Code Online (Sandbox Code Playgroud)

有很多方法可以做到这一点.


mst*_*obl 5

既然你在问一个与设计相关的问题,我会尝试给出一些提示,而不是你发布的大量代码,因为其他人已经开始了.

游戏非常适合模型 - 视图 - 控制器模式.你明确地谈论了显示部分,但想一想:你在那里有矢量等,这通常是你建模的一部分.把游戏世界和那里的物体想象成模型.他们有一个位置,他们可能有速度和方向.这是MVC的模型部分.

我注意到你想让船开火.例如,您可能希望机械师在控制器类中进行发射,这需要键盘输入.此控制器类将消息发送到模型部分.一个声音很容易想到你可能想要的是在模型上有一些fireAction()方法.在您的基础模型上,这可能是一个可覆盖(虚拟,抽象)的方法.在船上它可能会给游戏世界添加一个"子弹".

看看我如何写了很多关于模型行为的文章,这里有另一件事给我带来了很多帮助:使用策略模式使类的行为可以交换.该策略模式,如果你认为AI和希望也很大行为在未来somewhen改变.你现在可能不知道,但是在一个月内你可能会想要弹道导弹作为默认导弹,如果你使用了策略模板,你可以在以后轻松改变它.

所以最终你可能想要真正做出类似于显示类的东西.它会有像你这样命名的原语:rotate,translate等.并且您希望从中派生它以添加更多功能或更改功能.但想想看,一旦你拥有的不仅仅是一艘船,一种特殊的船,另一种特殊的船,你将会遇到衍生地狱,你会复制很多代码.再次,使用策略模式.并记得保持简单.Displayble类需要具备什么?

  • 意味着知道它相对于屏幕的位置.它可以,这要归功于它的in-world对象的模型以及类似gameworld的模型!
  • 它必须知道要显示其模型及其尺寸的位图.
  • 它必须知道是否有任何东西应该阻止它绘制,如果它不是由绘图引擎处理(即另一个对象遮挡它).


Rya*_*yan 4

这种设计没有分离对象的 UI 部分和对象的行为部分,可以说是“模型”和“视图”。这不一定是坏事,但如果继续这种设计,您可能会发现以下重构很困难:

  • 通过更改所有艺术资产来重新设计游戏皮肤
  • 更改许多不同游戏内对象的行为,例如确定对象是否存活的规则
  • 更改为不同的精灵引擎。

但如果你可以接受这些权衡,并且这个设计对你来说有意义,那么我认为它没有什么严重的错误。