Fix (missing) timeline tracks + lost editable properties on 2nd scene load

FindActorById now skips stale TWeakObjectPtr entries instead of returning
the first null match, RegisterActorId prunes stale + same-GUID entries
before adding, and Clear() resets the ActorIds map so a fresh scenario
starts with no ghost references.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 16:08:26 +02:00
parent 721c9bb5c3
commit 9877c1b6de

View File

@@ -300,6 +300,10 @@ void UPS_Editor_TimelineSubsystem::Clear()
bIsPlaying = false;
LastAppliedValues.Empty();
PrePlayBaseline.Reset();
// Drop the old ActorIds — the previous scenario's actors have been destroyed and the
// weak references are now stale. Keeping them around confuses FindActorById when the
// next scenario happens to reuse the same FGuid (it can return a stale null entry).
ActorIds.Reset();
OnTimelineChanged.Broadcast();
}
@@ -570,11 +574,18 @@ FGuid UPS_Editor_TimelineSubsystem::FindActorId(AActor* Actor) const
AActor* UPS_Editor_TimelineSubsystem::FindActorById(const FGuid& Id) const
{
if (!Id.IsValid()) return nullptr;
// Iterate every pair: the map can legitimately contain stale weak references from a
// previous scenario whose actors were destroyed. Without this check the first match on
// GUID can return a null key before we reach the live entry, giving "(missing)" after
// the second load of the same scenario.
for (const TPair<TWeakObjectPtr<AActor>, FGuid>& Pair : ActorIds)
{
if (Pair.Value == Id)
{
return Pair.Key.Get();
if (AActor* Live = Pair.Key.Get())
{
return Live;
}
}
}
return nullptr;
@@ -583,6 +594,16 @@ AActor* UPS_Editor_TimelineSubsystem::FindActorById(const FGuid& Id) const
void UPS_Editor_TimelineSubsystem::RegisterActorId(AActor* Actor, const FGuid& Id)
{
if (!Actor || !Id.IsValid()) return;
// Prune any stale entries plus any previous entry holding the same GUID (the old actor
// has been destroyed and replaced by this one). Keeps the map tight and ensures
// FindActorById cannot hit a ghost key with the same GUID.
for (auto It = ActorIds.CreateIterator(); It; ++It)
{
if (!It.Key().IsValid() || It.Value() == Id)
{
It.RemoveCurrent();
}
}
ActorIds.Add(TWeakObjectPtr<AActor>(Actor), Id);
}