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?