SFML中两圆碰撞检测异常问题求助
SFML圆形碰撞异常问题修复
核心错误分析
- CircleShape原点默认是左上角而非圆心:当前用
getPosition()获取的是圆的左上角坐标,不是圆心,导致距离计算完全错误,这是碰撞异常的根本原因。 - Check_Collision函数缺少默认返回值:仅在碰撞时返回true,未碰撞时无返回值,触发未定义行为,导致逻辑混乱。
- 随机位置未考虑圆的半径:boost圆半径为10,直接生成0-1000的坐标会导致部分圆超出窗口,进一步干扰碰撞判断。
修复方案
1. 修正圆心坐标计算
所有涉及圆位置的计算,需将左上角坐标转换为圆心坐标:圆心坐标 = getPosition() + sf::Vector2f(radius, radius),或直接设置圆的原点为圆心。
2. 完善碰撞检测函数
添加默认返回分支,确保函数始终有返回值。
3. 调整随机位置范围
将boost的随机位置限制在[radius, 窗口大小 - radius],避免圆超出窗口。
4. 优化CircleShape原点设置(推荐)
将圆的原点设为圆心,移动和缩放时位置更直观。
修复后的完整代码
#include <SFML/Graphics.hpp> #include <SFML/Audio.hpp> #include <SFML/System.hpp> #include <Windows.h> #include <iostream> #include <math.h> #include <cmath> #include <thread> #include <random> double DISTANCE(sf::Vector2f l, sf::Vector2f k); bool Check_Collision(sf::CircleShape x, sf::CircleShape y); double angle_opp(sf::Vector2f l, sf::Vector2f k); double angle(sf::Vector2f l, sf::Vector2f k); double angle_adj(sf::Vector2f l, sf::Vector2f k); int main() { int rad = 20; sf::Clock clock; sf::RenderWindow window(sf::VideoMode(1000, 1000), "Sheka Snake"); sf::CircleShape circle1; sf::CircleShape boost; circle1.setPosition(400, 200); circle1.setRadius(rad); circle1.setOrigin(rad, rad); // 设置原点为圆心 boost.setPosition(300, 500); boost.setRadius(10); boost.setFillColor(sf::Color::Red); boost.setOrigin(10, 10); // 设置boost原点为圆心 window.setFramerateLimit(60); window.setVerticalSyncEnabled(false); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } float deltaTime = clock.restart().asSeconds(); float speed = 200.f * deltaTime; // 用秒数计算速度,帧率变化时速度稳定 if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { circle1.move(-speed, 0); } if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { circle1.move(0, -speed); } if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { circle1.move(0, speed); } if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { circle1.move(speed, 0); } if (Check_Collision(circle1, boost)) { std::random_device dev; std::mt19937 rng(dev()); // 限制随机位置,避免boost超出窗口 std::uniform_int_distribution<std::mt19937::result_type> distX(boost.getRadius(), window.getSize().x - boost.getRadius()); std::uniform_int_distribution<std::mt19937::result_type> distY(boost.getRadius(), window.getSize().y - boost.getRadius()); boost.setPosition(distX(rng), distY(rng)); rad += 30; circle1.setRadius(rad); circle1.setOrigin(rad, rad); // 更新原点为新圆心 } window.clear(); window.draw(circle1); window.draw(boost); window.display(); } } double DISTANCE(sf::Vector2f l, sf::Vector2f k) { double dy = l.y - k.y; double dx = l.x - k.x; return sqrt(dy * dy + dx * dx); // 用乘法替代pow,提升效率 } double angle_opp(sf::Vector2f l, sf::Vector2f k) { return k.y - l.y; } double angle_adj(sf::Vector2f l, sf::Vector2f k) { return k.x - l.x; } double angle(sf::Vector2f l, sf::Vector2f k) { return atan2(k.y - l.y, k.x - l.x); // 用atan2避免除以0的问题 } bool Check_Collision(sf::CircleShape x, sf::CircleShape y) { sf::Vector2f centerX = x.getPosition(); sf::Vector2f centerY = y.getPosition(); double dis = DISTANCE(centerX, centerY); double sumRadius = x.getRadius() + y.getRadius(); return dis <= sumRadius; }
额外优化说明
- 用
deltaTime(秒数)计算移动速度,确保不同帧率下移动速度一致。 - 替换
pow为乘法,提升距离计算效率。 - 用
atan2代替atan,避免x坐标相同时的除以0错误。
内容的提问来源于stack exchange,提问作者wildmushle
相关产品推荐
相关产品推荐

