我正在尝试用 C++ 制作一个物理引擎,并且我有一个刚体脚本和一个用于解决碰撞的函数。但是,当一个刚体与另一个刚体之间的距离稍微变大时,质量较小的刚体就会滑落。我当然不想要这个。请帮我。这是我解决冲突的函数:
void ResolveCollisions(Rigidbody& rigidbody, Rigidbody& boxRigidbody)
{
float distance = glm::distance(rigidbody.getPosition(), boxRigidbody.getPosition());
std::cout << distance;
// Calculate minimum penetration depth based on combined radius/bounding box half size
float minimumPenetrationDepth = rigidbody.getMass() + boxRigidbody.getMass();
if (distance - minimumPenetrationDepth <= 0.01f)
{
glm::vec3 collisionNormal = glm::normalize(rigidbody.getPosition() - boxRigidbody.getPosition());
// Calculate relative velocity
glm::vec3 relativeVelocity = rigidbody.getVelocity() - boxRigidbody.getVelocity();
float relativeVelocityNormal = glm::dot(relativeVelocity, collisionNormal);
float restitution = 0.1f; // Adjust the coefficient as needed
// Calculate impulse magnitude for normal direction
float j = -(1 + restitution) * relativeVelocityNormal;
j /= 1 / rigidbody.getMass() + 1 / boxRigidbody.getMass();
// Apply impulse for normal direction
glm::vec3 impulse = j * collisionNormal;
// Update velocities for normal direction
rigidbody.setVelocity(rigidbody.getVelocity() + impulse / rigidbody.getMass());
// Resolve penetration
(rigidbody.getMass() + boxRigidbody.getMass()); // Use combined mass for center of mass calculation
const float percent = 0.2f; // Penetration percentage to correct
const float slop = 0.1f; // Allowance to prevent jittering
float penetrationDepth = calculatePenetrationDepth(rigidbody, boxRigidbody);
glm::vec3 desiredDistance =
0.5f * (rigidbody.getBoundingBoxMax() - rigidbody.getBoundingBoxMin()) +
0.5f * (boxRigidbody.getBoundingBoxMax() - boxRigidbody.getBoundingBoxMin());; // Calculate desired non-penetration distance (e.g., sum of bounding box half sizes)
float desiredDistanceMagnitude = glm::length(desiredDistance);
float penetrationDepthBruh = desiredDistanceMagnitude - distance;
if (penetrationDepthBruh > slop) {
glm::vec3 correction = penetrationDepth * collisionNormal;
rigidbody.setPosition(rigidbody.getPosition() + correction);
}
// Calculate relative velocity in the direction of the tangent (friction)
glm::vec3 relativeVelocityTangent = relativeVelocity - (glm::dot(relativeVelocity, collisionNormal) * collisionNormal);
float relativeVelocityTangentMagnitude = glm::length(relativeVelocityTangent);
// Calculate friction coefficient
float staticFrictionThreshold = 0.001f;
float frictionCoefficient = 0.1f;
// Apply friction impulse if there's relative tangential velocity
if (relativeVelocityTangentMagnitude < staticFrictionThreshold) {
// If relative tangential velocity is low, apply static friction to prevent sliding
// Calculate static friction impulse
glm::vec3 staticFrictionImpulseA = -relativeVelocityTangent * rigidbody.getMass(); // Opposes motion
glm::vec3 staticFrictionImpulseB = -relativeVelocityTangent * boxRigidbody.getMass(); // Opposes motion
// Apply static friction impulse
rigidbody.setVelocity(rigidbody.getVelocity() + staticFrictionImpulseA / rigidbody.getMass());
}
else {
// If relative tangential velocity is high, apply dynamic friction
// Calculate friction coefficient
float frictionCoefficient = 0.1f; // Adjust as needed
// Apply friction impulse if there's relative tangential velocity
// Calculate impulse magnitude for friction
float frictionImpulseMagnitude = frictionCoefficient * j;
// Clamp friction impulse magnitude to prevent reversal of relative motion
frictionImpulseMagnitude = std::min(frictionImpulseMagnitude, relativeVelocityTangentMagnitude);
// Calculate friction impulse vector
glm::vec3 frictionImpulse = glm::normalize(relativeVelocityTangent) * frictionImpulseMagnitude;
// Apply friction impulse
rigidbody.setVelocity(rigidbody.getVelocity() - frictionImpulse / rigidbody.getMass());
}
// Calculate angular velocity change due to collision
glm::vec3 rA = rigidbody.getPosition() - boxRigidbody.getPosition();
glm::vec3 rB = boxRigidbody.getPosition() - rigidbody.getPosition();
glm::vec3 angularVelocityChangeA = glm::cross(rA, impulse) / rigidbody.getMass();
glm::vec3 angularVelocityChangeB = glm::cross(rB, -impulse) / boxRigidbody.getMass();
// Apply angular velocity change
rigidbody.setRotation(rigidbody.getRotation() + angularVelocityChangeA);
}
}
如果您想知道,这是我的刚体脚本:
class Rigidbody {
private:
glm::vec3 position;
glm::vec3 velocity;
glm::vec3 acceleration;
glm::vec3 force;
float mass;
glm::vec3 gravity;
glm::vec3 rotation;
// Bounding box dimensions
glm::vec3 boundingBoxMin;
glm::vec3 boundingBoxMax;
glm::vec3 colliderRotation;
float radius;
public:
Rigidbody(glm::vec3 initialPosition, float initialMass, glm::vec3 initialGravity,
Model& model, glm::vec3 initialRotation)
: position(initialPosition), mass(initialMass), velocity(glm::vec3(0.0f)),
acceleration(glm::vec3(0.0f)), force(glm::vec3(0.0f)), gravity(initialGravity),
rotation(initialRotation), colliderType(ColliderType::BoundingBox) {
// Get bounding box from the model
boundingBoxMax = model.GetMaxBoundingBox();
boundingBoxMin = model.GetMinBoundingBox();
}
// Constructor for spherical collider
Rigidbody(glm::vec3 initialPosition, float initialMass, glm::vec3 initialGravity,
float initialRadius)
: position(initialPosition), mass(initialMass), velocity(glm::vec3(0.0f)),
acceleration(glm::vec3(0.0f)), force(glm::vec3(0.0f)), gravity(initialGravity),
rotation(glm::vec3(0.0f)), radius(initialRadius), colliderType(ColliderType::Sphere) {}
void applyForce(glm::vec3 externalForce) {
force += externalForce;
}
void update(float deltaTime) {
// Apply gravity
force += mass * gravity;
// Apply Newton's second law: F = ma
acceleration = force / mass;
// Update velocity
velocity += acceleration * deltaTime;
// Update position
position += velocity * deltaTime;
// Reset force for next update
force = glm::vec3(0.0f);
}
//here would be all the getters and setters
enum class ColliderType {
BoundingBox,
Sphere
} colliderType;
// Methods to get the bounding box dimensions
glm::vec3 getBoundingBoxMin() const {
// Apply rotation to the min bounding box coordinates
glm::vec3 rotatedMin = boundingBoxMin;
rotatedMin = glm::rotateX(rotatedMin, colliderRotation.x);
rotatedMin = glm::rotateY(rotatedMin, colliderRotation.y);
rotatedMin = glm::rotateZ(rotatedMin, colliderRotation.z);
return rotatedMin;
}
glm::vec3 getBoundingBoxMax() const {
// Apply rotation to the max bounding box coordinates
glm::vec3 rotatedMax = boundingBoxMax;
rotatedMax = glm::rotateX(rotatedMax, colliderRotation.x);
rotatedMax = glm::rotateY(rotatedMax, colliderRotation.y);
rotatedMax = glm::rotateZ(rotatedMax, colliderRotation.z);
return rotatedMax;
}
// Bounding box collision detection
bool checkCollision(const Rigidbody& other) const {
// Get the bounding box of the other Rigidbody
glm::vec3 minB = other.getPosition() + other.getBoundingBoxMin();
glm::vec3 maxB = other.getPosition() + other.getBoundingBoxMax();
// Check if this bounding box intersects with the other bounding box
return (position.x + boundingBoxMax.x >= minB.x &&
position.x + boundingBoxMin.x <= maxB.x &&
position.y + boundingBoxMax.y >= minB.y &&
position.y + boundingBoxMin.y <= maxB.y &&
position.z + boundingBoxMax.z >= minB.z &&
position.z + boundingBoxMin.z <= maxB.z);
}
};
1。缺少一些方法:
我想你只是为了让事情变得更容易而把它们排除在外, 但你必须假设以下几点:
“
Rigidbody.getPosition()
”返回变量“glm::vec3 position
”。
“
Rigidbody.getMass()
”返回变量“float mass;
”。
“
Rigidbody.setVelocity(glm::vec3)
”设置变量“glm::vec3 velocity;
”。
“
calculatePenetrationDepth(rigidbody, boxRigidbody)
”有什么作用?
2。什么是“刚体.位置”?!
我认为这是刚体的中心点...... 但什么样的中心(质心或体积中心)我不知道......
3. getBoundingBoxMin() / -Max()
这里你要小心,这些方法不会返回真正的最小值/最大值。 “
getBoundingBoxMax()
”可能比“getBoundingBoxMin()
”小(在所有维度上)
因此,我建议它要么在方法中内部计算,要么
它被命名为“getBoundingBox1()
”和“getBoundingBox2()
”
4。 “checkCollision(const Rigidbody& other)”
此方法仅适用于 AABB,这里我们又遇到了第三个问题...
5。第一个 if 语句
“
if (distance - minimumPenetrationDepth <= 0.01f)
”
在这里,您仍然应该在对象的距离中包含边界框,因为低
质量大,体积大,距离大,不计算碰撞。
或者,如果质量非常高而体积很小,则计算不需要的碰撞
待计算。
6。未使用的计算
(rigidbody.getMass() + boxRigidbody.getMass());
不予评论...
7。摩擦
也许您只是忘记考虑底座的摩擦力,并且 也因为问题 5,计算出的力通常小于惯性 以及身体的摩擦力