7

我正在通过使用 SFML 和 OpenGL 制作一个小游戏来练习我的新手 C++ 技能。编程部分大部分都进​​行得很好,但我对实际代码/类设计有疑问。

我有一个类 MainLoop,它包含游戏循环并拥有以下每个类的一个实例:事件、图形、命令、游戏和 UI。我最初希望所有这些都是一个类(函数在不同的 .cpp 文件中分隔),但被告知这是 OOP/C++ 的错误方法。然而,虽然我可以看到分离它们的好处(封装、模块化、调试),但我似乎也遇到了很多不好的事情。让我以用户按下 UI 按钮为例。

首先,MainLoop 从 SFML 的窗口类中获取事件。MainLoop 将它发送到我自己的 Event 类,它解释事件并将其发送到 UI 类以检查它是否“点击”任何按钮。如果为真,则 UI 类将其发送到解释按钮命令的 Command 类。然后,最后,命令类将其发送到 Game 类或它需要去的任何其他地方。

对我来说,这一切似乎都非常沉重,而且至少按照我目前的做法,需要大量的前向声明(在我了解这些之前,我最终得到了大量的循环依赖) . 我怀疑它对性能也有多大好处。

无论如何,这里有什么我错过的技巧吗?这些类应该如何连接,它们应该如何通信?我应该如何将命令从 Event 类转发到 UI 类?我真的应该在任何地方都有前向声明、包含和东西,这不会破坏模块化吗?我是否应该让它全部通过 MainLoop 类并使用不需要声明的整数/浮点数/字符转发返回?我在这里有点不知所措。

4

2 回答 2

4

我可以向你推荐一些我在开发游戏时使用的设计,虽然它们肯定不是什么好东西,但我从来没有遇到过很多问题。我会保持简短,只是为了给你一个想法。

首先你需要一个视图管理器,这个管理器应该管理你游戏的当前视图,这可以实现为视图堆栈或其他任何东西。因此,您将拥有一个ViewManager知道游戏的所有视图并能够将内容分派到当前视图的类。

然后你需要一个抽象类GameView,它应该从外部提供基本接口,例如:

  • drawMe(), 绘制视图
  • receivedMouseEvent(Event e),将接收鼠标事件
  • activate()deactivate()执行在视图管理器中推送或弹出视图时应执行的操作

现在使用这个抽象类,您应该实现游戏的任何特定视图或部分视图,以便您可以在视图堆栈中推送和弹出它们。

好的是有子类来管理 UI 元素,例如ActiveArea响应点击的类,Button它继承自ActiveArea并且还能够提供两种状态的图形。这些元素应包含在存储在抽象视图中的可点击元素列表中,以便每个具体视图都可以将其按钮添加到通用实现中而无需担心。通过这种方式,您可以拥有类似(元代码)的东西

void AbstractView::receiveEvent(Event e) {
  for (ActiveArea *area in areas)
    if (area.isInside(e)) {
      area->action();
      return;
    }

  innerReceiveEvent(e); //which should be a pure virtual function that will call a method specified in concrete views
}

通过这种方式,您将让每个视图管理自己的状态,以及负责绘制和管理事件的视图管理器,例如

void ViewManager::draw() {
  for (AbstractView *view in views) // from top to bottom of the stack
    view.draw();
}
于 2012-12-02T18:26:04.230 回答
3

我可以想象它看起来很重,但这是正确的方法。请注意,函数调用一点也不繁重,它确实使整个事情更容易阅读。或者至少应该。;-)

每个类都应该有一个包含类定义的头文件,但不包含其成员函数的实现。任何文件都应该能够包含任何类头文件。只有当您使用模板(实现必须在头文件中)时,才可能存在循环依赖关系,但根据您的描述,我认为您没有它们。标题不需要相互包含。如果您需要在函数参数中传递指向其他类的指针或引用,则可以在标题的开头前向声明其他类。您应该能够在源文件的顶部包含任何内容。如果不是,请提供更多信息,说明您认为在您的情况下需要这样做的原因。

于 2012-12-02T18:26:40.160 回答