Animation Curves Blended by Montage?

Lets say you have an idle/walk/run setup and play a montage which blends into another animation.

So it blends into and out of the animation in the montage but it also seems to blend float curve data that I have in the animation controlled by the montage. This is undesirable in my current setup. Is there a way to avoid blending the curve data while still blending the animation. Or is there a way to get access to the original unblended curve data?

Thanks.

I found a simple workaround. I added a metadata curve (which has a value of 1) to the animation sequence. This curve is similarly reduced during blend-in and blend-out. I then divide my curve by this metadata value to get the original unblended value. In my case I was using a curve to control drawing my bow, but the instant release of the blow string was blended and was not instant. Here’s my workaround code:

3 Likes

not sure if it’s safely handled, there’s a probability ofdividing by zero, might wanna multiply by 1.0f - bowactive, and to add on top of that, if you have a reference to your montage object that’s playing, you can use get the blend weight without the metadata curve workaround by using the montage instance that can return the weight:

		FAnimMontageInstance* montageInstance = characterAnimInstance->GetActiveInstanceForMontage(currentMontage);
		float blendWeight = characterAnimInstance->Montage_GetBlendTime(currentMontage);
		if (montageInstance)
		{
			blendWeight = montageInstance->GetWeight();
		}

It’s really an old post, but I went into this issue recently. Hope my solution can help others.

Using the workaround provided by @SteveProteau can be a bit inconvenient, and it does not support blending values between curves if multiple montages have curves with the same name. For instance, if you have two montages, A and B, active at the same time, and both of them have a curve X, you might want to get a blended result of curve X that respects the values provided by both A and B.

Here is my solution, you need to implement a custom anim instance like this:

UCLASS(Config = Game)
class UMyAnimInstance : public UAnimInstance
{
	GENERATED_BODY()

public:
	UMyAnimInstance(const FObjectInitializer& ObjectInitializer);

	// keep montage curve value when blending in/out
	UFUNCTION(BlueprintCallable, BlueprintPure = false)
	float GetFadelessCurveValue(FName CurveName) const;
	bool GetFadelessCurveValue(FName CurveName, float& OutValue) const;
};
float UMyAnimInstance::GetFadelessCurveValue(FName const CurveName) const
{
	float Value = 0.f;
	GetFadelessCurveValue(CurveName, Value);
	return Value;
}

bool UMyAnimInstance::GetFadelessCurveValue(FName const CurveName, float& OutValue) const
{
	float TotalWeight{};
	TArray<TTuple<float, float>, TInlineAllocator<5>> CurvesWithWeight{};

	for (FAnimMontageInstance const* Instance : MontageInstances)
	{
		if (!Instance->Montage || !Instance->Montage->HasCurveData(CurveName))
		{
			continue;
		}

		float const Weight = Instance->GetWeight();
		float const Position = Instance->GetPosition();
		float const CurveValue = Instance->Montage->EvaluateCurveData(CurveName, Position);
		CurvesWithWeight.Emplace(CurveValue, Weight);
		TotalWeight += Weight;
	}

	if (TotalWeight <= 0 || FMath::IsNearlyZero(TotalWeight))
	{
		return false;
	}

	OutValue = 0.0f;
	for (auto const& [CurveValue, Weight] : CurvesWithWeight)
	{
		OutValue += CurveValue * Weight / TotalWeight;
	}

	return true;
}

Then you can get correct curve values by GetFadelessCurveValue function.

1 Like

I think this should be the easiest way

bool UUsefulBlueprintFunctionLibrary::getAnimationCurveRawValue(const UAnimSequenceBase* anim, const FName& curveName, float position, float& value, float defaultValue)
{
	value = defaultValue;
	if (!anim)
		return false;

	FRawCurveTracks tracks = anim->GetCurveData();
	for (const FFloatCurve& curve : tracks.FloatCurves)
	{
		if (curve.GetName() == curveName)
		{
			value = curve.Evaluate(position);
			return true;
		}
	}

	return false;
}

anim can be any subclass of UAnimSequenceBase, so montage and AnimSequence will work correctly.
It is worth noting that this function only queries curve data from the anim itself. If the curve data is in an AnimSequence in a Montage, and the anim is a Montage, it will not work correctly. Therefore, either transfer the curve from the AnimSequence to the Montage, or pass in the AnimSequence as the anim (I don’t recommend this because it is difficult to know the current playback progress of the AnimSequence)