diff --git a/Source/ChildHood/Interaction/EffectManager.cpp b/Source/ChildHood/Interaction/EffectManager.cpp index caedf25..8fd5f8a 100644 --- a/Source/ChildHood/Interaction/EffectManager.cpp +++ b/Source/ChildHood/Interaction/EffectManager.cpp @@ -444,13 +444,19 @@ void FEffectManager::ExecuteShowNarrative(const FInteractAction& Action) void FEffectManager::ExecuteHideNarrative() { // P-016:淡出叙事文本 Widget(执行案要求 0.2s 淡出) - // Hide() 播放 FadeOutAnim,0.2s 后 SetVisibility(Hidden)。Widget 实例保留在视口中, - // 下次 ShowNarrative 时由 RemoveNarrativeWidget 先行清理避免叠加。 + // + // 对称 Pickup 路径的清理机制(修复 reviewer 建议关注项 1:淡出后视口残留累积): + // - 不立即置空缓存(原 bug:调用 Hide() 后立即 CachedNarrativeWidget = nullptr, + // 但 Hide() 只淡出 + SetVisibility(Hidden) 不 RemoveFromParent,导致残留 Widget + // 实例仍挂在视口 Root,且缓存已空 → PerformCleanup / 下次 ShowNarrative 开头的 + // RemoveNarrativeWidget 因 IsValid 返回 false 检测不到残留,每次交互循环累积一个实例)。 + // - 现在与 Pickup 对称:Hide() 后保留缓存,由 RemoveNarrativeWidget + // (PerformCleanup 兜底 / 下次 ShowNarrative 开头)检测到有效缓存并 RemoveFromParent, + // 保证「淡出 → 下次清理 RemoveFromParent」闭环。 if (CachedNarrativeWidget.IsValid()) { CachedNarrativeWidget->Hide(); - CachedNarrativeWidget = nullptr; - UE_LOG(LogTemp, Verbose, TEXT("EffectManager: HideNarrative (Widget 淡出)")); + UE_LOG(LogTemp, Verbose, TEXT("EffectManager: HideNarrative (Widget 淡出,下次清理时移除)")); } else { diff --git a/Source/ChildHood/Interaction/NarrativeTextWidget.cpp b/Source/ChildHood/Interaction/NarrativeTextWidget.cpp index 14cb308..dd5e8d2 100644 --- a/Source/ChildHood/Interaction/NarrativeTextWidget.cpp +++ b/Source/ChildHood/Interaction/NarrativeTextWidget.cpp @@ -1,6 +1,7 @@ // ChildHood Project - 文本底框 UI 基类 实现 #include "Interaction/NarrativeTextWidget.h" +#include "Animation/WidgetAnimation.h" // UWidgetAnimation 完整定义(GetEndTime 需要完整类型) #include "Components/TextBlock.h" void UNarrativeTextWidget::NativeOnInitialized() @@ -36,18 +37,30 @@ void UNarrativeTextWidget::Hide() { if (!bIsVisible) return; + // 淡出时长从 FadeOutAnim 实际时长读取,消除与动画时长耦合的硬编码 + // (美术调整 FadeOutAnim 时长时无需同步改 C++) + const float FadeOutDuration = (FadeOutAnim ? FadeOutAnim->GetEndTime() : 0.2f); + if (FadeOutAnim) { PlayAnimation(FadeOutAnim, 0.0f, 1, EUMGSequencePlayMode::Forward, 1.0f); + } + + if (UWorld* World = GetWorld()) + { + // 捕获弱引用避免悬空 this(Widget 可能在 Timer 触发前被 RemoveFromParent/GC) + // 注:RemoveFromParent 不在此处执行,而由 EffectManager::RemoveNarrativeWidget 统一负责 + // (与 PickupNotificationWidget::Hide() 对称),保证「淡出 → 下次清理 RemoveFromParent」闭环。 + TWeakObjectPtr WeakThis(this); FTimerHandle TempHandle; - if (UWorld* World = GetWorld()) + World->GetTimerManager().SetTimer(TempHandle, [WeakThis]() { - World->GetTimerManager().SetTimer(TempHandle, [this]() + if (UNarrativeTextWidget* Self = WeakThis.Get()) { - bIsVisible = false; - SetVisibility(ESlateVisibility::Hidden); - }, 0.2f, false); - } + Self->bIsVisible = false; + Self->SetVisibility(ESlateVisibility::Hidden); + } + }, FadeOutDuration, false); } else { diff --git a/Source/ChildHood/Interaction/PickupNotificationWidget.cpp b/Source/ChildHood/Interaction/PickupNotificationWidget.cpp index e6630c4..abc4a2c 100644 --- a/Source/ChildHood/Interaction/PickupNotificationWidget.cpp +++ b/Source/ChildHood/Interaction/PickupNotificationWidget.cpp @@ -1,6 +1,7 @@ // ChildHood Project - 拾取通知 UI 基类 实现 #include "Interaction/PickupNotificationWidget.h" +#include "Animation/WidgetAnimation.h" // UWidgetAnimation 完整定义(GetEndTime 需要完整类型) #include "Components/TextBlock.h" void UPickupNotificationWidget::NativeOnInitialized() @@ -36,18 +37,27 @@ void UPickupNotificationWidget::Hide() { if (!bIsVisible) return; + // 淡出时长从 FadeOutAnim 实际时长读取,消除与动画时长耦合的硬编码 + const float FadeOutDuration = (FadeOutAnim ? FadeOutAnim->GetEndTime() : 0.2f); + if (FadeOutAnim) { PlayAnimation(FadeOutAnim, 0.0f, 1, EUMGSequencePlayMode::Forward, 1.0f); + } + + if (UWorld* World = GetWorld()) + { + // 捕获弱引用避免悬空 this + TWeakObjectPtr WeakThis(this); FTimerHandle TempHandle; - if (UWorld* World = GetWorld()) + World->GetTimerManager().SetTimer(TempHandle, [WeakThis]() { - World->GetTimerManager().SetTimer(TempHandle, [this]() + if (UPickupNotificationWidget* Self = WeakThis.Get()) { - bIsVisible = false; - SetVisibility(ESlateVisibility::Hidden); - }, 0.2f, false); - } + Self->bIsVisible = false; + Self->SetVisibility(ESlateVisibility::Hidden); + } + }, FadeOutDuration, false); } else {