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); } }
解决方案
问题根源
- 你生成新小行星时,创建的是全新的
Asteroids对象,调用的spawn_new函数只是给这个新对象设置自身初始位置(不是被击中 asteroid 的位置),完全没用到被击中目标的坐标。 - 碰撞发生时仅标记了
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
相关产品推荐
相关产品推荐

