Fix character rotation in editor + defer timeline play until AI ready
- Save pawn rotation/movement settings (bUseControllerRotation*, bOrient- RotationToMovement, MovementMode) on SpawnableComponent when editor disables AI, then restore them exactly in StartSimulation - Re-apply rotation/movement overrides on StopSimulation so the character doesn't auto-rotate after returning from Simulate - Poll spawned pawns' BrainComponent on scene load (gameplay via SceneLoader); start timeline playback only once every AI BT is running. Fixes timeline keys firing before the AI-driven sequence (walk) starts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,9 @@
|
||||
#include "PS_Editor_SplineEditable.h"
|
||||
#include "PS_Editor_PointPlaceable.h"
|
||||
#include "Engine/LevelStreamingDynamic.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
#include "PS_Editor_SpawnableComponent.h"
|
||||
#include "Engine/PostProcessVolume.h"
|
||||
#include "PS_Editor_CameraStart.h"
|
||||
#include "PS_Editor_ChildMovable.h"
|
||||
@@ -256,7 +259,7 @@ void APS_Editor_PlayerController::StartSimulation()
|
||||
SelectionManager->ClearSelection();
|
||||
}
|
||||
|
||||
// Enable AI logic on all spawned pawns
|
||||
// Enable AI logic on all spawned pawns and restore movement settings from class defaults
|
||||
if (SpawnManager)
|
||||
{
|
||||
for (const TWeakObjectPtr<AActor>& Weak : SpawnManager->GetSpawnedActors())
|
||||
@@ -264,6 +267,25 @@ void APS_Editor_PlayerController::StartSimulation()
|
||||
if (APawn* P = Cast<APawn>(Weak.Get()))
|
||||
{
|
||||
P->SetActorTickEnabled(true);
|
||||
|
||||
// Restore rotation/movement settings saved when AI was disabled
|
||||
UPS_Editor_SpawnableComponent* SaveComp = P->FindComponentByClass<UPS_Editor_SpawnableComponent>();
|
||||
if (SaveComp && SaveComp->bSavedMovementState)
|
||||
{
|
||||
P->bUseControllerRotationYaw = SaveComp->bSavedUseControllerRotationYaw;
|
||||
P->bUseControllerRotationPitch = SaveComp->bSavedUseControllerRotationPitch;
|
||||
P->bUseControllerRotationRoll = SaveComp->bSavedUseControllerRotationRoll;
|
||||
if (ACharacter* Char = Cast<ACharacter>(P))
|
||||
{
|
||||
if (UCharacterMovementComponent* Move = Char->GetCharacterMovement())
|
||||
{
|
||||
Move->bOrientRotationToMovement = SaveComp->bSavedOrientRotationToMovement;
|
||||
Move->bUseControllerDesiredRotation = SaveComp->bSavedUseControllerDesiredRotation;
|
||||
Move->SetMovementMode(SaveComp->SavedMovementMode, SaveComp->SavedCustomMovementMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (AAIController* AIC = Cast<AAIController>(P->GetController()))
|
||||
{
|
||||
AIC->SetActorTickEnabled(true);
|
||||
@@ -315,7 +337,7 @@ void APS_Editor_PlayerController::StopSimulation()
|
||||
if (!bSimulating) return;
|
||||
bSimulating = false;
|
||||
|
||||
// Disable AI logic on all spawned pawns
|
||||
// Disable AI logic and re-apply rotation/movement overrides on all spawned pawns
|
||||
if (SpawnManager)
|
||||
{
|
||||
for (const TWeakObjectPtr<AActor>& Weak : SpawnManager->GetSpawnedActors())
|
||||
@@ -331,6 +353,20 @@ void APS_Editor_PlayerController::StopSimulation()
|
||||
Brain->StopLogic(TEXT("PS_Editor"));
|
||||
}
|
||||
}
|
||||
|
||||
// Re-apply editor overrides (values were already saved at first disable)
|
||||
if (ACharacter* Char = Cast<ACharacter>(P))
|
||||
{
|
||||
if (UCharacterMovementComponent* Move = Char->GetCharacterMovement())
|
||||
{
|
||||
Move->bOrientRotationToMovement = false;
|
||||
Move->bUseControllerDesiredRotation = false;
|
||||
Move->SetMovementMode(MOVE_None);
|
||||
}
|
||||
}
|
||||
P->bUseControllerRotationYaw = false;
|
||||
P->bUseControllerRotationPitch = false;
|
||||
P->bUseControllerRotationRoll = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "PS_Editor_TimelineSubsystem.h"
|
||||
#include "PS_Editor_SpawnManager.h"
|
||||
#include "PS_Editor_PlayerController.h"
|
||||
#include "AIController.h"
|
||||
#include "BrainComponent.h"
|
||||
#include "JsonObjectConverter.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
#include "Misc/Paths.h"
|
||||
@@ -160,7 +162,47 @@ bool UPS_Editor_SceneLoader::LoadSceneFromData(UWorld* World, const FPS_Editor_S
|
||||
TimelineSS->SetData(SceneData.Timeline);
|
||||
if (!IsInEditorMode() && SceneData.Timeline.Tracks.Num() > 0)
|
||||
{
|
||||
TimelineSS->Play();
|
||||
// Start timeline only when all spawned pawns have their BT running.
|
||||
// Poll each tick until ready to avoid firing keys before AI-driven sequences start.
|
||||
TWeakObjectPtr<UPS_Editor_TimelineSubsystem> WeakTS = TimelineSS;
|
||||
TArray<TWeakObjectPtr<APawn>> WeakPawns;
|
||||
for (AActor* A : OutActors)
|
||||
{
|
||||
if (APawn* P = Cast<APawn>(A)) WeakPawns.Add(P);
|
||||
}
|
||||
TWeakObjectPtr<UWorld> WeakWorld = World;
|
||||
TSharedPtr<FTimerHandle> PollTimerPtr = MakeShared<FTimerHandle>();
|
||||
TSharedPtr<int32> PollCount = MakeShared<int32>(0);
|
||||
|
||||
World->GetTimerManager().SetTimer(*PollTimerPtr, [WeakTS, WeakPawns, WeakWorld, PollTimerPtr, PollCount]()
|
||||
{
|
||||
(*PollCount)++;
|
||||
|
||||
// Check all pawns have an AIController with a running BrainComponent
|
||||
bool bAllReady = true;
|
||||
for (const TWeakObjectPtr<APawn>& WeakP : WeakPawns)
|
||||
{
|
||||
APawn* P = WeakP.Get();
|
||||
if (!P) continue;
|
||||
AAIController* AIC = Cast<AAIController>(P->GetController());
|
||||
if (!AIC) { bAllReady = false; continue; }
|
||||
UBrainComponent* Brain = AIC->GetBrainComponent();
|
||||
if (!Brain || !Brain->IsRunning()) { bAllReady = false; continue; }
|
||||
}
|
||||
|
||||
// Wait for all pawns AND at least 2 poll cycles to ensure BT has started its first task
|
||||
if (bAllReady && *PollCount >= 2)
|
||||
{
|
||||
if (UPS_Editor_TimelineSubsystem* TS = WeakTS.Get())
|
||||
{
|
||||
TS->Play();
|
||||
}
|
||||
if (UWorld* W = WeakWorld.Get())
|
||||
{
|
||||
W->GetTimerManager().ClearTimer(*PollTimerPtr);
|
||||
}
|
||||
}
|
||||
}, 0.1f, true); // poll every 100ms
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "PS_Editor_SplineActor.h"
|
||||
#include "AIController.h"
|
||||
#include "BrainComponent.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
#include "PS_Editor_SplineEditable.h"
|
||||
#include "PS_Editor_TimelineSubsystem.h"
|
||||
#include "JsonObjectConverter.h"
|
||||
@@ -348,6 +350,39 @@ bool UPS_Editor_SceneSerializer::LoadScene(const FString& SceneName, UPS_Editor_
|
||||
Brain->StopLogic(TEXT("PS_Editor"));
|
||||
}
|
||||
}
|
||||
|
||||
// Save original movement/rotation state on SpawnableComponent, then override
|
||||
UPS_Editor_SpawnableComponent* SaveComp = Pawn->FindComponentByClass<UPS_Editor_SpawnableComponent>();
|
||||
if (SaveComp && !SaveComp->bSavedMovementState)
|
||||
{
|
||||
SaveComp->bSavedUseControllerRotationYaw = Pawn->bUseControllerRotationYaw;
|
||||
SaveComp->bSavedUseControllerRotationPitch = Pawn->bUseControllerRotationPitch;
|
||||
SaveComp->bSavedUseControllerRotationRoll = Pawn->bUseControllerRotationRoll;
|
||||
if (ACharacter* Char = Cast<ACharacter>(Pawn))
|
||||
{
|
||||
if (UCharacterMovementComponent* Move = Char->GetCharacterMovement())
|
||||
{
|
||||
SaveComp->bSavedOrientRotationToMovement = Move->bOrientRotationToMovement;
|
||||
SaveComp->bSavedUseControllerDesiredRotation = Move->bUseControllerDesiredRotation;
|
||||
SaveComp->SavedMovementMode = Move->MovementMode;
|
||||
SaveComp->SavedCustomMovementMode = Move->CustomMovementMode;
|
||||
}
|
||||
}
|
||||
SaveComp->bSavedMovementState = true;
|
||||
}
|
||||
|
||||
if (ACharacter* Char = Cast<ACharacter>(Pawn))
|
||||
{
|
||||
if (UCharacterMovementComponent* Move = Char->GetCharacterMovement())
|
||||
{
|
||||
Move->bOrientRotationToMovement = false;
|
||||
Move->bUseControllerDesiredRotation = false;
|
||||
Move->SetMovementMode(MOVE_None);
|
||||
}
|
||||
}
|
||||
Pawn->bUseControllerRotationYaw = false;
|
||||
Pawn->bUseControllerRotationPitch = false;
|
||||
Pawn->bUseControllerRotationRoll = false;
|
||||
};
|
||||
DisableAI(P);
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "AIController.h"
|
||||
#include "BrainComponent.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
#include "Engine/World.h"
|
||||
|
||||
namespace
|
||||
@@ -209,6 +211,40 @@ AActor* UPS_Editor_SpawnManager::SpawnFromCatalogAtLocation(int32 Index, FVector
|
||||
Brain->StopLogic(TEXT("PS_Editor"));
|
||||
}
|
||||
}
|
||||
|
||||
// Save original movement/rotation state on SpawnableComponent, then override
|
||||
UPS_Editor_SpawnableComponent* SaveComp = Pawn->FindComponentByClass<UPS_Editor_SpawnableComponent>();
|
||||
if (SaveComp && !SaveComp->bSavedMovementState)
|
||||
{
|
||||
SaveComp->bSavedUseControllerRotationYaw = Pawn->bUseControllerRotationYaw;
|
||||
SaveComp->bSavedUseControllerRotationPitch = Pawn->bUseControllerRotationPitch;
|
||||
SaveComp->bSavedUseControllerRotationRoll = Pawn->bUseControllerRotationRoll;
|
||||
if (ACharacter* Char = Cast<ACharacter>(Pawn))
|
||||
{
|
||||
if (UCharacterMovementComponent* Move = Char->GetCharacterMovement())
|
||||
{
|
||||
SaveComp->bSavedOrientRotationToMovement = Move->bOrientRotationToMovement;
|
||||
SaveComp->bSavedUseControllerDesiredRotation = Move->bUseControllerDesiredRotation;
|
||||
SaveComp->SavedMovementMode = Move->MovementMode;
|
||||
SaveComp->SavedCustomMovementMode = Move->CustomMovementMode;
|
||||
}
|
||||
}
|
||||
SaveComp->bSavedMovementState = true;
|
||||
}
|
||||
|
||||
// Override to prevent auto-rotation in editor
|
||||
if (ACharacter* Char = Cast<ACharacter>(Pawn))
|
||||
{
|
||||
if (UCharacterMovementComponent* Move = Char->GetCharacterMovement())
|
||||
{
|
||||
Move->bOrientRotationToMovement = false;
|
||||
Move->bUseControllerDesiredRotation = false;
|
||||
Move->SetMovementMode(MOVE_None);
|
||||
}
|
||||
}
|
||||
Pawn->bUseControllerRotationYaw = false;
|
||||
Pawn->bUseControllerRotationPitch = false;
|
||||
Pawn->bUseControllerRotationRoll = false;
|
||||
};
|
||||
DisableAI(SpawnedPawn);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "Engine/EngineTypes.h"
|
||||
#include "PS_Editor_SpawnableComponent.generated.h"
|
||||
|
||||
/**
|
||||
@@ -52,4 +53,15 @@ public:
|
||||
* Use scene loading filters to load only preload actors or exclude them. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawnable")
|
||||
bool bPreload = false;
|
||||
|
||||
// ---- Runtime-only: saved movement/rotation settings before editor override ----
|
||||
// Set when the editor disables AI/rotation overrides, restored on StartSimulation.
|
||||
bool bSavedMovementState = false;
|
||||
bool bSavedUseControllerRotationYaw = false;
|
||||
bool bSavedUseControllerRotationPitch = false;
|
||||
bool bSavedUseControllerRotationRoll = false;
|
||||
bool bSavedOrientRotationToMovement = false;
|
||||
bool bSavedUseControllerDesiredRotation = false;
|
||||
TEnumAsByte<EMovementMode> SavedMovementMode = MOVE_Walking;
|
||||
uint8 SavedCustomMovementMode = 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user