C ++中正确的球体碰撞分辨率

问题描述

我正在实现一个球到球的碰撞解决方案,我对从哪里开始感到有些困惑。第一个问题,游戏/引擎是否有标准方法解决球到球的碰撞?只有几种标准方法可以做到吗?还是根据需要的分辨率差异很大?

我想在我的引擎中实现这一点,我写了一个基本的推球体和另一个球体(因此基本上一个交互的球体可以推另一个球体),但这只是一个超级简单的概念。我到底该如何改善它以使其更加准确? (请注意,由于我仍在测试中,因此代码未经过优化)

由于它是一个比较小众的话题,因此似乎总体上缺乏关于冲突解决的可靠文档。我发现的大多数资源仅涉及检测部分。

bool issphereInsideSphere(glm::vec3 sphere,float sphereRadius,glm::vec3 otherSphere,float otherSphereRadius,Entity* e1,Entity* e2)
{
    float dist = glm::sqrt((sphere.x - otherSphere.x) * (sphere.x - otherSphere.x) + (sphere.y - otherSphere.y) * (sphere.y - otherSphere.y) + (sphere.z - otherSphere.z) * (sphere.z - otherSphere.z));
    if (dist <= (sphereRadius + otherSphereRadius))
    {
        //Push code
        e1->move(-e1->xVeLocity / 2,-e1->zVeLocity / 2);
        e2->move(e1->xVeLocity / 2,e1->zVeLocity / 2);
    }
    return dist <= (sphereRadius + otherSphereRadius);
}

解决方法

不需要使用std::sqrt,并且将平方长度与(sphereRadius + otherSphereRadius) 2 进行比较可能要快得多。

示例:

#include <glm/glm.hpp>
#include <iostream>
#include <cstdlib>

auto squared_length(const glm::vec3& v) {
    return std::abs(v.x * v.x + v.y * v.y + v.z * v.z);
}

class Sphere {
public:
    Sphere(const glm::vec3& Position,float Radius) :
        position{Position},radius(Radius) {}

    bool isSphereInsideSphere(const Sphere& other) const {
        auto dist = squared_length(position - other.position);

        // compare the squared values
        if(dist <= (radius + other.radius) * (radius + other.radius)) {
            // Push code ...
            return true;
        }
        return false;
    }

private:
    glm::vec3 position;
    float radius;
};

int main() {
    Sphere a({2,3,0},2.5);
    Sphere b({5,7,2.5);
    std::cout << std::boolalpha << a.isSphereInsideSphere(b) << '\n'; // prints true
}
,

这是一个更简单的示例(不涉及新类)。

bool isSphereInsideSphere(glm::vec3 sphere,float sphereRadius,glm::vec3 otherSphere,float otherSphereRadius,Entity* e1,Entity* e2)
{
    auto delta = otherSphere - sphere;
    auto r2 = (sphereRadius + otherSphereRadius)*(sphereRadius + otherSphereRadius);
    if (glm::dot(delta,delta) <= r2)
    {
        //Push code
        
        return true;
    }
    return false;
}