在游戏中,每帧都应更新许多实体。我玩弄不同的设计模式来实现这一目标。到目前为止,Ive有一个单例管理器类,每个逻辑实例都添加到该类中。但我考虑以下,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);
}
};
答案 0 :(得分:2)
您也可以将observer pattern用于此
答案 1 :(得分:2)
首先,您需要使用remove()
而不是erase()
(后者需要迭代器作为参数)
如果使用稍微不同的循环,如
std::list<Logic*>::iterator it = all.begin();
while (it != all.end()) {
Logic* current = *it;
++it;
current->update(deltatime);
}
您甚至可以解决提到的 siukurnin 问题(在update()期间删除Logic对象)。除了指向已删除元素的迭代器之外,list::remove()
不会使迭代器失效。
除此之外,我还投票支持这是单身人士模式的变体。我建议将原始解决方案与单独的管理类保持一致,以防万一你需要有两个不同delta时间的循环或显式多线程支持(不同线程上的不同Logic对象)或将来的任何东西。
在我看来,这是单例类相对于静态方法的一般优势(你可以随时使用):如果你想在未来这样做,你可以轻松地增加你的功能......
答案 2 :(得分:1)
我认为它仍然是单身:“只能有一个”
单身是一种模式,一种概念:你可以用不同的方式实现它......
静态类成员或全局实例是同一想法的两种可能实现。
问题是:你为什么要改变它?
答案 3 :(得分:1)
Imho,它是一个Observer模式(cfr update
调用每个订阅者),其中Subject恰好是Singleton。
更新观察员时取消注册的“警告”很难。我发现自己经常多次挣扎。
关于这个问题的优雅解决方案在this answer中暗示我的问题:对于每个观察者,添加一个包含指向“真实”观察者的指针的中间“代理”。取消注册相当于交换(原子地)代理的指针。更新后,可以安全地删除所有带空指针的代理。
答案 4 :(得分:1)
通常,您希望在每个更新调用中浏览游戏中的每个实体,因此您可以继续使用Composite pattern,其中您有一个根节点。从那里,您将递归遍历节点并调用每个实体的update()方法。从我在代码中看到的内容,您已经有了一个列表,但是使用复合模式,您可以创建实体组,这可以简化您的任务。
据我所知,您的引擎只需要调用根节点的Update()方法(如果使用复合模式)。从那以后,根节点将使用他们的update()调用后续节点。在某些时候,通过复合树,您将获得知道如何正确更新自己的叶子。
您只需要在引擎中有一个指向根节点的指针,该指针将具有一个函数UpdateAll()(或其他东西),然后调用rootNode-&gt; Update();反过来,它将完成我在前一段中描述的内容。
答案 5 :(得分:0)
有一点需要注意,这个模式(当前)不允许在调用update_all期间删除Logic实例,因为它会使迭代器指针无效。
解决方案可能是让析构函数变为私有并让更新返回一个标志,说明是否应删除该实例?