And*_*nko 3 c++ iterator vector stdvector visual-c++
我有一个游戏,检查子弹与敌人之间的碰撞,并将其存储为2个矢量容器。人们说,如果您要删除for循环中的元素,则最好使用迭代器,所以我做到了。但是现在我有一个将迭代器传递给函数的问题。问题是我不必删除元素,因此它必须更复杂一些。
这是我检查碰撞的方法。“ CircularCollision”工作正常,没有错误。
void ResolveColision(Weapon &weap, Map &map)
{
std::vector<Bullet> bullets = weap.GetBullets();
if (!bullets.empty())
{
for (std::vector<Bullet>::iterator i = bullets.begin(); i != bullets.end(); ++i)
{
std::vector<Enemy> enemies = map.GetEnemies();
if (!enemies.empty())
{
for (std::vector<Enemy>::iterator j = enemies.begin(); j != enemies.end(); ++j)
{
if (CircularCollision((*i), (*j)))
{
weap.DeleteByIndex(i);
map.TakeDamageByIndex(j, weap.GetDamage());
std::cout << "HIT!\n";
}
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是应该减少敌人生命值的方法:
void Map::TakeDamageByIndex(std::vector<Enemy>::iterator &itr, int damage)
{
(*itr).SetHealth((*itr).GetHealth() - damage);
}
Run Code Online (Sandbox Code Playgroud)
这是删除项目符号的方法:
void Weapon::DeleteByIndex(std::vector<Bullet>::iterator &itr)
{
destroySprite((*itr).GetSprite());
bullets.erase(itr);
}
Run Code Online (Sandbox Code Playgroud)
我敢肯定它看起来很可怕并且不起作用,但我不知道如何正确执行。请帮忙!同样,当for循环使用索引(例如bullets [i])操作时,这两种方法都可以正常工作,在这种情况下,问题出在“向量下标超出范围”错误。
在中DeleteByIndex(),更改此:
bullets.erase(itr);
Run Code Online (Sandbox Code Playgroud)
对此:
itr = bullets.erase(itr);
Run Code Online (Sandbox Code Playgroud)
std::vector::erase()将迭代器返回到已删除元素之后的下一个剩余元素。下一个元素是您的外循环在下一个迭代中需要继续的地方。
因此,您需要将外部循环从a for改为a while,否则您将跳过元素(实际上,当您仍在使用索引时,原始代码会遇到该问题):
void ResolveColision(Weapon &weap, Map &map)
{
std::vector<Bullet> bullets = weap.GetBullets();
std::vector<Bullet>::iterator bullerItr = bullets.begin();
while (bullerItr != bullets.end())
{
std::vector<Enemy> enemies = map.GetEnemies();
bool wasAnyHit = false;
for (std::vector<Enemy>::iterator enemyItr = enemies.begin(); enemyItr != enemies.end(); ++enemyItr)
{
if (CircularCollision(*bulletItr, *enemyItr))
{
wasAnyHit = true;
weap.DeleteByIndex(bulletItr);
map.TakeDamageByIndex(enemyItr, weap.GetDamage());
std::cout << "HIT!\n";
break;
}
}
if (!wasAnyHit)
++bulletItr;
}
}
Run Code Online (Sandbox Code Playgroud)
话虽这么说,我建议std::find_if()改为用内循环代替。并且重命名DeleteByIndex(),TakeDamageByIndex()因为它们不再使用索引了。实际上,我根本不会传递迭代器TakeDamage...(),Enemy而是传递实际的对象。或者更好地,TakeDamage()进入Enemy自己。
尝试更多类似这样的方法:
void ResolveColision(Weapon &weap, Map &map)
{
auto bullets = weap.GetBullets();
auto bulletItr = bullets.begin();
while (bulletItr != bullets.end())
{
auto enemies = map.GetEnemies();
auto &bullet = *bulletItr;
auto enemyHit = std::find_if(enemies.begin(), enemies.end(),
[&](Enemy &enemy){ return CircularCollision(bullet, enemy); }
);
if (enemyHit != enemies.end())
{
weap.DeleteBulletByIterator(bulletItr);
enemyHit->TakeDamage(weap.GetDamage());
std::cout << "HIT!\n";
}
else
++bulletItr;
}
}
void Enemy::TakeDamage(int damage)
{
SetHealth(GetHealth() - damage);
}
void Weapon::DeleteBulletByIterator(std::vector<Bullet>::iterator &itr)
{
destroySprite(itr->GetSprite());
itr = bullets.erase(itr);
}
Run Code Online (Sandbox Code Playgroud)