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?