From dec183ff02e9274047401f7117abb694aee977f1 Mon Sep 17 00:00:00 2001 From: starmen1265 Date: Sun, 7 Jun 2026 01:52:56 +0800 Subject: [PATCH] =?UTF-8?q?docs(tech):=20=E6=94=B6=E9=9B=86=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E6=8A=80=E6=9C=AF=E8=AE=BE=E8=AE=A1=E6=96=87=E6=A1=A3?= =?UTF-8?q?=EF=BC=88P-009=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 基于执行案 v0.1.1 需求分析 8 步流程重新产出 - 新增 FCollectionConfig 统一 17 个可调参数管理 - 直接绑定 InteractionManager 委托,移除不存在的 ItemManager - 新增 3 个 UUserWidget 子类替代旧版裸 UUserWidget - 完整覆盖全部行为规则的实现方案映射 --- docs/technical/collection-system-tech.md | 1082 +++++++++++++++------- 1 file changed, 756 insertions(+), 326 deletions(-) diff --git a/docs/technical/collection-system-tech.md b/docs/technical/collection-system-tech.md index 140038c..6cf068c 100644 --- a/docs/technical/collection-system-tech.md +++ b/docs/technical/collection-system-tech.md @@ -1,227 +1,526 @@ -# 收集系统技术设计文档(v0.1 原型) +# 收集系统 技术设计文档(v0.1 原型) -> 任务ID: P-003(部分) -> 对应执行案: `docs/design/prototype/collection-system-execution.md` -> 关联系统: 物品系统(上游)、交互系统(间接) +> 任务ID: P-009 +> 对应执行案: `docs/design/prototype/collection-system-execution.md` (v0.1.1) +> 关联系统: 物品系统(上游单向接收)、交互系统(间接,通过物品系统委托) > 阶段: 原型 -> 日期: 2026-06-06 +> 日期: 2026-06-07 --- ## 概述 -收集系统是 v0.1 的辅助体验系统,追踪玩家在场景中收集"杂物"品类物品的进度。系统从物品系统(ItemManager)接收获得通知,判定是否为新收集,更新进度,弹出收集通知 UI。 +收集系统追踪玩家的收集进度,在达成收集里程碑时给予反馈。v0.1 原型实现单一品类「杂物」的 10 个物品追踪:接收物品系统的获得通知 → 判定新收集 → 更新进度 → 弹出收集通知。包含收集状态管理、收集通知 UI、收集进度 UI 和全部完成表现四个子模块。 -v0.1 范围极小:单一品类"杂物",共 10 个物品,无持久化、无分类、无图鉴。 - -**实现复杂度**:低 -**预估工期**:1-2 天 -**新增代码量**:约 200-300 行 C++ + 2 个 UMG 蓝图 +**实现复杂度**:低-中 +**预估工期**:2-3 天 +**新增/修改代码量**:约 250-350 行 C++ + 3 个 UMG 蓝图 --- -## 类与组件架构 +## 1. 需求功能点罗列 + +将执行文档中所有行为规则逐一列出,按实现方式分类。 + +### UE 内置功能 + +以下功能点直接使用 UE5 引擎内置能力,无需或仅需极少量自定义代码: + +| 功能点 | 使用的 UE 内置能力 | 对应行为规则 | 备注 | +|--------|-------------------|-------------|------| +| 收集通知淡入淡出动画 | UMG Widget Animation(FadeIn/FadeOut) | 收集通知 UI 显示动画 | UMG 内建动画时间轴 | +| 收集进度 UI 常驻显示 | UMG Widget AddToViewport | 收集进度 UI 常驻显示 | BeginPlay 时添加 | +| 通知延迟弹出 | FTimerHandle + SetTimer | 收集通知延迟 0.3s | 委托回调后启动定时器 | +| 通知自动移除 | FTimerHandle + SetTimer | 收集通知淡出后移除 | 停留+淡出时间后 RemoveFromParent | +| 重复判定数据结构 | TSet\ | 步骤 2 查重复 | O(1) 查找 | +| 进度 UI 屏幕定位 | UMG Anchors(Top Left) | 收集进度 UI 位置 | Anchors + Offset 配置 | +| 完成提示淡入淡出 | UMG Widget Animation | 全部收集完成表现 | 与通知 UI 同类 | + +### 自定义实现 + +以下功能点需要编写自定义 C++ / 蓝图代码: + +| 功能点 | 实现方式概述 | 对应行为规则 | 预估复杂度 | +|--------|-------------|-------------|-----------| +| 收集状态管理 | UActorComponent 维护 progress / collectedIDs / isComplete | 状态定义/状态转换 | 低 | +| 物品通知接收与处理 | 绑定 InteractionManager 委托,执行查品类→查重复→记录→通知→完成判定 | 收到获得物品通知后的流程(步骤 1-5) | 低-中 | +| 收集通知 UI 控制器 | 管理通知 Widget 创建、位置叠放、最大数量限制 | 收集通知 UI + 叠放规则 | 中 | +| 进度 UI 数值动画 | UMG Widget Scale 动画(弹性缩放) | 进度数字更新动画 + 完成金色缩放 | 低 | +| 全部完成表现 | 完成判定 → 进度变金色 → 延迟弹出中心文字 → 淡出 | 全部收集完成表现 | 低 | + +> **分类原则**:收集系统核心逻辑为简单的状态管理 + UI 控制,大部分功能基于 UE 定时器和 UMG 动画。自定义部分集中在通知 UI 的时序控制和叠放管理。 + +--- + +## 2. 功能领域图 + +```mermaid +flowchart TD + subgraph 上游系统 + ItemSys[物品系统
InteractionManager 委托] + end + + subgraph 收集核心 + NotifHandler[通知处理器
品类检查 + 去重 + 记录] + StateTracker[状态追踪器
progress / collectedIDs / isComplete] + CompleteHandler[完成处理器
完成判定 + 完成表现] + end + + subgraph 表现层 + NotifUI[收集通知 UI
WBP_CollectionNotification] + ProgressUI[收集进度 UI
WBP_CollectionProgress] + CompleteUI[完成提示 UI
WBP_CollectionComplete] + end + + ItemSys -.->|OnItemPickedUp / OnItemFound| NotifHandler + NotifHandler -->|新增收集| StateTracker + NotifHandler -->|延迟 0.3s 后显示| NotifUI + StateTracker -->|progress 达到 total| CompleteHandler + StateTracker -->|progress 更新| ProgressUI + CompleteHandler -->|进度变金色| ProgressUI + CompleteHandler -->|延迟后弹出| CompleteUI +``` + +### 模块职责说明 + +| 模块 | 职责 | 关键输入 | 关键输出 | +|------|------|---------|---------| +| 物品系统(上游) | 广播物品获得事件 | InteractionManager 在拾取/翻找动画事件点触发 | OnItemPickedUp/OnItemFound 委托广播 | +| 通知处理器 | 接收物品通知,执行品类检查和去重判定 | 委托回调(ItemId, DisplayName, Category) | 新增收集信号 → 状态追踪器;显示通知信号 → 通知 UI | +| 状态追踪器 | 维护收集进度数据 | 新增收集信号 | progress/collectedIDs/isComplete 状态;UI 更新触发 | +| 完成处理器 | 判断并执行全部收集完成表现 | progress == total 信号 | 进度变金色 + 延迟弹出完成提示 | +| 收集通知 UI | 显示「发现了新的杂物,进度 X/Y」通知 | 创建/移除指令 | 屏幕右上角淡入淡出通知 | +| 收集进度 UI | 常驻显示收集进度文本 | progress 更新 | 屏幕左上角进度文本 + 弹性动画 | +| 完成提示 UI | 屏幕中央完成文字 | 显示指令 | 「杂物全部收集完成!」白色大字 | + +--- + +## 3. 类设计图 + +```mermaid +classDiagram + class AChildHoodCharacter { + -UCollectionManager* CollectionManager + +GetCollectionManager() UCollectionManager* + } + + class UCollectionManager { + -FCollectionConfig Config + -int32 Progress + -TSet~FName~ CollectedIDs + -bool bIsComplete + -TArray~UCollectionNotifWidget*~ ActiveNotifications + -UCollectionProgressWidget* ProgressWidget + -TSubclassOf~UCollectionNotifWidget~ NotifWidgetClass + -TSubclassOf~UCollectionProgressWidget~ ProgressWidgetClass + -TSubclassOf~UCollectionCompleteWidget~ CompleteWidgetClass + -FTimerHandle NotifDelayTimerHandle + -FTimerHandle CompleteTimerHandle + +BeginPlay() void + -OnItemObtained(FName, FString, FName) void + -ProcessNewCollection(FName) void + -ShowCollectionNotification() void + -OnNotificationRemoved(UCollectionNotifWidget*) void + -CheckCompletion() void + -ShowCompletionPrompt() void + -UpdateProgressUI() void + } + + class UCollectionNotifWidget { + -FString DisplayText + -FLinearColor TextColor + -float FadeInDuration + -float StayDuration + -float FadeOutDuration + -FTimerHandle StayTimerHandle + -FTimerHandle FadeOutTimerHandle + +InitializeNotif(FString, FLinearColor, float, float, float) void + -OnFadeOutComplete() void + } + + class UCollectionProgressWidget { + -FLinearColor NormalColor + -FLinearColor CompleteColor + +UpdateProgress(int32, int32) void + +SetCompleteStyle() void + } + + class UCollectionCompleteWidget { + -float DisplayDuration + +ShowComplete() void + } + + class UInteractionManager { + +OnItemPickedUp FMLocalItemEvent + +OnItemFound FMLocalItemEvent + } + + class FCollectionConfig { + +FName TrackedCategory + +int32 TotalCount + +float NotifDelay + +float NotifFadeInDuration + +float NotifStayDuration + +float NotifFadeOutDuration + +FLinearColor NotifTextColor + +int32 MaxSimultaneousNotifs + +float ProgressMarginX + +float ProgressMarginY + +float BounceScale + +float BounceDuration + +float CompleteScale + +float CompleteScaleDuration + +float CompletePromptDelay + +float CompletePromptDuration + } + + AChildHoodCharacter *-- UCollectionManager : 组合 + UCollectionManager --> UInteractionManager : 关联(绑定委托) + UCollectionManager o-- UCollectionNotifWidget : 聚合(管理生命周期) + UCollectionManager o-- UCollectionProgressWidget : 聚合(管理生命周期) + UCollectionManager ..> UCollectionCompleteWidget : 按需创建 + UCollectionManager *-- FCollectionConfig : 组合(内含) +``` + +### 关系类型说明 + +| 关系类型 | 含义 | 本项目中的体现 | +|---------|------|--------------| +| 组合 `*--` | 生命周期绑定 | Character 持有 CollectionManager;CollectionManager 内含 Config | +| 聚合 `o--` | 引用但非拥有 | CollectionManager 管理通知/进度 Widget 的创建和销毁 | +| 关联 `-->` | 持久引用 | CollectionManager 绑定 InteractionManager 的委托 | +| 依赖 `..>` | 临时使用 | CollectionManager 按需创建完成提示 Widget | ### 新增类 | 类名 | 类型 | 职责 | 位置 | |------|------|------|------| -| `UCollectionManager` | UActorComponent | 收集进度追踪 + 收集通知 UI + 进度 UI | `Collection/` | +| `UCollectionManager` | UActorComponent | 收集系统核心:状态管理、委托监听、通知处理、UI 控制 | `Source/ChildHood/Collection/` | +| `UCollectionNotifWidget` | UUserWidget | 收集通知 UI 单条实例:淡入→停留→淡出生命周期 | `Source/ChildHood/Collection/` | +| `UCollectionProgressWidget` | UUserWidget | 收集进度常驻 UI:进度文本 + 弹性动画 + 完成变色 | `Source/ChildHood/Collection/` | +| `UCollectionCompleteWidget` | UUserWidget | 全部收集完成中心提示 UI:淡入→停留→淡出 | `Source/ChildHood/Collection/` | ### 修改类 | 类名 | 修改内容 | |------|---------| -| `AChildHoodCharacter` | 持有 CollectionManager,提供收集进度查询接口 | +| `AChildHoodCharacter` | 新增 `UCollectionManager*` 成员和 `GetCollectionManager()` 访问器;`BeginPlay()` 中确保收集系统初始化 | ### 新增蓝图 | 蓝图名 | 类型 | 用途 | |--------|------|------| -| `WBP_CollectionNotification` | UMG Widget | 收集通知("发现了新的杂物,进度 X/Y") | -| `WBP_CollectionProgress` | UMG Widget | 收集进度常驻 UI("📦 杂物 3/10") | -| `WBP_CollectionComplete` | UMG Widget | 全部收集完成提示("杂物全部收集完成!") | +| `WBP_CollectionNotification` | UMG Widget | 收集通知 UI(浅金色文字 + 淡入/停留/淡出三段动画) | +| `WBP_CollectionProgress` | UMG Widget | 收集进度 HUD(图标 + 品类名 + 进度文本 + 弹性缩放动画) | +| `WBP_CollectionComplete` | UMG Widget | 全部收集完成提示(白色大字 + 淡入淡出动画) | -### 架构关系 +--- -``` -AChildHoodCharacter -├── UInteractionManager (交互系统) -├── UItemManager (物品系统) -└── UCollectionManager (收集系统) - ├── TSet CollectedIDs // 已收集物品 ID 集合 - ├── int32 Progress // 当前收集数 - ├── bool bIsComplete // 是否全部完成 - ├── WBP_CollectionProgress // 常驻进度 UI - ├── WBP_CollectionNotification // 弹出式收集通知 - └── WBP_CollectionComplete // 完成提示 +## 4. 时序图 -数据流: -UItemManager (物品系统) - │ OnItemCollectedEvent(ItemId, Category, Number) - ▼ -UCollectionManager (收集系统) - ├── 1. 查品类 → 仅处理"杂物" - ├── 2. 查重复 → CollectedIDs.Contains(ItemId) - ├── 3. 记录 → CollectedIDs.Add + Progress++ - ├── 4. 弹收集通知 → WBP_CollectionNotification - └── 5. 完成判定 → Progress == TotalCount +> ⚠️ 收集系统涉及物品系统(委托源)→ 收集系统 → UI 三层协调,且有时序要求(通知延迟、完成延迟),满足时序图绘制条件。 + +### 4.1 新物品收集完整流程 + +```mermaid +sequenceDiagram + autonumber + participant IM as InteractionManager + participant CM as CollectionManager + participant State as 内部状态 + participant NotifUI as WBP_CollectionNotification + participant ProgressUI as WBP_CollectionProgress + + IM->>CM: OnItemPickedUp/OnItemFound(ItemId, DisplayName, "杂物") + activate CM + + Note over CM: 步骤 1: 查品类 + CM->>CM: Category == TrackedCategory("杂物")? + + Note over CM: 步骤 2: 查重复 + CM->>State: CollectedIDs.Contains(ItemId)? + + Note over CM,State: 判定为新收集 + + Note over CM: 步骤 3: 记录 + CM->>State: CollectedIDs.Add(ItemId), Progress = Num() + + Note over CM: 步骤 4: 弹收集通知(延迟) + CM->>CM: SetTimer(NotifDelay = 0.3s) + + Note over CM: 同步更新进度 UI + CM->>ProgressUI: UpdateProgress(Progress, TotalCount) + activate ProgressUI + ProgressUI->>ProgressUI: PlayBounceAnimation (1.2x, 0.2s) + deactivate ProgressUI + + Note over CM: 等待 0.3 秒... + + CM->>NotifUI: CreateWidget + AddToViewport + activate NotifUI + NotifUI->>NotifUI: PlayFadeIn (0.2s) + NotifUI->>NotifUI: StayTimer (2.0s) + + Note over CM: 步骤 5: 完成判定 + CM->>State: Progress >= TotalCount? + + alt 是(全部完成) + CM->>State: bIsComplete = true + CM->>ProgressUI: SetCompleteStyle() — 文字变金色 + CM->>ProgressUI: PlayCompleteAnimation (1.4x, 0.5s) + + Note over NotifUI: 通知继续显示/淡出 + + NotifUI->>NotifUI: PlayFadeOut (0.3s) + NotifUI->>NotifUI: RemoveFromParent() + deactivate NotifUI + + Note over CM: 等待通知消失 + 0.5s 延迟 + + CM->>CM: ShowCompletionPrompt() + Note over CM: 屏幕中央弹出 "杂物全部收集完成!" + else 未完成 + NotifUI->>NotifUI: PlayFadeOut (0.3s) + NotifUI->>NotifUI: RemoveFromParent() + deactivate NotifUI + end + + deactivate CM ``` --- -## 核心数据结构 +## 5. 核心数据结构 -### 配置数据 +### 枚举 + +> 收集系统 v0.1 不引入新的枚举类型。所有状态通过内置类型管理(int32 progress, bool bIsComplete, TSet\ collectedIDs)。 + +### 配置数据结构 ```cpp -UCLASS() +// 收集系统配置(v0.1 在 CollectionManager 构造函数中设置默认值) +USTRUCT(BlueprintType) +struct FCollectionConfig +{ + GENERATED_BODY() + + // === 追踪品类 === + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collection|Category") + FName TrackedCategory = FName("杂物"); // 追踪的物品品类名称 + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collection|Category") + int32 TotalCount = 10; // 品类总收集数 + + // === 收集通知参数 === + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collection|Notification", + meta = (ClampMin = "0.1", ClampMax = "0.5")) + float NotifDelay = 0.3f; // 拾取通知后到收集通知弹出的间隔(秒) + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collection|Notification", + meta = (ClampMin = "0.1", ClampMax = "0.3")) + float NotifFadeInDuration = 0.2f; // 通知淡入时长(秒) + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collection|Notification", + meta = (ClampMin = "1.5", ClampMax = "3.0")) + float NotifStayDuration = 2.0f; // 通知停留时长(秒) + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collection|Notification", + meta = (ClampMin = "0.2", ClampMax = "0.5")) + float NotifFadeOutDuration = 0.3f; // 通知淡出时长(秒) + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collection|Notification") + FLinearColor NotifTextColor = FLinearColor(0.94f, 0.82f, 0.38f, 1.f); // 浅金色 #F0D060 + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collection|Notification", + meta = (ClampMin = "1", ClampMax = "3")) + int32 MaxSimultaneousNotifs = 2; // 最大同屏收集通知数 + + // === 进度 UI 参数 === + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collection|Progress", + meta = (ClampMin = "0.02", ClampMax = "0.05")) + float ProgressMarginX = 0.03f; // 距左边距(屏幕宽度比例) + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collection|Progress", + meta = (ClampMin = "0.02", ClampMax = "0.05")) + float ProgressMarginY = 0.03f; // 距上边距(屏幕高度比例) + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collection|Progress", + meta = (ClampMin = "1.1", ClampMax = "1.5")) + float BounceScale = 1.2f; // 进度数字弹性缩放倍率 + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collection|Progress", + meta = (ClampMin = "0.1", ClampMax = "0.4")) + float BounceDuration = 0.2f; // 进度数字弹性时长(秒) + + // === 完成表现参数 === + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collection|Complete", + meta = (ClampMin = "1.2", ClampMax = "1.8")) + float CompleteScale = 1.4f; // 完成时金色缩放倍率 + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collection|Complete", + meta = (ClampMin = "0.3", ClampMax = "0.8")) + float CompleteScaleDuration = 0.5f; // 完成缩放时长(秒) + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collection|Complete", + meta = (ClampMin = "0.3", ClampMax = "1.0")) + float CompletePromptDelay = 0.5f; // 收集通知消失后到完成提示弹出的间隔(秒) + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Collection|Complete", + meta = (ClampMin = "1.5", ClampMax = "3.0")) + float CompletePromptDuration = 2.0f; // 完成提示显示时长(秒) +}; +``` + +### UCollectionManager 关键数据定义 + +```cpp +UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent)) class UCollectionManager : public UActorComponent { GENERATED_BODY() - // === 配置参数(构造函数设置默认值) === +public: + UCollectionManager(); - // 收集品类配置 + // === 配置参数 === UPROPERTY(EditAnywhere, Category="Collection") - FString TargetCategory = TEXT("杂物"); // 追踪的品类名 + FCollectionConfig Config; - UPROPERTY(EditAnywhere, Category="Collection") - int32 TotalCount = 10; // 品类总数 + // === UI 类引用(蓝图设置) === + UPROPERTY(EditDefaultsOnly, Category="Collection|UI") + TSubclassOf NotifWidgetClass; - // 收集通知参数 - UPROPERTY(EditAnywhere, Category="Collection|Notification") - float NotificationDelay = 0.3f; // 拾取通知后延迟(秒) + UPROPERTY(EditDefaultsOnly, Category="Collection|UI") + TSubclassOf ProgressWidgetClass; - UPROPERTY(EditAnywhere, Category="Collection|Notification") - float NotificationFadeInDuration = 0.2f; + UPROPERTY(EditDefaultsOnly, Category="Collection|UI") + TSubclassOf CompleteWidgetClass; - UPROPERTY(EditAnywhere, Category="Collection|Notification") - float NotificationDisplayDuration = 2.0f; - - UPROPERTY(EditAnywhere, Category="Collection|Notification") - float NotificationFadeOutDuration = 0.3f; - - UPROPERTY(EditAnywhere, Category="Collection|Notification") - int32 MaxSimultaneousNotifications = 2; - - UPROPERTY(EditAnywhere, Category="Collection|Notification") - FLinearColor NotificationColor = FLinearColor(0.94f, 0.82f, 0.38f); // #F0D060 浅金色 - - // 进度 UI 参数 - UPROPERTY(EditAnywhere, Category="Collection|Progress") - float ProgressScaleAmplitude = 1.2f; // 弹性缩放倍率 - - UPROPERTY(EditAnywhere, Category="Collection|Progress") - float ProgressScaleDuration = 0.2f; // 弹性动画时长 - - // 完成表现参数 - UPROPERTY(EditAnywhere, Category="Collection|Complete") - float CompleteScaleAmplitude = 1.4f; - - UPROPERTY(EditAnywhere, Category="Collection|Complete") - float CompleteScaleDuration = 0.5f; - - UPROPERTY(EditAnywhere, Category="Collection|Complete") - float CompleteMessageDelay = 0.5f; // 收集通知消失后延迟 - - UPROPERTY(EditAnywhere, Category="Collection|Complete") - float CompleteMessageDisplayDuration = 2.0f; +protected: + virtual void BeginPlay() override; // === 运行时状态 === + int32 Progress = 0; // 当前已收集数 + TSet CollectedIDs; // 已收集物品 ID 集合 + bool bIsComplete = false; // 是否全部收集完成 - UPROPERTY() - TSet CollectedIDs; + // === UI 引用 === + TArray ActiveNotifications; // 当前活跃的通知 Widget + UCollectionProgressWidget* ProgressWidget = nullptr; // 进度 UI - int32 Progress = 0; - bool bIsComplete = false; + // === 定时器 === + FTimerHandle NotifDelayTimerHandle; // 通知延迟定时器 + FTimerHandle CompleteTimerHandle; // 完成提示定时器 - // UI 实例 - UPROPERTY() - UUserWidget* ProgressWidget = nullptr; + // === 关键接口 === - UPROPERTY() - TArray ActiveNotifications; + // 物品获得回调(绑定到 InteractionManager 委托) + UFUNCTION() + void OnItemObtained(FName ItemId, const FString& DisplayName, const FName& Category); - // UI 类引用 - UPROPERTY(EditAnywhere, Category="Collection|UI") - TSubclassOf NotificationWidgetClass; + // 处理新收集(查品类 + 查重复通过后调用) + void ProcessNewCollection(FName ItemId); - UPROPERTY(EditAnywhere, Category="Collection|UI") - TSubclassOf ProgressWidgetClass; + // 显示收集通知(延迟定时器回调) + void ShowCollectionNotification(); - UPROPERTY(EditAnywhere, Category="Collection|UI") - TSubclassOf CompleteWidgetClass; + // 通知 Widget 移除回调 + UFUNCTION() + void OnNotificationRemoved(UCollectionNotifWidget* Widget); - // 延迟通知定时器 - FTimerHandle NotificationDelayTimer; - FName PendingNotificationItemId; + // 检查完成状态并触发完成表现 + void CheckCompletion(); + + // 显示完成提示 UI + void ShowCompletionPrompt(); + + // 更新进度 UI 文本和动画 + void UpdateProgressUI(); }; ``` --- -## 行为规则 → 实现方案映射 +## 6. 行为规则 → 实现方案映射 -### 一、收到获得物品通知后的流程 +对执行文档中每条行为规则,逐一说明实现方案。 -#### 入口:ItemManager 委托回调 +### 一、收到获得物品通知后的流程(步骤 1-5) + +#### 步骤 1: 查品类 — 检查物品品类是否为追踪品类 + +**实现**:在委托回调中检查 Category 是否匹配 TrackedCategory ```cpp -// UCollectionManager::BeginPlay 中绑定 ItemManager 委托 -void UCollectionManager::BeginPlay() +void UCollectionManager::OnItemObtained(FName ItemId, const FString& DisplayName, const FName& Category) { - Super::BeginPlay(); - - // 查找 ItemManager 并绑定委托 - if (auto* ItemManager = GetOwner()->FindComponentByClass()) - { - ItemManager->OnItemCollectedEvent.AddDynamic(this, &UCollectionManager::OnItemReceived); - } - - // 创建常驻进度 UI - CreateProgressWidget(); + // 步骤 1: 查品类 + if (Category != Config.TrackedCategory) return; // 非追踪品类,忽略 + // → 继续查重复 + ProcessNewCollection(ItemId); } ``` -#### 五步流程实现 +**可调参数**:`Config.TrackedCategory`(默认 "杂物") + +--- + +#### 步骤 2: 查重复 — 检查物品编号是否已收集 + +**实现**:TSet\ 的 Contains 查找 ```cpp -void UCollectionManager::OnItemReceived(FName ItemId, const FString& Category, const FString& ItemNumber) +void UCollectionManager::ProcessNewCollection(FName ItemId) { - // 步骤 1:查品类 — 不是"杂物"则忽略 - if (Category != TargetCategory) return; + // 步骤 2: 查重复 + if (CollectedIDs.Contains(ItemId)) return; // 非新收集,流程结束 + // → 新收集,继续记录 +``` - // 步骤 2:查重复 — 已收集过则忽略 - if (CollectedIDs.Contains(ItemId)) return; +> TSet::Contains 为 O(1) 操作,v0.1 最多 10 个元素,性能无忧。 - // 步骤 3:记录 +--- + +#### 步骤 3: 记录 — 将编号加入 collectedIDs,progress +1 + +**实现**: + +```cpp + // 步骤 3: 记录 CollectedIDs.Add(ItemId); - Progress++; + Progress = CollectedIDs.Num(); // 用 Set 大小作为权威进度值,避免状态不一致 +``` - // 步骤 4:弹收集通知(延迟 0.3 秒,等拾取通知先显示) - PendingNotificationItemId = ItemId; +--- + +#### 步骤 4: 弹收集通知(延迟 0.3s) + +**实现**:启动定时器,延迟后创建通知 Widget + +```cpp + // 步骤 4: 弹收集通知(延迟) GetWorld()->GetTimerManager().SetTimer( - NotificationDelayTimer, + NotifDelayTimerHandle, this, &UCollectionManager::ShowCollectionNotification, - NotificationDelay, - false - ); + Config.NotifDelay, false); - // 更新进度 UI - UpdateProgressWidget(); + // 同步更新进度 UI + UpdateProgressUI(); +``` - // 步骤 5:完成判定 - if (Progress >= TotalCount && !bIsComplete) +--- + +#### 步骤 5: 完成判定 — progress 达到 10 则 isComplete = true + +**实现**:ProcessNewCollection 末尾检查 + +```cpp + // 步骤 5: 完成判定 + if (Progress >= Config.TotalCount && !bIsComplete) { bIsComplete = true; - // 完成表现延迟到收集通知消失后 - GetWorld()->GetTimerManager().SetTimer( - CompleteTimer, - this, &UCollectionManager::ShowCompletionCelebration, - NotificationDelay + NotificationDisplayDuration + NotificationFadeOutDuration + CompleteMessageDelay, - false - ); + CheckCompletion(); // 触发完成表现 } } ``` @@ -230,272 +529,403 @@ void UCollectionManager::OnItemReceived(FName ItemId, const FString& Category, c ### 二、收集通知 UI -#### WBP_CollectionNotification +#### 显示规则 -**实现**:UMG Widget Blueprint - -- **位置**:屏幕右上角,拾取通知下方 -- **Anchors**:TopRight,Offset X = -20, Offset Y 依赖拾取通知位置 -- **内容**:`TextBlock` 显示 `发现了新的杂物,进度 {Progress}/{TotalCount}` -- **文字颜色**:浅金色 `#F0D060`(FLinearColor(0.94, 0.82, 0.38)) - -#### 显示动画(Widget Animation) - -``` -淡入 (0.2s) → 停留 (2.0s) → 淡出 (0.3s) → 自动移除 -``` +**实现**:ShowCollectionNotification() 创建 WBP_CollectionNotification 并添加到视口 ```cpp void UCollectionManager::ShowCollectionNotification() { - if (!NotificationWidgetClass) return; + // 检查同屏最大数量 + if (ActiveNotifications.Num() >= Config.MaxSimultaneousNotifs) return; - // 检查最大同屏数 - CleanExpiredNotifications(); - if (ActiveNotifications.Num() >= MaxSimultaneousNotifications) return; + UCollectionNotifWidget* NotifWidget = CreateWidget( + GetWorld(), NotifWidgetClass); + if (!NotifWidget) return; - // 创建通知 Widget - UUserWidget* Notification = CreateWidget(GetWorld(), NotificationWidgetClass); - Notification->AddToViewport(); + // 设置内容 + FString Content = FString::Printf(TEXT("发现了新的杂物,进度 %d/%d"), + Progress, Config.TotalCount); - // 设置内容(通过 BlueprintImplementableEvent 或直接设置 TextBlock) - // 调用蓝图函数设置文字:FString::Format("发现了新的杂物,进度 {0}/{1}", ...) - SetNotificationText(Notification, Progress, TotalCount); + NotifWidget->InitializeNotif(Content, Config.NotifTextColor, + Config.NotifFadeInDuration, Config.NotifStayDuration, Config.NotifFadeOutDuration); - ActiveNotifications.Add(Notification); + // 设置位置:屏幕右上角,上一条通知下方 + NotifWidget->AddToViewport(10); // Z-order - // 设置自动销毁定时器 - float TotalLifetime = NotificationFadeInDuration + NotificationDisplayDuration + NotificationFadeOutDuration; - FTimerHandle AutoRemoveTimer; - GetWorld()->GetTimerManager().SetTimer(AutoRemoveTimer, [this, Notification]() - { - Notification->RemoveFromParent(); - ActiveNotifications.Remove(Notification); - }, TotalLifetime, false); + ActiveNotifications.Add(NotifWidget); } ``` +**可调参数**:NotifDelay (0.3s, 0.1-0.5s), NotifTextColor (#F0D060), MaxSimultaneousNotifs (2, 1-3) + +--- + +#### 显示动画(淡入 → 停留 → 淡出) + +**实现**:UCollectionNotifWidget 内部管理三阶段生命周期 + +- **淡入**:UMG Animation 从 Opacity 0 → 1,时长 0.2s +- **停留**:SetTimer 延迟 2.0s 后触发淡出 +- **淡出**:UMG Animation 从 Opacity 1 → 0,时长 0.3s;完成后调用 RemoveFromParent() + +```cpp +void UCollectionNotifWidget::InitializeNotif(const FString& Text, const FLinearColor& Color, + float FadeIn, float Stay, float FadeOut) +{ + // 更新 TextBlock 内容和颜色 + // 保存动画参数 + FadeInDuration = FadeIn; + StayDuration = Stay; + FadeOutDuration = FadeOut; +} + +void UCollectionNotifWidget::NativeOnAddedToViewport() +{ + // 播放淡入动画 + PlayAnimation(FadeInAnim); + // 启动停留定时器 + GetWorld()->GetTimerManager().SetTimer(StayTimerHandle, this, + &UCollectionNotifWidget::StartFadeOut, StayDuration, false); +} + +void UCollectionNotifWidget::StartFadeOut() +{ + PlayAnimation(FadeOutAnim); + GetWorld()->GetTimerManager().SetTimer(FadeOutTimerHandle, this, + &UCollectionNotifWidget::OnFadeOutComplete, FadeOutDuration, false); +} + +void UCollectionNotifWidget::OnFadeOutComplete() +{ + RemoveFromParent(); + // 通知 CollectionManager 从 ActiveNotifications 中移除引用 +} +``` + +**可调参数**:NotifFadeInDuration (0.2s, 0.1-0.3s), NotifStayDuration (2.0s, 1.5-3.0s), NotifFadeOutDuration (0.3s, 0.2-0.5s) + +--- + #### 与拾取通知的叠放 -- 收集通知通过 Anchor 定位在拾取通知下方 -- 实现方式:通知 Widget 的 Anchor 设为 TopRight,Y 偏移量 = 拾取通知高度 + 间距 -- v0.1 简化:固定偏移位置,不动态检测拾取通知实际高度 +**实现**:通知位置基于 Anchor 和垂直偏移量 + +- **Anchor**:屏幕右上角(HorizontalAlignment: Right, VerticalAlignment: Top) +- **与拾取通知关系**:收集通知始终在拾取通知下方。v0.1 简化为固定偏移位置(拾取通知默认位置下方约 60px,可调) +- **收集通知之间**:新通知从上一条下方弹出,间距约 40px(可调) +- **最大 2 条**:ActiveNotifications.Num() >= MaxSimultaneousNotifs 时不再创建 + +> v0.1 原型简化:收集通知使用固定 Anchor + 垂直偏移。不动态追踪拾取通知的具体像素位置。若拾取通知 UI 不存在,收集通知在其默认位置显示。 --- ### 三、收集进度 UI -#### WBP_CollectionProgress +#### 显示规则 -**实现**:UMG Widget Blueprint +**实现**:WBP_CollectionProgress 在 CollectionManager::BeginPlay() 时添加到视口 -- **位置**:屏幕左上角 -- **Anchors**:TopLeft,Offset X = 3% 屏幕宽度,Offset Y = 3% 屏幕高度 -- **格式**:`📦 杂物 {Progress}/{TotalCount}` -- **图标**:v0.1 用简单 emoji 或纯色方块(20×20 像素) -- **文字**:白色,正常字体 -- **常驻显示**:游戏开始后始终可见,直到角色销毁 - -#### 数值更新动画 +- **Anchor**:Top Left(Anchors: X=0, Y=0) +- **Offset**:左边距 3% 屏幕宽度,上边距 3% 屏幕高度(在 NativeOnAddedToViewport 中根据屏幕尺寸计算) +- **格式**:`📦 杂物 3/10`(品类图标 + 品类名 + 当前/总数) +- **图标**:白盒阶段用 emoji 📦 或纯色方块,尺寸约 20×20px +- **文字**:白色,正常字体大小 +- **常驻显示**:游戏开始后始终可见,不随交互状态隐藏 ```cpp -void UCollectionManager::UpdateProgressWidget() +void UCollectionManager::BeginPlay() { - if (!ProgressWidget) return; + Super::BeginPlay(); - // 更新文字 - UpdateProgressText(Progress, TotalCount); + // 初始化状态 + Progress = 0; + CollectedIDs.Empty(); + bIsComplete = false; - // 播放弹性动画(蓝图中的 Widget Animation) - PlayProgressPulseAnimation(ProgressScaleAmplitude, ProgressScaleDuration); + // 创建进度 UI + if (ProgressWidgetClass) + { + APlayerController* PC = GetWorld()->GetFirstPlayerController(); + if (PC) + { + ProgressWidget = CreateWidget(PC, ProgressWidgetClass); + if (ProgressWidget) + { + ProgressWidget->AddToViewport(5); + ProgressWidget->UpdateProgress(0, Config.TotalCount); + } + } + } + + // 绑定 InteractionManager 委托 + AChildHoodCharacter* Char = Cast(GetOwner()); + if (Char && Char->GetInteractionManager()) + { + UInteractionManager* IM = Char->GetInteractionManager(); + IM->OnItemPickedUp.AddDynamic(this, &UCollectionManager::OnItemObtained); + IM->OnItemFound.AddDynamic(this, &UCollectionManager::OnItemObtained); + } } ``` -弹性动画在 UMG 蓝图中通过 Widget Animation 实现: -- 缩放序列:1.0 → 1.2 → 1.0,时长 0.2 秒 -- 使用 Ease: Out 重心曲线 +**可调参数**:ProgressMarginX (3%, 2%-5%), ProgressMarginY (3%, 2%-5%) + +--- + +#### 数值更新动画 + +**实现**:UpdateProgress() 触发 UMG Scale 动画 + +```cpp +void UCollectionProgressWidget::UpdateProgress(int32 Current, int32 Total) +{ + // 更新文本 + FString Text = FString::Printf(TEXT("📦 杂物 %d/%d"), Current, Total); + // 设置到 TextBlock + + // 播放弹性缩放动画 + PlayAnimation(BounceAnim); +} +``` + +- **弹性效果**:数字部分 RenderScale 从 1.0 → 1.2 → 1.0,时长 0.2s +- **使用 UMG Animation 时间轴**:FloatCurve 在 0→0.2→0 映射 Scale X/Y + +**可调参数**:BounceScale (1.2, 1.1-1.5), BounceDuration (0.2s, 0.1-0.4s) --- ### 四、全部收集完成表现 -#### WBP_CollectionComplete +**触发条件**:ProcessNewCollection() 中 Progress 达到 TotalCount 时调用 CheckCompletion() -**实现**:UMG Widget Blueprint +**实现步骤**: -- **位置**:屏幕正中 -- **内容**:`杂物全部收集完成!`,白色大字 -- **显示时长**:2.0 秒后淡出 +**1. 进度 UI 变金色 + 弹性缩放**: ```cpp -void UCollectionManager::ShowCompletionCelebration() +void UCollectionManager::CheckCompletion() { - // 1. 进度 UI 数字变金色 + 放大弹性效果 - if (ProgressWidget) - { - SetProgressTextColor(FLinearColor::Gold); - PlayProgressPulseAnimation(CompleteScaleAmplitude, CompleteScaleDuration); - } + if (!ProgressWidget) return; - // 2. 屏幕中央弹出完成提示 - if (CompleteWidgetClass) - { - UUserWidget* CompleteWidget = CreateWidget(GetWorld(), CompleteWidgetClass); - CompleteWidget->AddToViewport(); + // 进度数字变金色 + ProgressWidget->SetCompleteStyle(); - // 自动淡出移除 - FTimerHandle AutoRemoveTimer; - GetWorld()->GetTimerManager().SetTimer(AutoRemoveTimer, [CompleteWidget]() - { - CompleteWidget->RemoveFromParent(); - }, CompleteMessageDisplayDuration, false); - } + // 完成缩放动画(1.4x, 0.5s) + ProgressWidget->PlayCompleteAnimation(); } ``` +**2. 延迟弹出完成提示**: + +完成提示需要在最后一条收集通知消失后 0.5s 弹出。从 ProcessNewCollection 调用时刻算起: + +``` +总延迟 = NotifDelay + NotifStayDuration + NotifFadeOutDuration + CompletePromptDelay + = 0.3 + 2.0 + 0.3 + 0.5 + = 3.1s +``` + +```cpp +// 在 ProcessNewCollection 中(完成判定分支内) +float TotalDelay = Config.NotifDelay + Config.NotifStayDuration + + Config.NotifFadeOutDuration + Config.CompletePromptDelay; +GetWorld()->GetTimerManager().SetTimer(CompleteTimerHandle, + this, &UCollectionManager::ShowCompletionPrompt, + TotalDelay, false); +``` + +**3. 完成提示 UI(WBP_CollectionComplete)**: + +```cpp +void UCollectionManager::ShowCompletionPrompt() +{ + if (!CompleteWidgetClass) return; + + UCollectionCompleteWidget* CompleteWidget = CreateWidget( + GetWorld(), CompleteWidgetClass); + if (!CompleteWidget) return; + + CompleteWidget->AddToViewport(20); // 最高 Z-order + CompleteWidget->ShowComplete(); // 开始淡入 + 定时淡出 +} +``` + +- **位置**:屏幕正中央(Anchors: Center) +- **内容**:「杂物全部收集完成!」白色大字 +- **动画**:淡入 → 停留 2.0s → 淡出 → 自动移除 +- **无其他效果**:过场动画、NPC 反应等留给后续版本 + +**可调参数**:CompleteScale (1.4, 1.2-1.8), CompleteScaleDuration (0.5s, 0.3-0.8s), CompletePromptDelay (0.5s, 0.3-1.0s), CompletePromptDuration (2.0s, 1.5-3.0s) + --- -### 五、重启状态处理 +### 五、边界情况处理 -v0.1 无持久化,每次游戏启动自动重置。 +#### 重复获得同一物品 + +**实现**:CollectedIDs.Contains(ItemId) 在步骤 2 拦截。v0.1 交互物均为 oneShot,理论上不会重复触发同一 ItemId 的通知。即使发生,查重机制保证 progress 不超过 TotalCount,不会弹出重复的收集通知。 + +#### 全部收集完成后继续收到通知 + +**实现**:v0.1 不会发生(oneShot 机制 + 最多 10 个物品)。即使发生,CollectedIDs 查重拦截,无副作用。bIsComplete 仅触发一次完成表现。 + +#### 重启状态处理 + +**实现**:v0.1 无持久化。CollectionManager::BeginPlay() 中完全重置: ```cpp void UCollectionManager::BeginPlay() { - // ... 绑定委托 ... - - // 初始化状态(保证干净状态) - CollectedIDs.Empty(); + Super::BeginPlay(); Progress = 0; + CollectedIDs.Empty(); bIsComplete = false; - - // 创建进度 UI,显示 "📦 杂物 0/10" - CreateProgressWidget(); + // ... 创建 UI、绑定委托 } ``` -无需额外重置逻辑——组件生命周期由 UE 管理,角色销毁时自动清理。 +重启后进度 UI 恢复显示 `📦 杂物 0/10`。 + +#### 收集通知与拾取通知时序 + +**实现**:收集通知通过 NotifDelay (0.3s) 定时器延迟弹出,保证与拾取通知有时间差。若拾取通知已消失(停留时间已过),收集通知在其原位置显示。两者不会完全重叠。 --- -## 数据流总览 +## 7. 美术资源需求 -``` -UInteractionManager (交互系统) - │ 拾取/翻找完成 - ▼ -UItemManager (物品系统) - │ OnItemPickedUpEvent → WBP_PickupNotification(拾取通知) - │ OnItemCollectedEvent(ItemId, Category, Number) - ▼ -UCollectionManager (收集系统) - ├── 1. Category != "杂物" → 忽略 - ├── 2. CollectedIDs.Contains(ItemId) → 忽略 - ├── 3. CollectedIDs.Add + Progress++ - ├── 4. 延迟 0.3s → WBP_CollectionNotification(收集通知) - │ 内容:"发现了新的杂物,进度 X/Y" - │ 颜色:浅金色 #F0D060 - ├── 5. Progress == 10 → bIsComplete = true - │ → 进度 UI 变金色 - │ → 延迟 → WBP_CollectionComplete(完成提示) - └── WBP_CollectionProgress(常驻进度 UI) - 内容:"📦 杂物 X/10" - 位置:左上角 -``` +| 资源类型 | 名称 | 数量 | 规格 | 触发时机/用途 | +|----------|------|------|------|---------------| +| UI Widget | WBP_CollectionNotification | 1 | UMG Widget,浅金色文字 (#F0D060),含淡入/停留/淡出三段动画 | 新收集判定时弹出 | +| UI Widget | WBP_CollectionProgress | 1 | UMG Widget,图标(20×20px) + 文字,含弹性缩放动画 + 完成金色动画 | 游戏开始常驻显示 | +| UI Widget | WBP_CollectionComplete | 1 | UMG Widget,白色大字,含淡入/淡出动画 | 全部收集完成时弹出 | +| 2D 图标 | 品类图标(杂物占位) | 1 | 约 20×20px,白盒阶段用 emoji 📦 或纯色方块 | 进度 UI 中的品类图标 | + +> v0.1 原型阶段:UI Widget 使用简单 UMG 实现(TextBlock + Border),图标用 emoji 或纯色方块占位。字体使用引擎默认字体。后续版本替换为正式美术资源和自定义字体。 --- -## 边界情况处理 - -| 边界情况 | 实现方案 | -|---------|---------| -| 重复获得同一物品 | CollectedIDs.Contains() 判重,已收集则忽略。不会导致 Progress 超过 10 | -| 全部收集完成后继续收到通知 | bIsComplete 已为 true,CollectedIDs 查重拦截。无副作用 | -| 游戏重启 | 无持久化,组件重新初始化。CollectedIDs/Progress/bIsComplete 全部重置 | -| 收集通知与拾取通知时序冲突 | 0.3s 延迟确保拾取通知先显示。固定偏移避免视觉重叠 | -| 多条收集通知快速触发 | MaxSimultaneousNotifications = 2,超出则丢弃。v0.1 正常流程不会触发此限制 | - ---- - -## 美术资源需求 - -### UI - -| 资源类型 | 名称 | 数量 | 规格 | 用途 | -|----------|------|------|------|------| -| UMG Widget | 收集通知 | 1 | 浅金色文字 `#F0D060`,右上角弹出,2 秒淡出 | 新收集时弹出 | -| UMG Widget | 收集进度 | 1 | 白色文字 + 占位图标,左上角常驻 | 显示收集进度 | -| UMG Widget | 完成提示 | 1 | 白色大字居中,2 秒淡出 | 全部收集完成 | - -### 图标 - -| 资源类型 | 名称 | 数量 | 规格 | 用途 | -|----------|------|------|------|------| -| 2D 图标 | 杂物品类图标 | 1 | 20×20 像素,简单符号或纯色方块 | 进度 UI 中的品类标识 | - -> v0.1 不需要 3D 模型、动画或其他美术资产。所有交互物的外观由交互系统负责。 - ---- - -## 技术风险与注意事项 +## 8. 技术风险与注意事项 | 风险 | 等级 | 说明 | 缓解措施 | |------|------|------|---------| -| 收集通知位置与拾取通知重叠 | 🟢 低 | 固定偏移可能不够精确 | v0.1 接受不完美;后续版本可动态检测拾取通知位置 | -| Widget 生命周期管理 | 🟢 低 | 通知 Widget 需要正确销毁 | 定时器自动移除;组件销毁时清理所有 Active Notifications | -| ItemManager 委托时序 | 🟢 低 | BeginPlay 中绑定委托时 ItemManager 可能未初始化 | 使用 FindComponentByClass 查找,若为空则在 Tick 中延迟绑定一次 | +| 通知叠放位置与拾取通知冲突 | 🟡中 | 收集通知需要与拾取通知协调屏幕位置,但两个系统独立管理各自的 UI | v0.1 简化处理:收集通知 Anchor 固定在右上角,不动态追踪拾取通知;后续版本引入统一 HUD 管理器协调位置 | +| 完成提示时机精度 | 🟢低 | 完成提示需要在最后一条收集通知消失后 0.5s 弹出,延迟计算依赖动画时间参数 | 使用精确的定时器计算总延迟,参数集中管理在 FCollectionConfig 中 | +| 通知 Widget 内存管理 | 🟢低 | 频繁创建/销毁通知 Widget 理论上有性能开销 | v0.1 最多 10 个物品、最多同屏 2 条通知,性能无忧;后续可改为对象池 | +| 委托绑定时机 | 🟢低 | CollectionManager 需在 InteractionManager 初始化后绑定委托 | Character::BeginPlay() 按序初始化两个组件;CollectionManager 中绑定前做非空检查 | +| 多通知定时器覆盖 | 🟢低 | 使用单个 NotifDelayTimerHandle,连续拾取可能覆盖前一个定时器 | v0.1 物品均为 oneShot 且拾取间隔远大于 0.3s,不会发生;后续版本改为队列机制 | -### 需要注意 +### 注意事项 -1. **委托绑定时机**:UCollectionManager 必须在 ItemManager 的 OnItemCollectedEvent 委托注册之后才能收到通知。确保 ItemManager 的初始化先于 CollectionManager,或在 CollectionManager 中做延迟绑定。 -2. **UI 叠放层级**:收集通知的 ZOrder 需要高于进度 UI,低于可能的弹窗/菜单。建议:进度 UI = 0,收集通知 = 1,完成提示 = 2。 -3. **进度 UI 文字更新**:使用 `SetNum` 或 `SetText` 更新 TextBlock 时需确保在 GameThread 上执行(委托回调本身在 GT 上)。 +1. **委托绑定时机**:CollectionManager 在 BeginPlay() 中通过 `GetOwner()->GetComponentByClass()` 获取引用并绑定。需确保 InteractionManager 已初始化。若为 nullptr 则在 Tick 中延迟绑定(最多重试 N 帧后报错)。 +2. **v0.1 无拾取通知 UI**:执行文档中拾取通知由物品系统负责,但 v0.1 物品系统仅为委托层(InteractionManager 上的委托)。收集通知的 0.3s 延迟仍按设计实现(为拾取通知预留时间窗口),即使拾取通知 UI 暂未实现。 +3. **通知移除回调**:UCollectionNotifWidget 淡出完成后需通知 CollectionManager 从 ActiveNotifications 中移除自身引用。使用委托或弱引用回调实现,避免循环引用。 +4. **FName 作为 ID**:收集系统使用 FName(ItemId)作为去重键,与 InteractionManager 委托广播的 ItemId 类型一致。 +5. **TSet 线程安全**:v0.1 单线程游戏,TSet 无需加锁。 --- -## 可调参数汇总 +## 9. 数据流 -所有参数均在构造函数中设置默认值,蓝图可覆盖: +### 配置参数流 -| 参数 | C++ 属性 | 默认值 | 对应执行案参数 | -|------|---------|--------|-------------| -| 追踪品类名 | `TargetCategory` | "杂物" | — | -| 品类总数 | `TotalCount` | 10 | 10 个杂物品 | -| 通知延迟 | `NotificationDelay` | 0.3 秒 | 0.1-0.5 秒 | -| 通知淡入 | `NotificationFadeInDuration` | 0.2 秒 | 0.1-0.3 秒 | -| 通知停留 | `NotificationDisplayDuration` | 2.0 秒 | 1.5-3.0 秒 | -| 通知淡出 | `NotificationFadeOutDuration` | 0.3 秒 | 0.2-0.5 秒 | -| 通知颜色 | `NotificationColor` | #F0D060 | — | -| 最大同屏数 | `MaxSimultaneousNotifications` | 2 | 1-3 条 | -| 进度弹性倍率 | `ProgressScaleAmplitude` | 1.2 | 1.1-1.5 | -| 进度弹性时长 | `ProgressScaleDuration` | 0.2 秒 | 0.1-0.4 秒 | -| 完成弹性倍率 | `CompleteScaleAmplitude` | 1.4 | 1.2-1.8 | -| 完成弹性时长 | `CompleteScaleDuration` | 0.5 秒 | 0.3-0.8 秒 | -| 完成提示延迟 | `CompleteMessageDelay` | 0.5 秒 | 0.3-1.0 秒 | -| 完成提示时长 | `CompleteMessageDisplayDuration` | 2.0 秒 | 1.5-3.0 秒 | +``` +FCollectionConfig(C++ 构造函数默认值) + → CollectionManager EditAnywhere 属性(蓝图/Details 面板可覆盖) + → 运行时读取 Config 中的参数驱动 UI 和定时器 +``` + +v0.1 原型阶段所有参数在 C++ 中设置默认值,蓝图可覆盖。批量生产阶段迁移到 DataAsset 配置。 + +### 运行时状态流 + +``` +InteractionManager (ExecuteAnimEvent) + → OnItemPickedUp / OnItemFound.Broadcast(ItemId, DisplayName, Category) + → UCollectionManager::OnItemObtained() (委托回调) + ├─ 查品类 (Category == TrackedCategory?) → 不匹配则忽略 + ├─ 查重复 (CollectedIDs.Contains?) → 已存在则忽略 + ├─ 记录 (CollectedIDs.Add + Progress = Num()) + ├─ UpdateProgressUI() → WBP_CollectionProgress (文本 + 弹性动画) + ├─ SetTimer(NotifDelay) → ShowCollectionNotification() → WBP_CollectionNotification + └─ 完成判定 (Progress >= TotalCount) + → bIsComplete = true + → SetCompleteStyle() → WBP_CollectionProgress (金色) + → SetTimer(TotalDelay) → ShowCompletionPrompt() → WBP_CollectionComplete +``` --- -## 文件结构 +## 10. 可调参数汇总 + +| 参数 | C++ 属性 | 默认值 | 位置 | +|------|---------|--------|------| +| 追踪品类 | `Config.TrackedCategory` | "杂物" | CollectionManager 构造函数 | +| 品类总数 | `Config.TotalCount` | 10 | CollectionManager 构造函数 | +| 通知延迟 | `Config.NotifDelay` | 0.3s | CollectionManager 构造函数 | +| 通知淡入时长 | `Config.NotifFadeInDuration` | 0.2s | CollectionManager 构造函数 | +| 通知停留时长 | `Config.NotifStayDuration` | 2.0s | CollectionManager 构造函数 | +| 通知淡出时长 | `Config.NotifFadeOutDuration` | 0.3s | CollectionManager 构造函数 | +| 通知文字颜色 | `Config.NotifTextColor` | #F0D060 | CollectionManager 构造函数 | +| 最大同屏通知数 | `Config.MaxSimultaneousNotifs` | 2 | CollectionManager 构造函数 | +| 进度 UI 左边距 | `Config.ProgressMarginX` | 3% | CollectionManager 构造函数 | +| 进度 UI 上边距 | `Config.ProgressMarginY` | 3% | CollectionManager 构造函数 | +| 进度数字弹性缩放 | `Config.BounceScale` | 1.2 | CollectionManager 构造函数 | +| 进度数字弹性时长 | `Config.BounceDuration` | 0.2s | CollectionManager 构造函数 | +| 完成金色缩放 | `Config.CompleteScale` | 1.4 | CollectionManager 构造函数 | +| 完成金色时长 | `Config.CompleteScaleDuration` | 0.5s | CollectionManager 构造函数 | +| 完成提示延迟 | `Config.CompletePromptDelay` | 0.5s | CollectionManager 构造函数 | +| 完成提示显示时长 | `Config.CompletePromptDuration` | 2.0s | CollectionManager 构造函数 | + +--- + +## 11. 文件结构 ``` Source/ChildHood/ ├── Collection/ -│ ├── CollectionManager.h/.cpp # 收集系统主组件 -│ └── CollectionTypes.h # (可选,v0.1 极简可能不需要独立类型文件) -├── Player/ -│ └── ChildHoodCharacter.h/.cpp # 修改:添加 CollectionManager -└── Interaction/ - └── ItemManager.h/.cpp # 已有:OnItemCollectedEvent 委托 +│ ├── CollectionTypes.h # FCollectionConfig 结构体定义 +│ ├── CollectionManager.h/.cpp # 收集系统核心组件 +│ ├── CollectionNotifWidget.h/.cpp # 收集通知 UI 基类 +│ ├── CollectionProgressWidget.h/.cpp # 收集进度 UI 基类 +│ └── CollectionCompleteWidget.h/.cpp # 完成提示 UI 基类 +├── ChildHoodCharacter.h/.cpp # 修改:添加 CollectionManager 成员和访问器 Content/ -├── Blueprints/ -│ └── Characters/ -│ └── BP_ChildHoodCharacter # 玩家角色蓝图(修改:添加 CollectionManager 组件引用) ├── UI/ -│ ├── WBP_CollectionNotification # 收集通知 Widget -│ ├── WBP_CollectionProgress # 收集进度 Widget -│ └── WBP_CollectionComplete # 完成提示 Widget -└── Data/ - └── (v0.1 不需要 DataAsset,参数硬编码在 C++ 中) +│ ├── WBP_CollectionNotification.uasset # 收集通知 UMG Widget +│ ├── WBP_CollectionProgress.uasset # 收集进度 UMG Widget +│ └── WBP_CollectionComplete.uasset # 完成提示 UMG Widget ``` + +--- + +## 工期预估与实现拆分 + +### 工作量估算 + +| 维度 | 估算 | +|------|------| +| 总工期 | 2-3 天 | +| 新增 C++ 代码 | 250-350 行 | +| 新增蓝图 | 3 个 UMG Widget | +| 修改现有代码 | 约 10 行(Character 添加成员) | +| 实现复杂度 | 低-中 | + +### 实现拆分建议 + +供步骤⑥(程序实现)参考的推荐拆分方式,每个子任务可独立验证: + +| 子任务 | 内容 | 预估时间 | 依赖 | 验证方式 | +|--------|------|---------|------|---------| +| 6b-1 | 数据类型定义 | CollectionTypes.h FCollectionConfig + CollectionManager 基础框架(状态管理 + BeginPlay) | 0.5 天 | 交互系统 6a | 编译通过 | +| 6b-2 | 委托绑定 + OnItemObtained 完整流程(查品类→查重复→记录→完成判定) | 0.5 天 | 6b-1, 交互系统 | PIE 控制台输出收集日志(新收集/重复/忽略) | +| 6b-3 | WBP_CollectionProgress UI + UpdateProgress + 弹性动画 | 0.5 天 | 6b-1 | PIE 中拾取物品后进度文本更新、数字弹跳 | +| 6b-4 | WBP_CollectionNotification UI + 延迟弹出 + 淡入淡出 + 叠放 | 1 天 | 6b-2 | PIE 中拾取新物品后 0.3s 看到金色通知弹出、自动消失 | +| 6b-5 | WBP_CollectionComplete UI + 完成表现(进度变金色 + 中心文字) | 0.5 天 | 6b-3, 6b-4 | 拾取全部 10 个物品后看到完成提示 | +| 6b-6 | 集成测试 + 参数微调 | 0.5 天 | 6b-5 | 按探索路线完整拾取全部物品验证 | + +> **关键路径**:6b-1 → 6b-2 → 6b-4 → 6b-5 → 6b-6。6b-3(进度 UI)可在 6b-1 完成后并行。总关键路径约 3 天。 +> **前置依赖**:交互系统的 InteractionManager 组件需已完成(特别是 OnItemPickedUp/OnItemFound 委托),方可进行 6b-2 的委托绑定测试。