Hi,
I’m trying to build the physics of a soccer ball from scratch, but I got into some issues while making it stop bouncing and resting on the ground. I’ve spent days on this, but I just don’t get it what I should do. It doesn’t work for different linear drag values or gravity, high and low. While I play with those values, some time the ball rests, sometimes it doesn’t. I’m calling my physics at a constant timestep of 0.016.
Next is my entire ball physics up until now. Any idea how to properly handle ball impact bounce and resting on the ground?
void ABall::CalculatePhysics(FPhysicsData& Data, float DeltaTime, bool IsFutureStep) const
{
Data.PreviousLocation = Data.CurrentLocation;
Data.PreviousRotation = Data.CurrentRotation;
if (MagnusEffect > 0.f)
{
Data.Forces.Add(MagnusEffect * FVector::CrossProduct(Data.AngularVelocity, Data.LinearVelocity));
}
// Linear motion
FVector NetForce = FVector::ZeroVector;
for (const FVector& Force : Data.Forces)
{
NetForce += Force;
}
NetForce += -LinearDrag * 0.5f * 1.225e-6f * (Data.LinearVelocity * Data.LinearVelocity) * (3.14f * Radius * Radius);
FVector Acceleration = (NetForce / Mass);
if (!Data.IsOnGround)
{
Acceleration += FVector(0.f, 0.f, Gravity);
}
Data.LinearVelocity += Acceleration * DeltaTime;
Data.CurrentLocation += Data.LinearVelocity * DeltaTime;
// Angular motion
FVector NetAngularForce = FVector::ZeroVector;
for (const FVector& Force : Data.AngularForces)
{
NetAngularForce += Force;
}
FVector AngularAcceleration = NetAngularForce / InertiaTensor;
Data.AngularVelocity = Data.AngularVelocity * FMath::Pow(AngularDamping, DeltaTime) + AngularAcceleration * DeltaTime;
if (Data.AngularVelocity.Size() > MaxAngularVelocity)
{
Data.AngularVelocity = Data.AngularVelocity.GetSafeNormal() * MaxAngularVelocity;
}
Data.CurrentRotation *= (FRotator(Data.AngularVelocity.Y, Data.AngularVelocity.Z, Data.AngularVelocity.X) * DeltaTime).Quaternion();
// Collision
FHitResult HitResult;
FCollisionResponseParams Params;
bool bHitFound = GetWorld()->SweepSingleByChannel(
HitResult,
Data.PreviousLocation + (Data.LinearVelocity.GetSafeNormal() * 0.0001f),
Data.CurrentLocation,
FQuat::Identity,
ECollisionChannel::ECC_WorldDynamic,
FCollisionShape::MakeSphere(Radius),
FCollisionQueryParams("BallCollisionTrace", false, this),
FCollisionResponseParams()
);
if (!bHitFound || !HitResult.bBlockingHit)
{
Data.IsColliding = false;
}
else if (!Data.IsOnGround)
{
Data.CurrentLocation = HitResult.ImpactNormal * Radius + HitResult.ImpactPoint;
if (!IsFutureStep)
{
Data.LinearVelocity = Restitution * Data.LinearVelocity.MirrorByVector(HitResult.ImpactNormal);
FPhysicsData FutureData = Data;
CalculatePhysics(FutureData, PhysicsTimestep, true);
if (FutureData.LinearVelocity.Size() < 10.f)
{
Data.LinearVelocity.Z = 0.f;
Data.IsOnGround = true;
}
}
Data.IsColliding = true;
}
}