Game animation sample 5.5 orient rotation

I’m working on a combat system based on the Game Animation Sample in Unreal Engine 5.5. The goal is to implement a “lock-on” mode during combat where:

  • The character strafes and stays locked onto the enemy.
  • The camera remains free to move independently of the character’s rotation.

In old game anim sample (5.4), I achieved this by disabling update orientation function, the functions that set OrientRotationToMovement and UseControllerRotation on tick , allowing me to override their behavior. However, in UE 5.5, after disabling the newPre CMC Tick function in the Game Anim Sample (5.5), these booleans (OrientRotationToMovement and UseControllerRotation) appear to be overridden elsewhere.

Is there a new system or function in GAS 5.5 that forces these values to reset? How can I properly override or block this behavior so that my character can strafe and stay locked onto the enemy, while the camera remains free to move?

thanks

2 Likes

Did you eve find a solution to this, I’m trying to get the motion matching to match with the orientation of the capsule but it only seems to match the orientation of the active camera.

I’ve messed around with just about everything and I can’t get it to change its output.

I’m waiting for UE 5.7 to be released, hoping to get it working with GASP 5.7 :sad_but_relieved_face:

This happens because we added some orientation of direction towards where the camera on the IA_Move function that is aiming your camera rotation. This style of movement is pretty modern for 3rd person games meaning that users always move towards what they are looking at if moving forward or away. You’ll also see there is a IA_Move_World noted for locomotion testing, but it doesn’t do the match movement vector to camera direction adjustment. You can test it using the arrow keys on your keyboard to see how it feels.

If I understand correctly this has nothing to do with the inputs, but with being able to strafe facing a specific orientation.

I found a simple way to do that by overriding the PoseSearchGenerateTransformTrajectory in the animation blueprint by this piece of code:

void ADefaultCharacter::PoseSearchGenerateTransformTrajectoryWithYaw(const UObject* InContext, const FPoseSearchTrajectoryData& InTrajectoryData,
	float InDeltaTime, FTransformTrajectory& InOutTrajectory, bool LockedOrientation, float LockedOrientationYaw, float& InOutDesiredControllerYawLastUpdate, FTransformTrajectory& OutTrajectory,
	float InHistorySamplingInterval, int32 InTrajectoryHistoryCount, float InPredictionSamplingInterval, int32 InTrajectoryPredictionCount)
{
	FPoseSearchTrajectoryData::FSampling TrajectoryDataSampling;
	TrajectoryDataSampling.NumHistorySamples = InTrajectoryHistoryCount;
	TrajectoryDataSampling.SecondsPerHistorySample = InHistorySamplingInterval;
	TrajectoryDataSampling.NumPredictionSamples = InTrajectoryPredictionCount;
	TrajectoryDataSampling.SecondsPerPredictionSample = InPredictionSamplingInterval;

	FPoseSearchTrajectoryData::FState TrajectoryDataState;
	TrajectoryDataState.DesiredControllerYawLastUpdate = InOutDesiredControllerYawLastUpdate;

	FPoseSearchTrajectoryData::FDerived TrajectoryDataDerived;
	UpdateDataWithYaw(InTrajectoryData, InDeltaTime, InContext, LockedOrientation, LockedOrientationYaw, TrajectoryDataDerived, TrajectoryDataState);
	UPoseSearchTrajectoryLibrary::InitTrajectorySamples(InOutTrajectory, TrajectoryDataDerived.Position, TrajectoryDataDerived.Facing, TrajectoryDataSampling, InDeltaTime);
	UPoseSearchTrajectoryLibrary::UpdateHistory_TransformHistory(InOutTrajectory, TrajectoryDataDerived.Position, TrajectoryDataDerived.Velocity, TrajectoryDataSampling, InDeltaTime);
	UPoseSearchTrajectoryLibrary::UpdatePrediction_SimulateCharacterMovement(InOutTrajectory, InTrajectoryData, TrajectoryDataDerived, TrajectoryDataSampling, InDeltaTime);

	InOutDesiredControllerYawLastUpdate = TrajectoryDataState.DesiredControllerYawLastUpdate;
	
	OutTrajectory = InOutTrajectory;
}

bool ADefaultCharacter::UpdateDataWithYaw(	const FPoseSearchTrajectoryData& PoseSearchTrajectoryData,
											float DeltaTime,
											const UObject* Context,
											bool LockedOrientation,
											float LockedOrientationYaw,
											FPoseSearchTrajectoryData::FDerived& TrajectoryDataDerived,
											FPoseSearchTrajectoryData::FState& TrajectoryDataState)
{
	const ACharacter* Character = Cast<ACharacter>(Context);
	if (!Character)
	{
		if (const UAnimInstance* AnimInstance = Cast<UAnimInstance>(Context))
		{
			Character = Cast<ACharacter>(AnimInstance->GetOwningActor());
		}
		else if (const UActorComponent* AnimNextComponent = Cast<UActorComponent>(Context))
		{
			Character = Cast<ACharacter>(AnimNextComponent->GetOwner());
		}
		
		if (!Character)
		{
			return false;
		}
	}

	const UCharacterMovementComponent* MoveComp = Character->GetCharacterMovement();
	const USkeletalMeshComponent* MeshComp = Character->GetMesh();
	if (!MoveComp || !MeshComp)
	{
		return false;
	}

	TrajectoryDataDerived.MaxSpeed = FMath::Max(MoveComp->GetMaxSpeed() * MoveComp->GetAnalogInputModifier(), MoveComp->GetMinAnalogSpeed());
	TrajectoryDataDerived.BrakingDeceleration = FMath::Max(0.f, MoveComp->GetMaxBrakingDeceleration());
	TrajectoryDataDerived.BrakingSubStepTime = MoveComp->BrakingSubStepTime;
	TrajectoryDataDerived.bOrientRotationToMovement = MoveComp->bOrientRotationToMovement; // false when LockedOrientation is true

	TrajectoryDataDerived.Velocity = MoveComp->Velocity;
	TrajectoryDataDerived.Acceleration = MoveComp->GetCurrentAcceleration();
		
	TrajectoryDataDerived.bStepGroundPrediction = !MoveComp->IsFalling() && !MoveComp->IsFlying();

	if (TrajectoryDataDerived.Acceleration.IsZero())
	{
		TrajectoryDataDerived.Friction = MoveComp->bUseSeparateBrakingFriction ? MoveComp->BrakingFriction : MoveComp->GroundFriction;
		const float FrictionFactor = FMath::Max(0.f, MoveComp->BrakingFrictionFactor);
		TrajectoryDataDerived.Friction = FMath::Max(0.f, TrajectoryDataDerived.Friction * FrictionFactor);
	}
	else
	{
		TrajectoryDataDerived.Friction = MoveComp->GroundFriction;
	}

	const float DesiredYaw = LockedOrientation ? LockedOrientationYaw : Character->GetViewRotation().Yaw;
	
	const float DesiredYawDelta = DesiredYaw - TrajectoryDataState.DesiredControllerYawLastUpdate;
	TrajectoryDataState.DesiredControllerYawLastUpdate = DesiredYaw;
	
	if (DeltaTime > UE_SMALL_NUMBER)
	{
		// An AnimInstance might call this during an AnimBP recompile with 0 delta time, so we don't update ControllerYawRate
		TrajectoryDataDerived.ControllerYawRate = FRotator::NormalizeAxis(DesiredYawDelta) / DeltaTime;
		if (PoseSearchTrajectoryData.MaxControllerYawRate >= 0.f)
		{
			TrajectoryDataDerived.ControllerYawRate = FMath::Sign(TrajectoryDataDerived.ControllerYawRate) * FMath::Min(FMath::Abs(TrajectoryDataDerived.ControllerYawRate), PoseSearchTrajectoryData.MaxControllerYawRate);
		}
	}

	TrajectoryDataDerived.Position = MeshComp->GetComponentLocation();
	TrajectoryDataDerived.MeshCompRelativeRotation = MeshComp->GetRelativeRotation().Quaternion();

	if (TrajectoryDataDerived.bOrientRotationToMovement)
	{
		TrajectoryDataDerived.Facing = MeshComp->GetComponentRotation().Quaternion();
	}
	else
	{
		TrajectoryDataDerived.Facing = FQuat::MakeFromRotator(FRotator(0,TrajectoryDataState.DesiredControllerYawLastUpdate,0)) * TrajectoryDataDerived.MeshCompRelativeRotation;
	}
	return true;
}

With this approach you can replace the default PoseSearchGenerateTransformTrajectory by the new PoseSearchGenerateTransformTrajectoryWithYaw in the AnimBP, and pass in the extra LockedOrientation bool and LockedOrientationYaw float.

The issue I’m running into:

  • If the actor isn’t aligned with the locked orientation, motion matching produces bad results (character looks like it’s sliding).

  • If I try to align the actor with the locked orientation manually, I get a one-frame glitch where the mesh pops its orientation.

Why does the actor need to be aligned with the locked orientation for motion matching to work correctly? Is there a recommended way to lock orientation for trajectory search without causing sliding or pops?