嵌套for循环引发Vector下标越界问题求助(附C++ SFML游戏代码)
分析与解决SFML游戏中Vector下标越界问题
从你给出的代码片段来看,虽然没写完完整的嵌套循环,但结合游戏开发中常见的场景,下标越界大概率是因为在遍历bulletVector或enemyVector时直接修改了容器(比如删除元素),而普通的基于索引的for循环没有处理容器大小变化的情况。
问题根源
举个典型的场景:假设你在遍历子弹的循环里嵌套了敌人碰撞检测,当子弹命中敌人时,你会从bulletVector中删除这个子弹。这时候问题就出现了:
- 原来的循环是
for (int i = 0; i < bulletVector.size(); i++),当你删除bulletVector[i]后,容器里的所有后续元素会往前移动一位,size()也会减小。 - 下一次循环
i++,就会跳过原本在i+1位置的元素;如果删除的是最后一个元素,i会直接超过新的size()-1,导致访问bulletVector[i]时触发下标越界。
比如这段有问题的碰撞检测代码:
for (int i = 0; i < bulletVector.size(); i++) { bulletVector[i].fire(); // 嵌套遍历敌人检测碰撞 for (int j = 0; j < enemyVector.size(); j++) { if (isColliding(bulletVector[i], enemyVector[j])) { // 删除命中的子弹和敌人 bulletVector.erase(bulletVector.begin() + i); enemyVector.erase(enemyVector.begin() + j); // 这里i没有调整,下一次循环会跳过元素甚至越界 } } }
解决方案
针对这种遍历中需要修改容器的场景,有两种常用的可靠方法:
方法1:使用迭代器遍历并处理删除
迭代器是STL容器设计用来安全遍历的方式,erase方法会返回下一个有效的迭代器,我们可以利用这一点避免越界:
// 遍历子弹容器 for (auto bulletIt = bulletVector.begin(); bulletIt != bulletVector.end(); ) { bulletIt->fire(); bool bulletErased = false; // 遍历敌人容器检测碰撞 for (auto enemyIt = enemyVector.begin(); enemyIt != enemyVector.end(); ) { if (isColliding(*bulletIt, *enemyIt)) { // 删除敌人,迭代器自动指向下一个元素 enemyIt = enemyVector.erase(enemyIt); // 删除子弹,更新子弹迭代器 bulletIt = bulletVector.erase(bulletIt); bulletErased = true; break; // 子弹命中后无需继续检测其他敌人 } else { // 没有碰撞,移动敌人迭代器 ++enemyIt; } } // 如果子弹没被删除,移动子弹迭代器 if (!bulletErased) { ++bulletIt; } }
方法2:反向遍历容器
反向遍历(从最后一个元素到第一个)时,删除当前元素不会影响前面未遍历的元素索引,因为索引是递减的:
// 反向遍历子弹 for (int i = bulletVector.size() - 1; i >= 0; i--) { bulletVector[i].fire(); // 反向遍历敌人 for (int j = enemyVector.size() - 1; j >= 0; j--) { if (isColliding(bulletVector[i], enemyVector[j])) { bulletVector.erase(bulletVector.begin() + i); enemyVector.erase(enemyVector.begin() + j); break; } } }
额外小建议
你代码里的sleep(milliseconds(5))会阻塞游戏主线程,导致游戏卡顿,建议用时间差来控制发射间隔,不要用sleep。比如:
if (Keyboard::isKeyPressed(Keyboard::Space)) { if (fireClock.getElapsedTime().asMilliseconds() > 150) { Bullet vecBullet(Vector2f(player->getPosition()), player->angle); bulletVector.push_back(vecBullet); fireClock.restart(); // 去掉sleep,用时钟控制间隔更流畅 } }
内容的提问来源于stack exchange,提问作者Gavin Kerr
相关产品推荐
相关产品推荐

