【问题标题】:SFML weird shooting bugSFML 奇怪的拍摄错误
【发布时间】:2023-03-08 10:29:01
【问题描述】:

我正在用 C++ 和 SFML 制作游戏。我有一个奇怪的错误,每当我在屏幕上恰好有 1 个敌人和 3 次射击并且一颗子弹与敌人相撞时,游戏就会崩溃。我得到的错误是:

Expression: vector subscript out of range

这是我检查射击和敌人之间碰撞的方式:

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”和“shots”是我在主循环中每隔 x 秒插入的向量。这是代码:

    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();
    }

正如我所说,这只发生在我有 1 个敌人和 3 个射击时,否则它似乎工作正常。

编辑:现在游戏会在第一个敌人生成时冻结。这是更新的代码:

//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++;
        }
    }
}

【问题讨论】:

    标签: c++ vector sfml


    【解决方案1】:

    @moka 的回答解释了您的代码的核心问题,他/她使用迭代器的代码将解决您的问题。但是,还有另一种解决问题的方法:将代码拆分为“计算通道”和“数组修改通道”。代码如下所示:

    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());
    

    这种风格也很好,因为它将游戏逻辑与实现分开了一点。例如,如果你想让一次射击对多个敌人造成伤害,那么无论如何你都必须对射击数组使用这样的方法。

    Chapter on Double Buffering 在优秀的游戏编程模式一书中进行了更深入的介绍。

    【讨论】:

      【解决方案2】:

      你不能只是从向量中删除东西,同时以你正在做的方式迭代它们,因为它会产生 i 值,这些值将不再在向量的范围内(在它的大小通过调用 erase 减小之后),这基本上会导致尝试索引一个不再存在的数组值。 如果您需要这种行为,最好使用迭代器:

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

      你可以对 shot 数组做同样的事情。

      【讨论】:

      • 谢谢,但现在我遇到了不同的问题。查看我编辑的帖子
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-31
      • 2016-02-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多