繁体   English   中英

在这种情况下如何擦除矢量元素?

[英]How to erase an vector element in this situation?

我做了一个游戏,rambo射击子弹和子弹击中了僵尸,我想从僵尸矢量中删除被击中的僵尸。

这个嵌套循环逐个检查每个僵尸和子弹之间的冲突。 它有效一段时间,但当我开始杀死更多时,它会崩溃,因为它想要使用已删除僵尸的功能。

for ( it = zombies.begin(); it != zombies.end(); ++it ) {
    it->attack();
    for (rambo.it = rambo.bullets.begin(); rambo.it != rambo.bullets.end(); ++rambo.it) {
        if(checkBasicCollision(it,rambo.it) && it != zombies.end()){
            zombies.erase(it);
        }
    }
}

我添加了it--; zombies.erase(it); 现在工作得更好但有时仍会崩溃。

我认为它的发生就像,例如有5个僵尸和20个子弹,僵尸迭代器是第二个僵尸,第二个僵尸开始子弹循环来检查它是否被击中。 循环开始,让第三个子弹击中僵尸,但循环仍在进行,即使僵尸被删除,它仍然继续循环。

我加了休息; zombies.erase(it); 现在它没有任何问题。 但代码看起来很脏。 有没有其他方法可以轻松擦除当前元素

虽然提出了手动擦除的解决方案,但请注意它不是最惯用的解决方案。 在惯用的C ++中,你可以在erase-remove习惯用法中使用std::remove_if算法,如下所示:

// 1. A predicate that check whether a zombie was it by any bullet:
auto is_zombie_hit = [&rambo](Zombie const& zombie) {
    auto is_bullet_hitting_zombie = [&zombie](Bullet const& bullet) {
        return checkBasicCollision(zombie, bullet);
    };

    return std::any_of(
        rambo.bullets.begin(),
        rambo.bullets.end(),
        is_bullet_hitting_zombie
    );
};

// 2. Use the erase-remove idiom:
zombies.erase(
    std::remove_if(zombies.begin(), zombies.end(), is_zombie_hit),
    zombies.end()
);

注意:是的,您可以就地使用lambda,但我更喜欢命名它们来表明它们的作用。

注意:这使用C ++ 11,但是用谓词替换lambda是微不足道的, any_of的实现很容易产生,就像all_ofnone_of

要使用erase您需要使用返回的值并将其分配回迭代器,以便它对下一次迭代有效。

for ( it = zombies.begin(); it != zombies.end(); ) {
    it->attack();
    for (rambo.it = rambo.bullets.begin(); rambo.it != rambo.bullets.end(); ++rambo.it) {
        if(checkBasicCollision(it,rambo.it) && it != zombies.end()){
            it = zombies.erase(it);    // erase will increment the iterator
        }
        else{
            ++it;    // no erase, increment the iterator manually
        }
    }
}

vector::erase的文档中,返回值为:

一个迭代器,指向函数调用擦除的最后一个元素后面元素的新位置。 如果操作擦除序列中的最后一个元素,则这是容器结束。

也许是这样的:

auto zombie_tail = std::remove_if(zombies.begin(), zombies.end(), [&](Zombie const & zombie) {
        zombie.attack();
        return std::any_of(rambo.bullets.begin(), rambo.bullets.end(), [&](Bullet const & bullet) {
            return checkBasicCollision(zombie, bullet);
        });
    });

zombies.erase(zombie_tail, zombies.end());

或者,如果您想远离c ++算法:

for (it = zombies.begin(); it != zombies.end(); ) {
    it->attack();

    // Attempt to find a bullet that hit.
    for(rambo.it = rambo.bullets.begin(); rambo.it != rambo.bullets.end(); ++rambo.it)
        if (checkBasicCollision(it, rambo.it))
            break;

    // Possibly remove the zombie, and advance the iterator
    if (rambo.it != rambo.bullets.end())
        it = zombies.erase(it);
    else
        ++it;
}

擦除向量元素时,迭代器和索引将失效。 你的代码也不正确2个或更多子弹击中同一个僵尸(如果可能的话)。 因为使用第二个子弹,内环将尝试擦除已经被击中的僵尸。 相反,你应该这样做:

for ( uint i = 0; i < zombies.size(); ++i)
{
    for( auto it = rambo.bullets.begin(); it != rambo.bullets.end(); ++it)
    {
         if(checkBasicCollision(zombies[i], it)
         {
             zombies.erase( zombies.begin() + i );
             --i;
             break; // zombie is dead (well, technically it was already dead)
             // so no further checks are needed (i.e. exit inner loop)
         }
     }
}

直接,易于阅读和掌握,但也许不是很花哨;

for ( auto& z : zombies )
    z.attack();

for( auto& b : rambo.bullets )
{
    auto itr = zombies.begin();
    while( itr != zombies.end() )
    {
        if( checkBasicCollision(b,*itr) )
            itr = zombies.erase(itr);
        else
            ++itr;
    }
}

checkBasicCollision现在接受引用,而不是迭代器

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM