I am having trouble with a frame independent buoyancy force using substepping in Unreal Engine 4.
The original problem (which I am attempting to solve using substepping) is that when you apply a buoyancy force (which is proportional to the depth of the object), the force needs to change as the object rises out of the water. Consider the following, where an object is under water and we either have 2FPS or 42FPS (the curve representing the true force of the object as is rises)
Clearly the lower the framerate, the larger force is applied for too long resulting in the object accelerating too much.
Substepping alone does not fix this as the force being applied isn't recalculated every substep.
If I hook the force calculation into the substep then compare running at 60FPS and 10FPS I can see that at 10FPS I get 6 substeps every frame so I believe it should be the same.
However, I still see a discrepancy. That is, at 10 FPS the object leaves the water at almost double the velocity.
Some data from the 60 vs. 10 FPS runs
| 60FPS | 10FPS ___________________________________________________ Velocity Leaving Water | 815 cm/s | 1576cm/s Time in water | 0.89s | 0.43s Total displacement | 122 | 41.84 Max Displacement | 209cm | 150cm
Here the time in water is measured by summing the dt's of each substep and the total displacement is the displacement at each substep multiplied by the dt.
I believe the problem is that the elapsed physics time is nearly double for the higher frame rate, though why this would mean the lower frame rate one comes out faster, I don't understand.
The code to apply the force is:
void UBuoyantPointComponent::CalculateBuoyancyPhysics(float DeltaTime, FBodyInstance* BI)
{
// Data passed in from the main thread, now just reduced to the components offset which doesn't change
PhysicsLock.lock();
FPhysSubstepData LocalSubstepData = FPhysSubstepData(SubstepData);
PhysicsLock.unlock();
FTransform BodyTransform = BI->GetUnrealWorldTransform();
const FVector& CalcCompLocation = (LocalSubstepData.ComponentOffset * BodyTransform).GetLocation();
const float Altitude = CalcCompLocation.Z;
const float Difference = 0.0f - Altitude;
if (Difference > 0) // we are under the sea since the wave height is greater than the height of this point
{
float SubmergedVolume = FMath::Min(Difference, 100.0f) * 100.0f * 100.0f;
FVector ComputedForce = ((FVector::UpVector * SubmergedVolume * BuoyancyStrength));
BI->AddForceAtPosition(ComputedForce, CalcCompLocation);
}
}
I can't even work out what I can try to gain more insight into this so any help would be greatly appreciated.
Update
Printing out data every substep reveals some interesting things that I didn't spot before. (Full log, the ---
indicates a game tick, each line is a substep tick). In summary though:
The first set of substeps don't actually move the object at all
This means after the first frame the 10FPS object is moving considerably faster than the 60FPS one since it has had a longer period of not moving and just accumulating force.
dv calculation seems to be out by a half
My dv
which I think should be how much the velocity changes from to frame seems to be out by a factor of a half. It is being computed in the following way:
`DeltaTime * ((SubmergedVolume * BuoyancyStrength) / BI->GetBodyMass())`
Where (SubmergedVolume * BuoyancyStrength)
is the only force applied to the object.
This discrepancy is magnified by the lower framerate and the objects speed increases twice a fast at lower depths (i.e. larger force) for longer.