简体   繁体   English

SFML奇怪的射击错误

[英]SFML weird shooting bug

I'm making a game in C++ and SFML. 我正在用C ++和SFML开发游戏。 I have a weird bug, whenever I have exactly 1 enemy and 3 shots on the screen and a bullet collides with the enemy, the game crashes. 我有一个怪异的错误,每当我在屏幕上恰好有1个敌人和3个射门并且子弹与敌人碰撞时,游戏就会崩溃。 The error I get is: 我得到的错误是:

Expression: vector subscript out of range

This is how I check for collision between shots and enemies: 这是我检查镜头和敌人之间是否发生碰撞的方法:

for (int i = 0; i < enemies.size(); i++)
{
    for (int s = 0; s < shots.size(); s++) {
        if (Collision::PixelPerfectTest(enemies[i].getSprite(), shots[s].getSprite())) {
            enemies[i].setHealth(enemies[i].getHealth() - player.getDamage());
            if (enemies[i].getHealth() <= 0) {
                enemies.erase(enemies.begin() + i);
            }
            shots.erase(shots.begin() + s);
        }
    }
}

"enemies" and "shots" are vectors that I insert into every x seconds in the main loop. “敌人”和“镜头”是我在主循环中每隔x秒插入的向量。 Here's the code for that: 这是该代码:

    if (enemySpawner.getElapsedTime().asSeconds() >= 1.5f) {
        enemies.push_back(Enemy(spriteManager.enemySprite));
        std::cout << enemies.size() << " enemies" << std::endl;
        enemySpawner.restart();
    }

    if (shotSpawner.getElapsedTime().asSeconds() >= 0.3f &&      sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
        shots.push_back(Shot(spriteManager.shotSprite, player.getPosition(), *window));
        std::cout << shots.size() << " shots" << std::endl;
        shotSpawner.restart();
    }

As I said, this only happens when I have 1 enemy and 3 shots, otherwise it seems to work fine. 就像我说的那样,只有当我有1个敌人和3发射击时,这种情况才会发生,否则它似乎可以正常工作。

EDIT: Now the game freezes when the first enemy spawns. 编辑:现在,当第一个敌人产生时,游戏将冻结。 Here's the updated code: 这是更新的代码:

//Shot vs enemy
for (auto eit = enemies.begin(); eit != enemies.end();)
{
    for (auto sit = shots.begin(); sit != shots.end();) {
        if (Collision::PixelPerfectTest((*eit).getSprite(), (*sit).getSprite())) {
            (*eit).setHealth((*eit).getHealth() - player.getDamage());
            if ((*eit).getHealth() <= 0) {
                enemies.erase(eit);
                shots.erase(sit);
            }
            shots.erase(sit);
            eit = eit++;
            sit = sit++;
        }
    }
}

you can't just erase stuff from vectors while iterating over them the way you are doing because it will yield i values that won't be in the range of the vector anymore (after its size got decreased by the call to erase), which will basically result in trying to index an array value that does not exist anymore. 您不仅可以从向量中擦除内容,而且还可以按照您的方式对其进行迭代,因为它会产生i值,该值将不再位于向量的范围内(在通过调用delete减小其大小之后),基本上将导致尝试索引不再存在的数组值。 If you need that behavior, it's a good idea to use iterators: 如果您需要这种行为,则最好使用迭代器:

auto eit = enemies.begin();
for (; eit != enemies.end(); )
{
    if((*eit).isDead())
    {
         eit = enemies.erase(eit);
    }
    else
    {
         eit++;
    }
}

you would do the same for the shot array. 您将对镜头数组执行相同的操作。

@moka's answer explains the core problem with your code, and his/her code using iterators will fix your problem. @moka的答案说明了您的代码的核心问题,他/她的代码使用迭代器将解决您的问题。 However, there is another way to deal with the problem: splitting your code into a "computation pass" and an "array modification pass." 但是,还有另一种方法可以解决该问题:将代码分为“计算过程”和“数组修改过程”。 The code would look like: 代码如下所示:

for (int i = 0; i < enemies.size(); i++)
{
    for (int s = 0; s < shots.size(); s++) {
        if (!enemies[i].alive || !shots[s].active) {
            continue;
        }
        if (Collision::PixelPerfectTest(enemies[i].sprite(), shots[s].sprite())) {
            shots[s].active = false;
            enemies[i].setHealth(enemies[i].getHealth() - player.getDamage());
            if (enemies[i].getHealth() <= 0) {
                enemies[i].alive = false;
            }
        }
    }
}

enemies.erase(std::remove_if(enemies.begin(), enemies.end(),
    [](Enemy const &e) { return !e.alive; }), enemies.end());

shots.erase(std::remove_if(shots.begin(), shots.end(),
    [](Shot const &s) { return !s.active; }), shots.end());

This style is also good because it separates the game logic from the implementation a bit more. 这种风格也很不错,因为它将游戏逻辑与实现分开了更多。 For example, if you want to allow a shot to damage more than one enemy, you would have to use an approach like this for the shots array anyway. 例如,如果您想允许一枪击中一个以上的敌人,那么无论如何,您都必须对枪阵列使用这种方法。

Chapter on Double Buffering in the excellent Game Programming Patterns book goes into more depth. 出色的《游戏编程模式》一书中的“ 双缓冲”一章更加深入。

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

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