C ++游戏状态系统

时间:2010-07-13 17:05:17

标签: c++ state

好的:我对C ++和静态语言都很陌生。来自多年的红宝石(以及其他动态语言),我不知道这是否可行。

我一直在制作游戏状态系统......好吧游戏。我想让系统很容易在没有任何(或很少)更改的情况下剪切和粘贴到其他游戏中。

我想要改进的两件事是状态切换的方式以及状态指针的保存方式。

可能有任意数量的状态,但内存中至少会有2到3个状态。

丑陋1号。

目前我有一个类似这样的州经理班:

void StateManager::changeState(StateID nextStateID)
{
    // UNFOCUS THE CURRENT STATE //
    if (currentState())
    {
        currentState()->onUnFocus();

        // DESTROY THE STATE IF IT WANTS IT //
        if(currentState()->isDestroyedOnUnFocus()) {
            destroyCurrentState();
        }
    }

    if (m_GameStates[nextStateID]) {
        // SWITCH TO NEXT STATE //
        setCurrentState(nextStateID);
    }
    else
    {
        // CREATE NEW STATE //
        switch (nextStateID)
        {
        case MainMenuStateID:
            m_GameStates[MainMenuStateID] = new MainMenuState;
            break;
        case GameStateID:
                        m_GameStates[MainMenuStateID] = new GameStates;
            break;
        };
        setCurrentState(nextStateID);
    }

    // FOCUS NEXT STATE //
    currentState()->onFocus();
}

这种方法有效,但我觉得它不是很好。

是否可以传递一种类型?然后打电话给新人?

new NextGameState;  // Whatever type that may be.

poloymophism可以帮助吗?所有国家都来自class State

丑陋2号。

我认为需要改进的另一件事是我存储状态的方式。

State* m_GameStates[MaxNumberOfStates];

所有状态都初始化为NULL,因此我可以测试状态是否存在,如果没有,则在需要时创建状态。

它可以很好地调用当前状态:

m_GameStates[m_CurrentState];

但是,我不喜欢这个有两个原因。当任何时候只有2或3个指针处于活动状态时,如果有一个充满NULL指针的数组,那似乎有点浪费。 [编者注:第二个原因是什么?]

我考虑将其转换为vector_ptr,但不会因为检查状态是否存在而产生额外的复杂性。矢量似乎强化了Ugance No 1.因为我需要有一个列表来检查每个州。

任何建议或指示都表示赞赏。

谢谢, 菲尔。

6 个答案:

答案 0 :(得分:2)

使用枚举(eration)来定义所有可能的状态(类似于具有常量的列表)。 只需创建一个对象一个变量来保存状态,并在需要更改变量时更改变量。

答案 1 :(得分:2)

一旦你说国家,我就会想到State pattern

基本上,您可以从State基类派生一堆对象。与状态相关的所有操作都是针对状态管理器维护的当前状态发生的。各州将通过经理从一个州迁移到另一个州。

例如,您可以拥有Paused和Unbaused状态,每个状态都有一个buttonPressed事件。按下按钮时,将向当前状态发送事件。如果它处于暂停状态,按钮是暂停按钮,则移至“未暂停”状态。对于Unpaused,反之亦然。

答案 2 :(得分:2)

void StateManager::changeState(StateID nextStateID)
{
     leaveState(actualState); 
     enterState(nextStateID);
}

我真的很喜欢这个 - 尽可能容易。 ; - )

我想告诉你的是 - 我认为在changeState函数中创建/删除你的统计数据的逻辑太多了 - 它只是应该改变状态,对吗?

编辑: 来讨论你的2个问题 - 我不认为使用这个阵列真的是浪费 - 你说的是3个字段,而不是300个左右。所以如果你喜欢使用数组 - 去吧。如果你不这样做,那么地图将是我的选择,如果你想检查是否有一个状态被创建,它会让事情变得容易,你不仅限于幻数“maxStates”。您可以检查是否有足够的ram然后创建X状态,而不是固定2-3。

答案 3 :(得分:1)

用于生成您想要工厂的州。这样,状态id保持良好的通用性。对于存储状态,我会使用std :: map

答案 4 :(得分:0)

对于你的第一个问题,是的,你可以传递一个类型,但有一些警告。

我在你的问题下添加了评论,要求提供更多信息。在我们做到这一点之前,我不能说应该如何,而是阅读模板。

您可以创建一个函数模板,可以传递一个类型,例如:

template <typename T>
void Foo() {
  T* x = new T();
  ...
}

Foo<int>() // call Foo with the type T set to 'int'

这有一些限制,因为类型必须在编译时指定,但它是一个非常强大的语言功能。

另一个选项,可能更好,因为你似乎在变量(MainState)和类型(MainMenu)之间有关联,可能是使用traits类。同样,我不确定在你的情况下究竟是怎么做的,因为我们还没有看到整个函数(特别是MainState是什么类型,以及它是如何/何时创建的? )

也可能通过多态来解决问题,但同样,我需要看一些上下文来提出解决方案。

对于第二个问题,您可以使用标准库map

#include <map>

// I'm not sure what type m_CurrentState is, so use its type instead of KeyType below
std::map<KeyType, State*> m_GameStates;

// and to perform a lookup in the map:
GameStates[m_CurrentState];

最后,这是一个非常重要的建议:

停止在任何地方使用指针。停止调用new来创建新对象。 作为一般规则,应在堆栈上创建对象(而不是Foo* f = new Foo;,只需执行Foo f;

而不是使用指针,你通常只想复制对象本身。或者,创建引用而不是指针。

需要使用动态内存分配时,仍然不应直接使用new。相反,创建一个包装器对象,内部在其构造函数中使用new分配它所需的内容,并在析构函数中再次释放它。

如果你这样做,它几乎解决了内存管​​理的所有麻烦。

一般技术称为RAII

答案 5 :(得分:0)