0

在游戏中,每帧都应该更新许多实体。我玩弄不同的设计模式来实现这一点。到目前为止,我有一个单例管理器类,每个 Logic 实例都添加到其中。但我正在考虑以下内容,即 Logic 类本身中的静态列表。这很好,因为它会从项目中删除一个类。本例中的“Engine”是调用 update_all 的主类。

class Logic
{
public:
    Logic() { all.push_back(this); }
    virtual ~Logic() { all.erase(this); }
    virtual void update(float deltatime) = 0;

private:
    friend Engine;
    static std::list<Logic*> all;
    static void update_all(float deltatime)
    {
        for (std::list::iterator i = all.begin(); i!=all.end(); ++i)
            (*i)->update(deltatime);
    }
};
  • 这种模式有名字吗?
  • 您认为这比单例管理器类更好吗?
  • 还有其他意见或警告吗?
4

6 回答 6

2

您也可以为此使用观察者模式

于 2009-03-23T15:28:13.560 回答
2

首先,您需要使用remove()而不是erase()(后者需要一个迭代器作为参数)

如果您使用稍微不同的循环,例如

std::list<Logic*>::iterator it = all.begin();
while (it != all.end()) {
  Logic* current = *it;
  ++it;
  current->update(deltatime);
}

您甚至可以克服siukurnin提到的问题(在 update() 期间删除逻辑对象)。list::remove()除了指向已移除元素的迭代器之外,不会使迭代器无效。

除此之外,我还投票赞成这是单例模式的一种变体。而且我建议将原始解决方案保留为单独的管理类,以防万一您希望拥有两个具有不同增量时间的循环或显式多线程支持(不同线程上的不同逻辑对象)或将来的任何内容。

在我看来,这是单例类相对于静态方法(您始终可以使用)的一般优势:如果您将来想这样做,您可以轻松地增加您的功能......

于 2009-03-23T15:41:46.553 回答
1

我认为它仍然是一个单例:“只能有一个”

单例是一种模式,一种概念:你可以用不同的方式实现它......

静态类成员或全局实例是同一想法的两种可能实现。

问题是:你为什么要改变它?

于 2009-03-23T15:15:24.073 回答
1

恕我直言,这是一种观察者模式(参见update对每个订阅者的调用),其中主题恰好是单例。

在更新观察者时注销的“警告”是一个困难的问题。我发现自己很多次都在为此苦苦挣扎。

这个问题的一个优雅的解决方案在我关于它的问题的答案中暗示:对于每个观察者,添加一个包含指向“真实”观察者的指针的中间“代理”。取消注册就相当于(原子地)交换代理的指针。更新后,可以安全地删除所有具有空指针的代理。

于 2009-03-23T15:42:25.040 回答
1

通常,您希望在每次更新调用时遍历游戏中的每个实体,因此您可以继续使用复合模式,其中您将拥有一个根节点。从此,您将递归地遍历节点并调用每个实体的 update() 方法。从我从您的代码中可以看出,您已经有了一个列表,但是使用复合模式,您可以改为创建实体组,这可能会简化您的任务。

据我了解,您的引擎只需要调用根节点的 Update() 方法(如果您使用复合模式)。从那以后,根节点将使用它们的 update() 调用后续节点。在通过复合树的某个时刻,您将接触到知道如何正确更新自己的叶子。

你只需要在你的引擎中有一个指向你的根节点的指针,它将有一个函数 UpdateAll() (或其他东西),然后调用 rootNode->Update(); 反过来,它将执行我在上一段中描述的操作。

于 2009-06-05T21:44:21.993 回答
0

需要注意的是,这种模式(当前)不允许在调用 update_all 期间删除逻辑实例,因为它会使迭代器指针无效。

一个解决方案可能是使析构函数私有并让更新返回一个标志,说明是否应该删除实例?

于 2009-03-23T15:13:10.990 回答