如何用C ++组织游戏引擎?我使用继承是个好主意吗?


11

我是游戏开发和编程的初学者。

我正在尝试学习构建游戏引擎的一些原理。
我想创建一个简单的游戏,正要尝试实现游戏引擎。

所以我认为我的游戏引擎应该控制这些事情:

- Moving the objects in the scene
- Checking the collisions
- Adjusting movements based on collisions
- Passing the polygons to the rendering engine

我设计了这样的对象:

class GlObject{
private:
    idEnum ObjId;
    //other identifiers
public:
    void move_obj(); //the movements are the same for all the objects (nextpos = pos + vel)
    void rotate_obj(); //rotations are the same for every objects too
    virtual Polygon generate_mesh() = 0; // the polygons are different
}

我在游戏中有4个不同的对象:飞机,障碍物,玩家,子弹,并且我将它们设计如下:

class Player : public GlObject{ 
private:
    std::string name;
public:
    Player();
    Bullet fire() const; //this method is unique to the class player
    void generate_mesh();
}

现在在游戏引擎中,我希望有一个常规的对象列表,可以在其中检查例如碰撞,移动对象等情况,但是我也希望游戏引擎将使用用户命令来控制玩家...

这是一个好主意吗?

class GameEngine{
private:
    std::vector<GlObject*> objects; //an array containg all the object present
    Player* hPlayer; //hPlayer is to be read as human player, and is a pointer to hold the reference to an object inside the array
public:
    GameEngine();
    //other stuff
}

GameEngine构造函数将如下所示:

GameEngine::GameEngine(){
    hPlayer = new Player;
    objects.push_back(hPlayer);
}

我使用指针的事实是因为我需要调用fire()Player对象唯一的那个。

所以我的问题是:这是一个好主意吗?我在这里使用继承是错误的吗?



发布后我见过它们,实际上构图是个好主意!我总是害怕最终将c与类一起使用,而不是真正的c ++设计!
Pella86

我想有更多的可用的组合物不是继承的原因...... 我的回答给了周围提供了灵活性优势的几个例子。有关实现的一个好的示例,请查看我链接的文章。
郊狼

Answers:


12

取决于您所说的“好”。当然,它将完成工作。它有很多优点,也有缺点。在引擎变得更加复杂之前,您将找不到它们。有关主题的讨论,请参见SO上的这个问题。

两种方法都有很多意见,但是如果您的引擎代码仍处于早期阶段,那么将很难向您解释为什么它可能很糟糕。

总的来说,斯科特·迈耶斯(Scott Meyers)对于继承有很多很棒的事情要说。如果您仍处于开发阶段,请在继续之前阅读本书(有效的C ++)。这是一本很棒的书,并且是我们公司中任何编码人员的必备条件。但是总结一下建议:如果您正在编写一个类并考虑使用继承,请绝对清楚该类与其祖先之间的关系是“是”关系。也就是说,每个B都是A。不要仅仅使用继承作为让B访问A提供的功能的简单方法,否则会导致严重的体系结构问题,还有其他方法可以做到这一点。

简单地说; 对于任何大小的引擎,您都会很快发现对象很少会落入继承起作用的整洁的层次结构中。在适当的时候使用它,但不要陷入将它用于所有事物的陷阱中-学习其他方法来相互关联对象。


我赞成关于继承的意见。我个人的经验法则是:除非必须这样做,否则不要这样做,否则其他任何方式都是愚蠢的。换句话说,在99%的情况下(至少:)),继承不是一个好主意。同样,最好将游戏逻辑(旋转/移动)与渲染(generate_mesh())分开。另一件事是fire()-考虑有一个动作列表和一个通用的action(my_action)函数-这样一来,您最终不会获得分布在所有类上的数以亿计的不同函数。无论哪种情况,都应使其简单并在遇到问题时进行无情的重构。
Gilead 2012年

8

对于游戏对象(实体)的逻辑数据和功能,我强烈建议使用合成而不是继承。一个好的开始将是基于组件的方法。在《牛仔编程》的这篇文章对此进行了很好的描述,该文章描述了如何实现该体系结构以及如何使Tony Hawk系列产品受益。

另外,有关实体系统的这篇文章在我第一次阅读时也启发了我。

继承是实现多态的好工具。除了非常简单的项目之外,您应该避免使用它作为构造实体和游戏逻辑的方法。如果继承,那么您肯定会不得不复制和维护相同代码的副本,以实现无法从公共父代继承的功能。然后,您可能会开始使用最常用的逻辑和数据来污染您的类,以避免重复代码过多。

在很多情况下组件都很方便。使用它们,您可以实现更多目标:

您的实体类型的灵活性:在游戏开发中,有时某些实体会突然发生变化,而游戏设计师则需要由开发人员决定是否实施它。仅需几个步骤即可添加现有行为,而无需复制代码。基于组件的系统允许非程序员进行更多更改。例如,实体的每种类型而不是由类表示,都可以由描述文件表示,该文件包含配置类型所需的所有组件名称和参数。这使非程序员无需重新编译游戏就可以进行和测试更改。只需编辑描述文件就足以添加,删除或更改每种实体类型使用的组件。

在特定情况和特定情况下的灵活性:在某些情况下,您需要您的玩家能够装备武器或物品,您可以向玩家添加库存组件。在游戏开发过程中,您将需要其他实体进行盘点(例如,敌人)。而且在您的场景中,有一种情况是一棵树应该包含某种隐藏项目。使用组件,您甚至可以将清单组件添加到类型最初不是设计为具有实体的实体,而无需额外的编程工作。

运行时的灵活性:组件提供了一种非常有效的方式来更改运行时实体的行为。您可以在运行时添加和删除组件,从而在需要时创建或删除行为和属性。它们还使您可以将不同类型的数据分为几组,这些组可以(有时)在多个GPU上并行地分别计算。

时间上的灵活性:组件可用于保持版本之间的兼容性。例如,在游戏的发行版之间进行更改时,您可以(并非总是)简单地添加实现新行为和更改的新组件。然后,游戏将根据加载的保存文件,对等连接或玩家加入的服务器,实例化连接到您实体的正确组件。在无法控制所有平台(Steam,Mac App Store,iPhone,Android ...)上新版本的发布日期的情况下,这非常方便。

为了获得更好的见解,请查看[基于组件][实体系统]标记的问题。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.