diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_ConvAgent/Source/PS_AI_ConvAgent/Private/PS_AI_ConvAgent_PostureComponent.cpp b/Unreal/PS_AI_Agent/Plugins/PS_AI_ConvAgent/Source/PS_AI_ConvAgent/Private/PS_AI_ConvAgent_PostureComponent.cpp index 61518b1..bacdf46 100644 --- a/Unreal/PS_AI_Agent/Plugins/PS_AI_ConvAgent/Source/PS_AI_ConvAgent/Private/PS_AI_ConvAgent_PostureComponent.cpp +++ b/Unreal/PS_AI_Agent/Plugins/PS_AI_ConvAgent/Source/PS_AI_ConvAgent/Private/PS_AI_ConvAgent_PostureComponent.cpp @@ -99,6 +99,7 @@ void UPS_AI_ConvAgent_PostureComponent::BeginPlay() // Apply the mesh forward offset so "neutral" aligns with where the face points. OriginalActorYaw = Owner->GetActorRotation().Yaw + MeshForwardYawOffset; TargetBodyWorldYaw = Owner->GetActorRotation().Yaw; + SmoothedBodyYaw = Owner->GetActorRotation().Yaw; if (bDebug) { @@ -179,6 +180,7 @@ void UPS_AI_ConvAgent_PostureComponent::ResetBodyTarget() if (AActor* Owner = GetOwner()) { TargetBodyWorldYaw = Owner->GetActorRotation().Yaw; + SmoothedBodyYaw = Owner->GetActorRotation().Yaw; } } @@ -328,9 +330,10 @@ void UPS_AI_ConvAgent_PostureComponent::TickComponent( TargetWorldYaw = HorizontalDir.Rotation().Yaw; } - // Body smoothly interpolates toward its persistent target - // (only when body tracking is enabled — otherwise only head+eyes move). - if (bEnableBodyTracking) + // Body smoothly interpolates toward its persistent target. + // Server/standalone: directly rotates the actor. + // Client: does NOT rotate — accepts replicated rotation from server. + if (bEnableBodyTracking && Owner->HasAuthority()) { const float BodyDelta = FMath::FindDeltaAngleDegrees( Owner->GetActorRotation().Yaw, TargetBodyWorldYaw); @@ -341,12 +344,26 @@ void UPS_AI_ConvAgent_PostureComponent::TickComponent( } } + // ── Smoothed body yaw for cascade computations ────────────────── + // Server: direct copy (no lag). Client: interpolate toward replicated + // rotation to avoid step artifacts from discrete ~30Hz network updates. + if (Owner->HasAuthority()) + { + SmoothedBodyYaw = Owner->GetActorRotation().Yaw; + } + else + { + const float ReplicatedYaw = Owner->GetActorRotation().Yaw; + const float ClientDelta = FMath::FindDeltaAngleDegrees(SmoothedBodyYaw, ReplicatedYaw); + SmoothedBodyYaw += FMath::FInterpTo(0.0f, ClientDelta, SafeDeltaTime, BodyInterpSpeed * 3.0f); + } + // ── 3. Compute DeltaYaw after body interp ────────────────────────── float DeltaYaw = 0.0f; if (!HorizontalDir.IsNearlyZero(1.0f)) { - const float CurrentFacingYaw = Owner->GetActorRotation().Yaw + MeshForwardYawOffset; + const float CurrentFacingYaw = SmoothedBodyYaw + MeshForwardYawOffset; DeltaYaw = FMath::FindDeltaAngleDegrees(CurrentFacingYaw, TargetWorldYaw); } @@ -432,6 +449,7 @@ void UPS_AI_ConvAgent_PostureComponent::TickComponent( // Body: keep current orientation — don't rotate back to original facing. // The body stays wherever the last tracking left it; only head and eyes reset. + SmoothedBodyYaw = Owner->GetActorRotation().Yaw; // Head + Eyes: return to center TargetHeadYaw = 0.0f; @@ -531,20 +549,21 @@ void UPS_AI_ConvAgent_PostureComponent::TickComponent( DebugFrameCounter++; if (DebugFrameCounter % 120 == 0) { - const float FacingYaw = Owner->GetActorRotation().Yaw + MeshForwardYawOffset; + const float FacingYaw = SmoothedBodyYaw + MeshForwardYawOffset; const FVector TP = TargetActor->GetActorLocation() + TargetOffset; const FVector Dir = TP - Owner->GetActorLocation(); const float TgtYaw = FVector(Dir.X, Dir.Y, 0.0f).Rotation().Yaw; const float Delta = FMath::FindDeltaAngleDegrees(FacingYaw, TgtYaw); UE_LOG(LogPS_AI_ConvAgent_Posture, Log, - TEXT("Posture [%s -> %s]: Delta=%.1f | Head=%.1f/%.1f | Eyes=%.1f/%.1f | Body: enabled=%s TargetYaw=%.1f ActorYaw=%.1f"), + TEXT("Posture [%s -> %s]: Delta=%.1f | Head=%.1f/%.1f | Eyes=%.1f/%.1f | Body: enabled=%s TargetYaw=%.1f SmoothedYaw=%.1f (raw=%.1f)"), *Owner->GetName(), *TargetActor->GetName(), Delta, CurrentHeadYaw, CurrentHeadPitch, CurrentEyeYaw, CurrentEyePitch, bEnableBodyTracking ? TEXT("Y") : TEXT("N"), TargetBodyWorldYaw, + SmoothedBodyYaw, Owner->GetActorRotation().Yaw); } } diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_ConvAgent/Source/PS_AI_ConvAgent/Public/PS_AI_ConvAgent_PostureComponent.h b/Unreal/PS_AI_Agent/Plugins/PS_AI_ConvAgent/Source/PS_AI_ConvAgent/Public/PS_AI_ConvAgent_PostureComponent.h index 4c9a117..49be5a5 100644 --- a/Unreal/PS_AI_Agent/Plugins/PS_AI_ConvAgent/Source/PS_AI_ConvAgent/Public/PS_AI_ConvAgent_PostureComponent.h +++ b/Unreal/PS_AI_Agent/Plugins/PS_AI_ConvAgent/Source/PS_AI_ConvAgent/Public/PS_AI_ConvAgent_PostureComponent.h @@ -321,6 +321,12 @@ private: * Same sticky pattern as TargetHeadYaw but for the body layer. */ float TargetBodyWorldYaw = 0.0f; + /** Smoothed body yaw for cascade computations. + * On server/standalone: mirrors Owner->GetActorRotation().Yaw directly. + * On client: interpolates toward the replicated rotation to avoid + * step artifacts from discrete network updates (~30Hz replication). */ + float SmoothedBodyYaw = 0.0f; + /** Original actor yaw at BeginPlay (for neutral return when TargetActor is null). */ float OriginalActorYaw = 0.0f;