我正在制作一款游戏,但我在制作地图时遇到了麻烦。
在每次渲染/更新调用中,我都必须遍历列表中的每个块。 当我移动块边框时,会添加几个块(只需要添加或删除的块)并将其移除到列表中(逐个)。
这需要太长时间,并且会严重影响我的游戏性能。如果我删除更新循环,我的FPS会增加100!
for(pair<const DWORD, Chunk*>& pair : loadedChunks){
Chunk* c = pair.second;
if(c != NULL){
c->update(delta); //Does totally nothing
}
}
(atm列表中有205个块。)
这是我目前的实施:
unordered_map<DWORD, Chunk*> loadedChunks;
void ChunkManager::addChunk(int x, int y, int z){
Chunk* chunk = new Chunk(this, x, y, z);
rebuildChunks.push_back(chunk);
loadedChunks.emplace(((x & 0xFFFF)) << 16 | ((y & 0xFFFF) << 8) | (z & 0xFFFF), chunk);
}
void ChunkManager::removeChunk(int x, int y, int z){
long key = ((x & 0xFFFF)) << 16 | ((y & 0xFFFF) << 8) | (z & 0xFFFF);
delete loadedChunks[key];
loadedChunks.erase(key);
}
Chunk* ChunkManager::getChunkAt(int x, int y, int z){
if(loadedChunks.find(((x & 0xFFFF)) << 16 | ((y & 0xFFFF) << 8) | (z & 0xFFFF)) != loadedChunks.end()){
return loadedChunks[((x & 0xFFFF)) << 16 | ((y & 0xFFFF) << 8) | (z & 0xFFFF)];
}else{
return NULL;
}
}
我想知道是否有更快的地图,或者我的实施中是否有错误。
如果这是唯一可行的方法,我愿意使用外部库。
另一种可能性是使用XYZ的std::vector
列表的某种方式;
注意:
散列键:((x&amp; 0xFFFF))&lt;&lt; 16 | ((y&amp; 0xFFFF)&lt;&lt; 8)| (z&amp; 0xFFFF)。 不是很接近完美,因为它只允许从0到255的值,但它现在有效,然后使用字符串更好,因为字符串的性能甚至更差,根据article
编辑:
在评论中注意到FPS不是一个好的指标,所以我使用了这个计时器:
unsigned int start = clock();
for(pair<const DWORD, Chunk*>& pair : loadedChunks){
Chunk* c = pair.second;
if(c != NULL){
c->update(delta);
}
}
unsigned int timepassed = clock() - start;
OutputDebugString((LPCWSTR) wstring(to_wstring(timepassed) + L"\n").c_str());
输出介于1和1之间。 2毫秒。 (那就像我的渲染一样......只是为了循环一个包含205个项目的列表)
编辑2:
我决定通过添加计时器来减少循环次数:
tickTimer+=delta;
if(tickTimer >= 0.05){
for(pair<const DWORD, Chunk*>& pair : loadedChunks){
pair.second->update(delta);
}
tickTimer = 0;
}
这样它每秒只能运行20次。
编辑3:
@brianbeuning有一个非常好的观点,我使用的是MSVS并且正在使用调试模式,这样所有代码都运行得非常慢。
答案 0 :(得分:0)
我的精神力量表明地图中的项目数量超出了您的想象。也许程序中其他地方的错误不是从地图中删除项目。
修改主循环:
int count = 0;
int nullcount = 0;
for(pair<const DWORD, Chunk*>& pair : loadedChunks){
Chunk* c = pair.second;
if(c != NULL){
c->update(delta);
count++;
}
else {
nullcount++;
}
}
unsigned int timepassed = clock() - start;
OutputDebugString((LPCWSTR) wstring(to_wstring(timepassed) + L" items:" + to_wstring(count) + L" nullitems" + to_wstring(nullcount) + L"\n").c_str());
现在打印什么?
答案 1 :(得分:0)
好吧,我不知道你在优化时使用的编译器有多好,但你在removeChunk()和getChunkAt()中都要对它进行两次相同的评估。例如,首先在getChunkAt中存储由loadedChunks.find()
返回的迭代器,然后在单独的语句中测试它是否为NULL,如果不是,则返回其->second
。您可以在removeChunk()中执行类似的优化。
答案 2 :(得分:-1)
如果迭代速度非常重要,请尝试使用map
而不是unordered_map
- unordered_map
可以更快地查找/插入/删除在整个地图上使用和减慢迭代。