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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 21:23:11