我正在通过使用SFML和OpenGL制作一款小游戏来练习我的新手c ++技能。编程部分在大多数情况下都很好,但我对实际的代码/类设计有疑问。
我有一个类,MainLoop,它包含游戏循环并拥有以下每个类的一个实例:事件,图形,命令,游戏和UI。我最初希望所有这些都是一个单独的类(在不同的.cpp文件中分隔函数),但被告知这是OOP / C ++的错误方法。然而,虽然我可以看到分离它们的好方面(封装,模块化,调试),但我似乎也遇到了很多不好的东西。让我举一个用户按下UI按钮的例子。
首先,MainLoop从SFML的窗口类中获取事件。 MainLoop将它发送到我自己的Event类,该类解释事件并将其发送到UI类以检查它是否“命中”了任何按钮。如果为true,则UI类将其发送到Command类,该类解释按钮命令。然后,最后,命令类将其发送到Game类或其他任何需要去的地方。
这对我来说似乎非常苛刻,并且至少我此刻一直在做的方式,还需要很多前瞻性声明(在我了解到那些我最终以大量的循环依赖)。我怀疑它对性能有好处。
无论如何,这里有一些我不知道的伎俩吗?这些课程应该如何联系,他们应该如何沟通?我应该如何将命令从事件类转发到UI类?我是否真的应该在任何地方都有前瞻性声明,包含和内容,这不会破坏模块化吗?我是否应该通过MainLoop类运行并使用不需要声明的整数/浮点数/字符转发返回值?我在这里有点不知所措。
答案 0 :(得分: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();
}
答案 1 :(得分:3)
我可以想象它似乎很重,但这是正确的方法。请注意,函数调用根本不重,它确实使整个事情更容易阅读。或者至少它应该。 ; - )
每个类都应该有一个包含类定义的头文件,但不包含其成员函数的实现。任何文件都应该能够包含任何类头文件。只有当您使用模板(实现必须在头文件中)时,可能存在循环依赖关系,但从您的描述中我不认为您拥有它们。标题不需要包含彼此。如果需要在函数参数中传递指针或引用其他类,可以在头文件的开头转发声明其他类。您应该能够在源文件的顶部包含任何包含。如果没有,请提供更多信息,说明您认为在您的案例中需要这样做的原因。