介绍
首先,我必须说这是为在线游戏服务器完成的,它跟踪地图中的每个对象,无论是玩家还是 AI(NPC 或您想称呼它)。
它不仅必须跟踪他们,还必须在玩家之间通知他们附近的玩家。我已经解决了这个问题,我目前正在使用多线程方法,它非常有效。
我的问题
我将所有对象存储在哈希表中。我们可以认为哈希表是一个unordered_map
,虽然我实际上正在使用rde::hash_map
,因为它在插入和获取(自测)时更快,虽然需要更多的 RAM(现在不是问题)。
问题是,该映射存储对象的唯一 ID(64 位)以及对象指针,例如:
rde::hash_map<UInt64, Object*>
我的问题是:
我的应用程序(服务器)必须在一个循环中运行(在一个线程内),该循环必须每 50 毫秒调用一次,以保持运行顺利。循环代码如下所示:
void loop()
{
UInt32 prev = clock();
UInt32 prevSleep = 0;
while (1)
{
UInt32 diff = clock() - prev;
prev = clock();
maps.update() // Suppose map is a class, which stores the objects map
if (diff <= 50 + prevSleep)
{
prevSleep = 50 + prevSleep - diff;
sleep(prevSleep);
}
else
prevSleep = 0;
}
}
现在,到了这一点,map::update()
导致循环增加到 4500 毫秒值的函数。
每次调用更新时,地图对象必须检查是否有新对象添加到存储中,如果添加了一个对象,该对象必须通知所有其他对象它正在添加,我通过(伪代码)执行此操作:
foreach obectsToBeAdded as joiner:
foreach objectsList as object:
joiner->notify(object);
object->notify(joiner);
稍后,必须调用每个对象的内部更新,我这样做(再次伪代码):
foreach objectsList as object:
object->update();
而且,如果这还不够,则必须将上述循环扩展为:
foreach objectsList as object:
object->update()
// Visit all the other objects
// Called once every 1 sec for the object, not on every call
foreach objectsList as other:
if other != object:
object->visit(other)
我尝试优化这个
将第一个循环(添加和通知)与更新循环合并,如下所示:
foreach objectsList as object:
foreach objectsToBeAdded as joiner:
object->notify(joiner)
joiner->notify(object)
object->update()
// Called once every 1 sec for the object, not on every call
foreach objectsList as other:
if other != object
object->visit(other)
这在对象数量不大的情况下有效,只要它增加循环开始最多需要 4 秒,这远远超出了我正在寻找的 50 毫秒。
我的问题
有没有其他方法可以进一步优化?我虽然使用八叉树来跟踪地图中的对象位置,但后来得出的结论是它只会使问题恶化。
我还将每个地图划分为实体的 35 个单元(35 是“视图范围”,LOS),因此给定的rde::hash_map
仅包含彼此可以看到的单元,因此需要更新。只要对象计数低就可以工作......
我还能做什么?谢谢!
笔记
所有这些foreach
都是使用迭代器的循环,比如rde::hash_map<...>::iterator
from objects.begin()
toobjects.end()
其他优化,例如在没有玩家(真实用户,而不是 NPC)时不更新,在给定地图上没有玩家时释放内存等等,已经被考虑在内。