I created My own Spring Controller Function for my Animation Blueprint It’s results are smoother then built in spring controller and can be inverted if need be. I thought I’d share the code. It hasn’t been strenuously tested but so far the results are look much better then with the spring controller. The Spring controller is super jittery and this is not.
/*SpringTime - Calculate the Spring Bone Displacement over time. Use Transform Bone, Translation, Add to existing, world space
Fname SpringBone: The Name of the Bone.
FVector& BoneJiggle: Vector to store Values over time.
FVector& Velocity: Vector to store Values over time.
FVector& PreVelocity: Vector to store Values over time.
float Stiffness: The Bouncyness of Bone, 0 No Vibrations, 1 More Vibrartion, Keep between 0 and 1.
float Damping: The Damping of Bone, 0 Instant Decay, 1 No Decay, Keep between 0 and 1.
float Mass: The Mass of the bone.
FVector Scale: The Scale of the Simulation, Scale up or down final results relative to parent bone space. Invert Results by setting negative.
FVector MinStretch: Lower bounds for Stretch. If Bone Stretch exeeds bounds, Bone will be clampped.
FVector MaxStretch: Upper bounds for Stretch. If Bone Stretch exeeds bounds, Bone will be clampped.
float ErrorThreshold: If Velocity or acceletation exeeds this Value, reset. Catches Telports.
bool UseVelocity: Use Velocity instead of Acceleration for Simulation, Causes Bone to be dragged.
float DeltaTime: Time elapsed.
*/
FVector UPlayerAnimInstance::SpringTime(
FName SpringBone,
UPARAM(ref)FVector& BoneJiggle,
UPARAM(ref)FVector& Velocity,
UPARAM(ref)FVector& PrevVelocity,
float Stiffness, float Damping, float Mass,
FVector Scale,
FVector MinStretch,
FVector MaxStretch,
float ErrorThreshhold,
bool UseVelocity,
float DeltaTime)
{
/* Calculate Spring Bone Origin and Parent Trasform*/
USkeletalMeshComponent* SK_Owner = GetOwningComponent();
FName ParentBone = SK_Owner->GetParentBone(SpringBone);
FTransform ParentTransform = SK_Owner->GetSocketTransform(ParentBone);
FVector BoneOrigin = ParentTransform.Inverse().TransformVector(SK_Owner->GetSocketLocation(SpringBone));
//Calculate Accelectation
FVector acc = (((ParentTransform.TransformVector(BoneOrigin) - BoneJiggle) * Stiffness) / Mass) ;
FVector newLoc;
//Calculate Velocity at 60FPS
PrevVelocity = Velocity;
Velocity = (acc + Velocity) * FMath::Pow(Damping, (DeltaTime * 60.f));
BoneJiggle += (Velocity)* (DeltaTime * 60.f);
if (UseVelocity) {
//Use Velocity, Bone Drags Behind
if (Velocity.Size() > ErrorThreshhold) {
PrevVelocity = Velocity;
BoneJiggle = BoneOrigin = ParentTransform.TransformVector(BoneOrigin);
}
newLoc = ParentTransform.Rotator().UnrotateVector(-Velocity)*Scale;
newLoc.X = FMath::Clamp(newLoc.X, MinStretch.X, MaxStretch.X);
newLoc.Y = FMath::Clamp(newLoc.Y, MinStretch.Y, MaxStretch.Y);
newLoc.Z = FMath::Clamp(newLoc.Z, MinStretch.Z, MaxStretch.Z);
newLoc = ParentTransform.Rotator().RotateVector(newLoc);
}
else {
//Use Accelaration, Bone is Springy
acc = Velocity - PrevVelocity;
if (acc.Size() > ErrorThreshhold) {
PrevVelocity = Velocity;
BoneJiggle = BoneOrigin = ParentTransform.TransformVector(BoneOrigin);
}
newLoc = ParentTransform.Rotator().UnrotateVector(-acc)*Scale;
newLoc.X = FMath::Clamp(newLoc.X, MinStretch.X, MaxStretch.X);
newLoc.Y = FMath::Clamp(newLoc.Y, MinStretch.Y, MaxStretch.Y);
newLoc.Z = FMath::Clamp(newLoc.Z, MinStretch.Z, MaxStretch.Z);
newLoc = ParentTransform.Rotator().RotateVector(newLoc);
}
return newLoc;
}