在一个作用域中推送五个对象指针,然后检查对象的布尔值是否为 false,会给出错误



我正在为我的大学项目开发一个游戏,遇到了指针问题。我有一个子弹矢量,可以检查每颗子弹是否有碰撞。但是,当我尝试一次生成 5 颗子弹(霰弹枪射击)时,它会正确生成所有内容,然后我浏览矢量并检查子弹是否还活着。由于某种原因,它会找到 NULL 指针或指针的损坏数据。如果有人知道如何解决这个问题,将不胜感激。谢谢!

项目符号检查:

void Bullet::checkOutOfRange()
{
if(type == "enemy_bullet")
{
if(body->GetPosition().x <= -10.0f)
{
alive = false;
}
}
// checks if the body is out of the window size
else if (body->GetPosition().x > 510.0f || body->GetPosition().x < -10.0f || body->GetPosition().y > 291.0f || body->GetPosition().y < -10)
{
alive = false;
}
}

子弹碰撞检测:

class MyContactListener : public b2ContactListener
{
void BeginContact(b2Contact* contact) override
{
if (contact && contact->IsTouching())
{
Entity* A = static_cast<Entity*>(contact->GetFixtureA()->GetBody()->GetUserData());
Entity* B = static_cast<Entity*>(contact->GetFixtureB()->GetBody()->GetUserData());

//------
if((A->type == "player_bullet" && B->type == "enemy") || (A->type == "enemy" && B->type == "player_bullet"))
{
Bullet *bullet = nullptr;
Enemy *other = nullptr;
if (A->type == "player_bullet" && B->type == "enemy")
{
bullet = dynamic_cast<Bullet*>(A);
other = dynamic_cast<Enemy*>(B);
}
else if (A->type == "enemy" && B->type == "player_bullet")
{
bullet = dynamic_cast<Bullet*>(B);
other = dynamic_cast<Enemy*>(A);
}
if ((A && B) || (B&&A)) 
{
other->bodySprite.set_animation("death");
bullet->alive = false;
other->alive = false;
}
}
}
}
}

游戏中的子弹检查:

for (auto itr = player_bullets.begin(), itrEnd = player_bullets.end(); itr != itrEnd; ++itr)
{
(*itr)->checkOutOfRange(); // go to declaration to see where the bullets stop
(*itr)->bodySprite.set_position((*itr)->body->GetPosition().x, (*itr)->body->GetPosition().y);
(*itr)->bodySprite.set_rotation((*itr)->bulletDir * 180 / PI);
(*itr)->moveBullet(20.0f);
if ((*itr)->alive == false)
{
input_world->DestroyBody((*itr)->body);
delete *itr;
itr = player_bullets.erase(itr);
}
}

霰弹枪射击生成:

void spawnShotgunShot(float x, float y, float dir, b2World *world)
{
// maybe bad pointer management?
Bullet *bullet_ptr1 = new Bullet(x, y, dir, world, "player_bullet", PLAYER_BULLET, ENEMY);
player_bullets.push_back(bullet_ptr1);
Bullet *bullet_ptr2 = new Bullet(x, y, dir + 0.5, world, "player_bullet", PLAYER_BULLET, ENEMY);
player_bullets.push_back(bullet_ptr2);
Bullet *bullet_ptr3 = new Bullet(x, y, dir + 0.25, world, "player_bullet", PLAYER_BULLET, ENEMY);
player_bullets.push_back(bullet_ptr3);
Bullet *bullet_ptr4 = new Bullet(x, y, dir - 0.5, world, "player_bullet", PLAYER_BULLET, ENEMY);
player_bullets.push_back(bullet_ptr4);
Bullet *bullet_ptr5 = new Bullet(x, y, dir - 0.25, world, "player_bullet", PLAYER_BULLET, ENEMY);
player_bullets.push_back(bullet_ptr5);
}

这是一个包含 sln 的测试版本: 链接到测试构建

您的问题是检查项目符号的循环中的itrEnd变量。 在循环开始时将其初始化为player_bullets.end(),然后检查itr != itrEnd作为循环条件。 但是,当您从向量中擦除项目符号时,这会在擦除的迭代器(包括结束迭代器)之后使迭代器失效。 您将继续使用矢量的原始结束迭代器,而不是基于其较小尺寸的新迭代器。

删除itrEnd变量,只需检查itr != player_bullets.end()作为循环条件。 这将确保您始终使用"当前"结束迭代器,即使它由于元素被擦除而更改。


在相关说明中,当您从向量中删除元素时,从erase调用中返回的迭代器将用于被擦除的元素之后的元素。 在下一次迭代开始时,循环的++itr将前进到该元素之外,然后再次检查循环条件。 这意味着每当您从矢量中删除项目符号时,您的循环都会"跳过"它之后的项目符号。 如果最后一个项目符号被删除,循环将"跳过"结束迭代器并继续迭代到向量的末尾。

有关如何解决此问题的信息,请参阅此 SO 问题。

最新更新