簡體   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