【问题标题】:Structuring a state based game engine, and Makefile structure help?构建基于状态的游戏引擎和 Makefile 结构有帮助吗?
【发布时间】:2011-09-21 18:12:58
【问题描述】:

我正在开发一个基于状态的游戏引擎,并且有一些我很满意的东西作为开始。

有一个抽象类 GameState.hpp,其中包含我使用的虚拟方法(初始化、运行、暂停等)。

GameEngine.cpp/hpp 是一个包含一堆 GameState 对象并通过运行当前状态的相关方法来设置游戏循环的类。

我的测试游戏 TestGame.cpp 创建了一个 GameEngine 对象并推送一个 TestState 实例并运行,等等……一切都按我的预期工作。

我想构建我的源代码树而不是从同一个目录编译所有内容,并且正在考虑以下问题,因为每个游戏会有多个状态:

src/
  +Engine/
    +GameEngine.cpp
    +GameEngine.hpp
    +GameState.hpp
  +TestGame/
    +States/
      +TestState.cpp
      +TestState.hpp
    +TestGame.cpp

在构建时,我不确定我是否了解需要编译什么,以及编译后的对象应该去哪里。

到目前为止,我最初的想法是:

  1. 用 GameState.hpp 编译 GameEngine.cpp/hpp,得到 GameEngine.o

  2. 编译每个游戏状态,例如。 TestState.cpp/hpp 和 GameState.hpp,给出 TestState.o

  3. 用 GameEngine.o/hpp、GameState.hpp、TestState.o/hpp(和任何其他状态)编译 TestGame.cpp/hpp,得到 TestGame.bin

我在正确的轨道上吗? GameEngine 应该构建一个库,还是一个常规的 .o 可以?对于是否需要在每个编译中包含标题,我仍然有点模糊。此外,每个编译的输出文件是否与源文件位于同一目录中,还是应该位于结构化 bin/ 目录中?

我会试一试并发布一些 Makefile 尝试和输出。让我知道我是否应该发布代码,总共可能有 200 行。没有实际的图形实现等...目前只是一个框架。

谢谢大家,非常感谢任何帮助!

编辑:感谢大家的快速回复。我已经阅读了下面的 cmets,并希望提供更新并澄清一些事情。

首先,我正在研究自动工具,但这主要是一个学习项目,其中一部分是探索 makefile。我也有 Java 背景,所以我真的很想了解我正在构建什么以及我的项目如何组合在一起。如上所述,我什至不知道应该是库还是只是在编译时指向。

我的目标概述:我几乎将引擎视为一个子项目。即使它很简单(handle events、update、display] 方法。我有一个算法,旨在每秒更新恒定数量,同时根据需要改变帧速率。

例如,states display 方法将利用引擎的 SDL 支持的显示功能(可能是磁贴引擎,状态可以说“加载这些资源,显示这些磁贴”或小部件库,无论如何...)来渲染游戏世界模型的表示。 handle events 方法可以使用抽象的键盘接口。我希望能够在引擎中添加这样的功能。 Update 将管理一个代表游戏世界的模型。它将跟踪世界中的对象(场景、玩家和 np),根据物理和碰撞检测等应用运动......

我想我希望能够构建它并在我正在开发的游戏中包含对象(和标题?)。 '将引擎源目录复制到游戏项目的根目录,并将其添加到主 Makefile,一起构建',或 '只要引擎发生变化,就在引擎上运行 make,使用最新引擎对象(共享对象,其他东西?)和标题的副本构建游戏项目', 使用一个引擎处理多个小型游戏项目对我来说最容易的是什么?我有点想做好自己的准备,这样我就可以随心所欲地用随机的游戏创意开始工作,比如 2 周的编码组合等......

状态代表任何不同的视图和交互集。引擎有一个 LIFO 状态堆栈,所以一个简单的游戏可能在堆栈上有以下状态: 驾驶模式 - 暂停菜单 - 设置 实际游戏有一个 GameEngine 实例,并且会添加 DrivingMode 来启动游戏。用户暂停,这会暂停 DrivingMode 状态并推送 PauseMenu 状态(在推送时运行其初始化、运行等)。在这里,用户选择暂停 PauseMenu 状态并加载设置的设置...当它们退出时,会弹出状态(清理等...首先运行),然后恢复顶部状态。

哇,现在好多了。随着我的进步,我会添加更多或提出新问题。再次非常感谢您的帮助。

【问题讨论】:

  • +1 提出了一个很好的问题!你绝对是在正确的轨道上,你所要求的实际上是一个品味问题:做任何你喜欢的事情。提示:链接库比链接多个目标文件更简单。
  • 谢谢,我试图弄清楚我能做什么,并问到底是什么阻碍了我。这是一个比任何东西都重要的学习项目,因此是手工制作的 makefile。我正在尝试从 Java 世界中转移出来,所以我正在花时间准确了解我在做什么。
  • 如果目标是学习,那么 autotools 绝对是您应该花一些时间在上面的东西。正如 vines 和 Eelke 所说,引擎的架构基本上归结为个人喜好。如果您可以证明设计选择的合理性,那么它很可能是一种可行的做事方式。

标签: c++ oop architecture makefile


【解决方案1】:

首先,您真的需要将状态模块化吗?如果您打算为特定目的编写引擎,则您的状态可能不会发生太大变化,因此应该将其编译为引擎本身的一部分。

将状态与引擎分离会增加额外的复杂性,并使实现更加棘手。

下一个问题是您是否希望您的引擎成为游戏链接以便使用的库,或者是否将引擎的源代码放在游戏的子文件夹中并与它一起编译。

您的选择是:

  • 将引擎和状态编译为一个库(可能是您链接游戏的静态库)
  • 将引擎编译为静态库,然后将状态编译为可以动态链接的共享对象(这适合模块化状态选项,但会增加很多复杂性)
  • 将整个引擎 + 游戏编译成一个大的可执行文件,无需任何库链接(但也使引擎和游戏更加耦合)

在我看来,选项 1 看起来是最好的。

无论选择哪一种,我都会将引擎保存在游戏中自己的子目录中。到目前为止,您的文件结构看起来相当稳固,您应该坚持下去。

如果您仍然选择允许游戏定义自己的状态,那么您将需要选择选项二并在您的引擎中实现一些核心状态(可能类似于 INIT、MAIN_MENU、SHUT_DOWN)以及类似一个“插件管理器”,在这种情况下,它将负责向游戏引擎注册游戏的状态。

然后可以将每个状态单独编译为共享对象 (.so),并在加载游戏时动态加载。

至于 Makefile,这真的取决于你打算使用什么(CMake、GNU autotools 等)

GNU Makefile 的语法非常简单,但学习如何使用它们可能有点棘手。

它们由如下定义的目标组成:

default:
    gcc [...]

all:
    gcc [...]
    gcc [...]
    <...>

第一个目标是不带参数调用 make 时的默认目标 我不会深入描述如何使用 makefile(对于 GNU Make:this link

您应该知道,makefile 很快就会变得非常繁琐,因此通常首选 autotools 这样的解决方案。

请注意,autotools 是一个由许多程序组成的非常大的系统,学习起来可能非常耗时。如果你想专注于开发你的游戏引擎,我强烈建议使用像Code::blocks这样的项目IDE来为你做代码编译和链接。

编辑

作为对您的修改的回应,我将添加以下内容:

您似乎非常清楚自己的目标,这是最重要的部分。根据您的说法,如果让游戏引擎成为您的许多游戏链接的自己的静态库,您会更好。

然后您的引擎将提供一个接口来注册游戏将使用的状态来表示“这是我的状态,称为 title”,然后会告诉引擎“切换到称为 title 的状态”随时需要。

您可能需要考虑的一件事是拥有一些所有游戏都必须注册的基本状态(由引擎本身强加)。

然后引擎将成为它自己的项目,例如,游戏项目只需在 /lib 中拥有引擎库的副本。然后,您将在编译时将引擎静态链接到您的游戏。

如果您想了解有关如何构建状态对象的更多详细信息,请告诉我。


【讨论】:

  • 我会推荐 cmake 而不是 autotools - 它更加通用(并且 autotools 甚至不是跨平台!)。我个人更喜欢 qmake,因为它是真正简单而灵活的工具。
【解决方案2】:

您提出的许多问题都没有明确的答案,这通常是个人喜好。

我从你的问题中得到的印象是你也编译你的头文件,你不应该编译头文件,它们应该只包含在 .cpp 文件和其他头文件中。然后将它们编译为 cpp 的一部分。文件应该只包含他们真正需要的标题。

只要您的项目很小,将所有内容保存在一个目录中通常是最好的,但如果您知道它会增长,那么设置一个好的目录结构是个好主意。像您一样将可能可重用的 GameEngine 从实际游戏中分离出来是一件好事。

当该部分将在其他项目中重复使用时,或者当您的项目变得如此之大以至于 .o 文件的数量变得非常多时,创建一个库非常有用。因此,如果这适用于 GameEngine,请使其成为一个库。对于像游戏引擎这样的可重用项目,将标头保存在单独的目录中通常也很有用。因为依赖它的项目只需要头文件和库,所以它们不需要其余的源。

当项目变得更大时,使用单独的源代码、中间 (.o) 和最终可执行文件目录有助于保持文件的可管理性,因为将 .o 与源代码一起存储通常会使 src 目录中的文件数量增加 50%。

如果你想很好地设置它,你可能想看看 autoconf 和 automake。他们有一个陡峭的学习曲线,但他们会从你手中夺走大量的 Makefile 写作。这些工具用于创建许多开源包的源代码分发中的配置脚本。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-03
    • 1970-01-01
    相关资源
    最近更新 更多