fix(interaction): 修复Narrative Widget淡出后残留累积并解耦时长
对应 P-016 game-reviewer 审查建议(非阻塞关注项)两项: 1. [生命周期] ExecuteHideNarrative 删除 Hide() 后立即 CachedNarrativeWidget=nullptr 的那行,与 Pickup 路径对称;让 RemoveNarrativeWidget 能检测到残留 Widget 并 RemoveFromParent,修复每次交互循环视口残留实例累积。 2. [一致性] NarrativeTextWidget/PickupNotificationWidget 的 Hide() 淡出时长改为从 FadeOutAnim->GetEndTime() 读取(无动画回退 0.2f),消除与动画时长耦合的硬编码;lambda 捕获由 [this] 改为 [WeakThis](TWeakObjectPtr) 避免悬空。 补充:两个 cpp 添加 #include Animation/WidgetAnimation.h(GetEndTime 需要 UWidgetAnimation 完整类型,原仅有前向声明)。UE5.7 编译验证 Result:Succeeded,零 error/warning。 Co-Authored-By: Claude Opus 5.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
{
|
||||
|
||||
@@ -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<UNarrativeTextWidget> 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
|
||||
{
|
||||
|
||||
@@ -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<UPickupNotificationWidget> 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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user