Damping is essential for stable hovering/spring physics. It’s impossible to get stability without it.
Here’s some basic code for a stable, hovering object.
// Trace below for hovering
FHitResult HoverHit = FHitResult();
const FVector Location = UpdatedComponent->GetComponentLocation();
FVector HoverAccel = FVector::ZeroVector;
FCollisionQueryParams Params = FCollisionQueryParams();
Params.bTraceComplex = true;
if (GetWorld()->LineTraceSingleByChannel(HoverHit, Location, Location + FVector(0.f, 0.f, -HoverSpring_Length), ECC_Visibility, Params))
const float CompressionRatio = FMath::GetMappedRangeValueClamped(FVector2D(0.f, HoverSpring_Length), FVector2D(1.f, 0.f), Location.Z - HoverHit.Location.Z);
const float HoverAccelZ = (CompressionRatio * HoverSpring_Tension) + (-HoverSpring_Damping * Velocity.Z);
HoverAccel = FVector(0.f, 0.f, HoverAccelZ);
Accel = HoverAccel; // Add your other accelerations here
Velocity += Accel * DeltaTime;
You can apply that to a physics-simulating object with gravity and shouldn’t have any trouble, alternatively you can just add FVector(0.f, 0.f -980.f) to Accel before adding it to Velocity, if you want custom gravity. You will also need to tweak the values. I recommend starting with something like this:
SpringTension = 1500.f;
SpringDamping = 1.5f;
SpringLength = 350.f;
I do my hovertanks with one downward-facing trace, and another trace that follows the velocity of the craft, and average the results. I use a rotational spring to align to the surface normal rather than using multiple traces. It’s more stable, faster, and you can control the strength of surface alignment.