3

我一直对我正在处理的程序的基本结构有疑问。我是一个非常缺乏经验的程序员,试图自学使用多种状态的程序的基础知识。

现在,我有一个非常简单的游戏式程序,它带有一个游戏循环,将事件、逻辑和渲染重定向到我的 StateManager 类,它将状态推送和弹出到 . StateManager 类然后将事件、逻辑和渲染重定向到向量的 back() 上的任何状态。这个想法是为程序的每个阶段(在这种情况下是一个简单的游戏,包括启动画面、菜单、游戏玩法、死亡画面等)提供各种不同的状态......

但是,我是一个非常新手的编码员(尽我所能去学习),而且我从第一个状态课程开始就遇到了我的程序的一个基本问题......

我制作的第一个类是 SplashScreenState。基本概念是有一个状态,基本上只显示一系列“启动屏幕图像”(例如,假设为 3),每次用户按下一个键,它切换到下一个图像,并且最后(当它没有启动画面图像循环时)切换到下一个状态(菜单状态)。

我的问题是我很难弄清楚如何构建它。最初,我错误地将每个不同的初始屏幕图像视为 SplashScreenState 的一个实例。但是,我认为这样做是不正确的,因为从技术上讲,所有 3 个启动画面都是同一个“状态”的一部分。

所以现在我有两个主要问题:

  • 第一个问题是我不确定如何/在哪里存储所有启动画面图像。如果我想在程序启动时在 3 个不同的屏幕图像之间循环,我应该让它们都成为 SplashScreenState 类的成员吗?或者简单地为“currentImage”设置一个类成员是否更聪明,并且每次用户点击一个键时,它都会运行一个 load() 函数将下一张图像加载到 currentImage 指针中?制作图像数组或矢量并循环浏览它们会更好吗?我只是不确定...

  • 我的第二个问题是 SplashScreenState 的 eventhandling() 。我知道我希望屏幕上的图像像这样改变;*image1 -> image 2 -> image 3 -> changeState(menuState).. 这样每次用户按下键盘上的一个键时,它就会切换到下一个启动画面,直到最后一个启动画面,然后它会改变状态到主菜单。我也不确定最好的方法是什么。我应该为每个初始屏幕创建一个枚举并通过它们递增(直到它改变状态的最终屏幕)?我还认为,如果我确实将所有各种屏幕存储在一个数组中,那么我可以轻松地递增它们,但是那会不会优化,因为所有屏幕都必须始终存储在内存中?

无论如何,我知道这个问题可能是非常基本和愚蠢的,但不幸的是,这就是我现在所处的位置!我没有接受过任何正规的编程教育,而且我一直在自学,所以我真的非常感谢这个网站上提供的所有帮助和专业知识!^^

谢谢!

4

2 回答 2

1

您似乎在面向对象与处理状态转换的过程范式之间左右为难。另一个建议使用 switch 语句来处理枚举状态更改的答案是一种很好的程序方法。这样做的缺点是您最终可能会得到一个单一的游戏类,其中包含所有代码和所有多余的特定于状态的数据,用于处理所有可能状态的事件/逻辑/渲染。面向对象的处理方式更加简洁,并将它们封装到它们自己独立的状态对象中,可以通过共享接口多态地使用它们。然后,您的游戏类不需要保存用于处理游戏类中所有状态的所有细节,而只需要存储一个状态指针,而不必担心具体状态对象的实现细节。这将处理状态转换的责任从游戏类移到它所属的状态类中。您应该阅读设计模式书籍中的状态/策略模式。处理状态的变化应该是状态对象本身的责任。这里有一些阅读:

http://www.codeproject.com/Articles/14325/Understanding-State-Pattern-in-C

http://sourcemaking.com/design_patterns/state

http://sourcemaking.com/design_patterns/state/cpp/1

http://codewrangler.home.comcast.net/~codewrangler/tech_info/patterns_code.html#State

http://en.wikipedia.org/wiki/State_pattern

http://www.codeproject.com/Articles/38962/State-Design-Pattern

引用 Design Patterns 和 Pattern-Oriented Software Architecture 书籍:基于模式的方法使用代码而不是数据结构来指定状态转换,但在适应状态转换操作方面做得很好。状态模式没有指定必须在哪里定义状态转换。它可以在上下文对象中完成,也可以在每个单独的派生状态类中完成。让状态子类指定它们的后继状态以及何时进行转换通常更加灵活和合适。

您可以选择提前创建状态对象并且从不销毁它们,这在状态变化迅速发生并且您希望避免销毁可能很快再次需要的状态时会很好。另一方面,它可能不方便,因为上下文必须保留对所有可能进入的状态的引用。

当将要进入的状态在运行时未知并且上下文不经常更改状态时,最好根据需要创建状态对象并在之后销毁它们。在确定使用哪个时,您需要考虑成本和转换频率。

于 2012-08-12T09:40:34.243 回答
1

首先,对您的问题的出色解释。

管理启动画面的游戏部分可以通过两种方式工作。你已经检查了这个问题,它真的很简单:

接收输入;设置下一个状态。

所以,例子:

 STATE_SPLASH1
 STATE_SPLASH2
 STATE_SPLASH3
 STATE_TITLE
 STATE_GAME_INIT
 STATE_GAME
 STATE_EXIT

伪代码:

state = STATE_SPLASH1

while (state != STATE_EXIT) 
  ... receive input ...
  ... process events to responders ...
  ... update ...
  ... yadda yadda ...
  switch (state) {
    case STATE_SPLASH1:
      show_splash1()
    case STATE_SPLASH2:
      show_splash2()
    case ..:

    case STATE_TITLE:
      show_title()
    case STATE_GAME_INIT:
      show_loading()
      setup_level()
      setup_characters()
    case STATE_GAME:
      game_update()
    case STATE_EXIT:
      cleanup_and_quit()

另一种方法是将启动画面管理为“游戏状态”,然后将启动画面的状态作为内部状态。当 splash 没有更多的逻辑可以运行时,将游戏状态设置为下一个。在我学习的时候,我发现 DOOM 源是一个宝贵的资源和人,它只不过是数百个状态机。:)

于 2012-08-12T09:50:35.363 回答