You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SFML C++小行星游戏问题:新小行星无法在被击毁位置生成

问题描述

我用SFML C++制作《小行星》游戏,子弹与小行星的碰撞检测功能正常,但尝试在刚被击中的小行星位置生成新小行星时,新小行星始终出现在初始小行星的预定生成位置。另外我不太清楚如何制作最小可复现示例(MRE),因为复现问题需要用到全部代码。


相关代码

Game.cpp

// Create asteroids
Asteroids asteroid(asteroid_width, asteroid_height);
if (level_2 == false)
{
    asteroid.spawnAsteroids(screen_width, screen_height, asteroid_width, asteroid_height);
}
else
{
    max_asteroids++;
    asteroid.spawn_new(level_2);
    level_2 = false;
}
asteroid.chooseAsteroidDirection();
asteroid.chooseTexture();

// Push asteroids to asteroid vector
if (asteroid_vector.size() < max_asteroids)
{
    asteroid_vector.push_back(asteroid);
}

// Draw and move asteroids
for (long unsigned int i = 0; i < asteroid_vector.size(); i++)
{
    asteroid_vector[i].drawTo(window);
    asteroid_vector[i].moveAsteroids(dt);
    asteroid_vector[i].screenWrapping(screen_width, screen_height);
    asteroid_vector[i].collision(asteroid_dead, bullet_dead, asteroid_scale, bullet_vector, level_2);

    // If bullet_dead is true despawn the bullet
    if (bullet_dead)
    {
        bullet_vector.erase(bullet_vector.begin() + i);
        bullet_dead = false;
    }

Asteroids.cpp

void Asteroids::spawnAsteroids(int screen_width, int screen_height, int asteroid_height, int asteroid_width)
{
    random_corner = rand() % 4;

    if (random_corner == 0)
    {
        spawn_point.x = asteroid_width;
        spawn_point.y = asteroid_height;
    }
    else if (random_corner == 1)
    {
        spawn_point.x = screen_width - asteroid_width;
        spawn_point.y = asteroid_height;
    }
    else if (random_corner == 2)
    {
        spawn_point.x = screen_width - asteroid_width;
        spawn_point.y = screen_height - asteroid_height;
    }
    else if (random_corner == 3)
    {
        spawn_point.x = asteroid_width;
        spawn_point.y = screen_height - asteroid_width;
    }

    asteroid.setTexture(asteroid_texture_1);
    asteroid.setPosition(spawn_point);
}

void Asteroids::collision(bool& asteroid_dead, bool& bullet_dead, float& asteroid_scale, std::vector<Bullet> bullet_vector, bool& level_2)
{
    for (unsigned int i = 0; i < bullet_vector.size(); i++)
    {
        auto bullet = bullet_vector[i].bullet;

        // If current bullet collides with asteroid,
        // set asteroid_dead and bullet_dead to true
        if (bullet.getGlobalBounds().intersects(asteroid.getGlobalBounds()))
        {
            asteroid_dead = true;
            bullet_dead = true;
            asteroid.scale(asteroid_scale, asteroid_scale);
            level_2 = true;
        }
    }
}

void Asteroids::spawn_new(bool& level_2)
{
    if (level_2)
    {
        asteroid.setPosition(asteroid.getPosition().x, asteroid.getPosition().y);
    }
}

解决方案

问题根源

  1. 你生成新小行星时,创建的是全新的Asteroids对象,调用的spawn_new函数只是给这个新对象设置自身初始位置(不是被击中 asteroid 的位置),完全没用到被击中目标的坐标。
  2. 碰撞发生时仅标记了level_2为true,但没有记录被击中 asteroid 的位置,后续生成新对象时无法获取这个关键坐标。

具体修改步骤

1. 在Game类中添加位置存储变量

在Game类的私有成员中添加:

sf::Vector2f hit_asteroid_pos;

2. 修改碰撞检测逻辑,记录被击中位置

修改Game.cpp中的遍历循环:

for (long unsigned int i = 0; i < asteroid_vector.size(); i++)
{
    asteroid_vector[i].drawTo(window);
    asteroid_vector[i].moveAsteroids(dt);
    asteroid_vector[i].screenWrapping(screen_width, screen_height);
    
    bool is_collided = false;
    // 用is_collided替代原level_2参数,明确标记是否发生碰撞
    asteroid_vector[i].collision(asteroid_dead, bullet_dead, asteroid_scale, bullet_vector, is_collided);

    if (is_collided)
    {
        // 记录被击中小行星的位置
        hit_asteroid_pos = asteroid_vector[i].getPosition();
        // 删除被击中的小行星
        asteroid_vector.erase(asteroid_vector.begin() + i);
        max_asteroids++; // 新增小行星数量
        i--; // 删除元素后修正循环索引
        continue;
    }

    if (bullet_dead)
    {
        bullet_vector.erase(bullet_vector.begin() + i);
        bullet_dead = false;
    }
}

3. 修改新小行星生成逻辑

替换原有的生成代码,直接使用记录的位置:

// 新增生成小行星的逻辑(放在循环外合适位置)
if (asteroid_vector.size() < max_asteroids)
{
    Asteroids new_asteroid(asteroid_width, asteroid_height);
    // 直接设置为被击中的位置
    new_asteroid.setPosition(hit_asteroid_pos);
    new_asteroid.chooseAsteroidDirection();
    new_asteroid.chooseTexture();
    asteroid_vector.push_back(new_asteroid);
}

4. 调整Asteroids类的方法

  • 给Asteroids类添加获取位置的方法:
sf::Vector2f Asteroids::getPosition() const
{
    return asteroid.getPosition();
}
  • 修改collision函数,用is_collided参数替代level_2:
void Asteroids::collision(bool& asteroid_dead, bool& bullet_dead, float& asteroid_scale, std::vector<Bullet> bullet_vector, bool& is_collided)
{
    is_collided = false;
    for (unsigned int i = 0; i < bullet_vector.size(); i++)
    {
        auto bullet = bullet_vector[i].bullet;
        if (bullet.getGlobalBounds().intersects(asteroid.getGlobalBounds()))
        {
            asteroid_dead = true;
            bullet_dead = true;
            is_collided = true;
            break; // 避免同一 asteroid 被多个子弹重复触发
        }
    }
}

核心优化点

  • 碰撞发生时立即记录目标位置,避免后续无法追溯
  • 生成新小行星时直接复用记录的坐标,不再调用随机生成位置的逻辑
  • 删除无效的spawn_new函数,简化逻辑

内容的提问来源于stack exchange,提问作者Aiden Rand

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 09:47:02