基于执行案 v0.1.1 需求分析 8 步流程重新产出 - 新增 FCollectionConfig 统一 17 个可调参数管理 - 直接绑定 InteractionManager 委托,移除不存在的 ItemManager - 新增 3 个 UUserWidget 子类替代旧版裸 UUserWidget - 完整覆盖全部行为规则的实现方案映射
37 KiB
收集系统 技术设计文档(v0.1 原型)
任务ID: P-009 对应执行案:
docs/design/prototype/collection-system-execution.md(v0.1.1) 关联系统: 物品系统(上游单向接收)、交互系统(间接,通过物品系统委托) 阶段: 原型 日期: 2026-06-07
概述
收集系统追踪玩家的收集进度,在达成收集里程碑时给予反馈。v0.1 原型实现单一品类「杂物」的 10 个物品追踪:接收物品系统的获得通知 → 判定新收集 → 更新进度 → 弹出收集通知。包含收集状态管理、收集通知 UI、收集进度 UI 和全部完成表现四个子模块。
实现复杂度:低-中 预估工期: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<FName> | 步骤 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. 功能领域图
flowchart TD
subgraph 上游系统
ItemSys[物品系统<br/>InteractionManager 委托]
end
subgraph 收集核心
NotifHandler[通知处理器<br/>品类检查 + 去重 + 记录]
StateTracker[状态追踪器<br/>progress / collectedIDs / isComplete]
CompleteHandler[完成处理器<br/>完成判定 + 完成表现]
end
subgraph 表现层
NotifUI[收集通知 UI<br/>WBP_CollectionNotification]
ProgressUI[收集进度 UI<br/>WBP_CollectionProgress]
CompleteUI[完成提示 UI<br/>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. 类设计图
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 控制 | Source/ChildHood/Collection/ |
UCollectionNotifWidget |
UUserWidget | 收集通知 UI 单条实例:淡入→停留→淡出生命周期 | Source/ChildHood/Collection/ |
UCollectionProgressWidget |
UUserWidget | 收集进度常驻 UI:进度文本 + 弹性动画 + 完成变色 | Source/ChildHood/Collection/ |
UCollectionCompleteWidget |
UUserWidget | 全部收集完成中心提示 UI:淡入→停留→淡出 | Source/ChildHood/Collection/ |
修改类
| 类名 | 修改内容 |
|---|---|
AChildHoodCharacter |
新增 UCollectionManager* 成员和 GetCollectionManager() 访问器;BeginPlay() 中确保收集系统初始化 |
新增蓝图
| 蓝图名 | 类型 | 用途 |
|---|---|---|
WBP_CollectionNotification |
UMG Widget | 收集通知 UI(浅金色文字 + 淡入/停留/淡出三段动画) |
WBP_CollectionProgress |
UMG Widget | 收集进度 HUD(图标 + 品类名 + 进度文本 + 弹性缩放动画) |
WBP_CollectionComplete |
UMG Widget | 全部收集完成提示(白色大字 + 淡入淡出动画) |
4. 时序图
⚠️ 收集系统涉及物品系统(委托源)→ 收集系统 → UI 三层协调,且有时序要求(通知延迟、完成延迟),满足时序图绘制条件。
4.1 新物品收集完整流程
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<FName> collectedIDs)。
配置数据结构
// 收集系统配置(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 关键数据定义
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class UCollectionManager : public UActorComponent
{
GENERATED_BODY()
public:
UCollectionManager();
// === 配置参数 ===
UPROPERTY(EditAnywhere, Category="Collection")
FCollectionConfig Config;
// === UI 类引用(蓝图设置) ===
UPROPERTY(EditDefaultsOnly, Category="Collection|UI")
TSubclassOf<UCollectionNotifWidget> NotifWidgetClass;
UPROPERTY(EditDefaultsOnly, Category="Collection|UI")
TSubclassOf<UCollectionProgressWidget> ProgressWidgetClass;
UPROPERTY(EditDefaultsOnly, Category="Collection|UI")
TSubclassOf<UCollectionCompleteWidget> CompleteWidgetClass;
protected:
virtual void BeginPlay() override;
// === 运行时状态 ===
int32 Progress = 0; // 当前已收集数
TSet<FName> CollectedIDs; // 已收集物品 ID 集合
bool bIsComplete = false; // 是否全部收集完成
// === UI 引用 ===
TArray<UCollectionNotifWidget*> ActiveNotifications; // 当前活跃的通知 Widget
UCollectionProgressWidget* ProgressWidget = nullptr; // 进度 UI
// === 定时器 ===
FTimerHandle NotifDelayTimerHandle; // 通知延迟定时器
FTimerHandle CompleteTimerHandle; // 完成提示定时器
// === 关键接口 ===
// 物品获得回调(绑定到 InteractionManager 委托)
UFUNCTION()
void OnItemObtained(FName ItemId, const FString& DisplayName, const FName& Category);
// 处理新收集(查品类 + 查重复通过后调用)
void ProcessNewCollection(FName ItemId);
// 显示收集通知(延迟定时器回调)
void ShowCollectionNotification();
// 通知 Widget 移除回调
UFUNCTION()
void OnNotificationRemoved(UCollectionNotifWidget* Widget);
// 检查完成状态并触发完成表现
void CheckCompletion();
// 显示完成提示 UI
void ShowCompletionPrompt();
// 更新进度 UI 文本和动画
void UpdateProgressUI();
};
6. 行为规则 → 实现方案映射
对执行文档中每条行为规则,逐一说明实现方案。
一、收到获得物品通知后的流程(步骤 1-5)
步骤 1: 查品类 — 检查物品品类是否为追踪品类
实现:在委托回调中检查 Category 是否匹配 TrackedCategory
void UCollectionManager::OnItemObtained(FName ItemId, const FString& DisplayName, const FName& Category)
{
// 步骤 1: 查品类
if (Category != Config.TrackedCategory) return; // 非追踪品类,忽略
// → 继续查重复
ProcessNewCollection(ItemId);
}
可调参数:Config.TrackedCategory(默认 "杂物")
步骤 2: 查重复 — 检查物品编号是否已收集
实现:TSet<FName> 的 Contains 查找
void UCollectionManager::ProcessNewCollection(FName ItemId)
{
// 步骤 2: 查重复
if (CollectedIDs.Contains(ItemId)) return; // 非新收集,流程结束
// → 新收集,继续记录
TSet::Contains 为 O(1) 操作,v0.1 最多 10 个元素,性能无忧。
步骤 3: 记录 — 将编号加入 collectedIDs,progress +1
实现:
// 步骤 3: 记录
CollectedIDs.Add(ItemId);
Progress = CollectedIDs.Num(); // 用 Set 大小作为权威进度值,避免状态不一致
步骤 4: 弹收集通知(延迟 0.3s)
实现:启动定时器,延迟后创建通知 Widget
// 步骤 4: 弹收集通知(延迟)
GetWorld()->GetTimerManager().SetTimer(
NotifDelayTimerHandle,
this, &UCollectionManager::ShowCollectionNotification,
Config.NotifDelay, false);
// 同步更新进度 UI
UpdateProgressUI();
步骤 5: 完成判定 — progress 达到 10 则 isComplete = true
实现:ProcessNewCollection 末尾检查
// 步骤 5: 完成判定
if (Progress >= Config.TotalCount && !bIsComplete)
{
bIsComplete = true;
CheckCompletion(); // 触发完成表现
}
}
二、收集通知 UI
显示规则
实现:ShowCollectionNotification() 创建 WBP_CollectionNotification 并添加到视口
void UCollectionManager::ShowCollectionNotification()
{
// 检查同屏最大数量
if (ActiveNotifications.Num() >= Config.MaxSimultaneousNotifs) return;
UCollectionNotifWidget* NotifWidget = CreateWidget<UCollectionNotifWidget>(
GetWorld(), NotifWidgetClass);
if (!NotifWidget) return;
// 设置内容
FString Content = FString::Printf(TEXT("发现了新的杂物,进度 %d/%d"),
Progress, Config.TotalCount);
NotifWidget->InitializeNotif(Content, Config.NotifTextColor,
Config.NotifFadeInDuration, Config.NotifStayDuration, Config.NotifFadeOutDuration);
// 设置位置:屏幕右上角,上一条通知下方
NotifWidget->AddToViewport(10); // Z-order
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()
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 和垂直偏移量
- Anchor:屏幕右上角(HorizontalAlignment: Right, VerticalAlignment: Top)
- 与拾取通知关系:收集通知始终在拾取通知下方。v0.1 简化为固定偏移位置(拾取通知默认位置下方约 60px,可调)
- 收集通知之间:新通知从上一条下方弹出,间距约 40px(可调)
- 最大 2 条:ActiveNotifications.Num() >= MaxSimultaneousNotifs 时不再创建
v0.1 原型简化:收集通知使用固定 Anchor + 垂直偏移。不动态追踪拾取通知的具体像素位置。若拾取通知 UI 不存在,收集通知在其默认位置显示。
三、收集进度 UI
显示规则
实现:WBP_CollectionProgress 在 CollectionManager::BeginPlay() 时添加到视口
- Anchor:Top Left(Anchors: X=0, Y=0)
- Offset:左边距 3% 屏幕宽度,上边距 3% 屏幕高度(在 NativeOnAddedToViewport 中根据屏幕尺寸计算)
- 格式:
📦 杂物 3/10(品类图标 + 品类名 + 当前/总数) - 图标:白盒阶段用 emoji 📦 或纯色方块,尺寸约 20×20px
- 文字:白色,正常字体大小
- 常驻显示:游戏开始后始终可见,不随交互状态隐藏
void UCollectionManager::BeginPlay()
{
Super::BeginPlay();
// 初始化状态
Progress = 0;
CollectedIDs.Empty();
bIsComplete = false;
// 创建进度 UI
if (ProgressWidgetClass)
{
APlayerController* PC = GetWorld()->GetFirstPlayerController();
if (PC)
{
ProgressWidget = CreateWidget<UCollectionProgressWidget>(PC, ProgressWidgetClass);
if (ProgressWidget)
{
ProgressWidget->AddToViewport(5);
ProgressWidget->UpdateProgress(0, Config.TotalCount);
}
}
}
// 绑定 InteractionManager 委托
AChildHoodCharacter* Char = Cast<AChildHoodCharacter>(GetOwner());
if (Char && Char->GetInteractionManager())
{
UInteractionManager* IM = Char->GetInteractionManager();
IM->OnItemPickedUp.AddDynamic(this, &UCollectionManager::OnItemObtained);
IM->OnItemFound.AddDynamic(this, &UCollectionManager::OnItemObtained);
}
}
可调参数:ProgressMarginX (3%, 2%-5%), ProgressMarginY (3%, 2%-5%)
数值更新动画
实现:UpdateProgress() 触发 UMG Scale 动画
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)
四、全部收集完成表现
触发条件:ProcessNewCollection() 中 Progress 达到 TotalCount 时调用 CheckCompletion()
实现步骤:
1. 进度 UI 变金色 + 弹性缩放:
void UCollectionManager::CheckCompletion()
{
if (!ProgressWidget) return;
// 进度数字变金色
ProgressWidget->SetCompleteStyle();
// 完成缩放动画(1.4x, 0.5s)
ProgressWidget->PlayCompleteAnimation();
}
2. 延迟弹出完成提示:
完成提示需要在最后一条收集通知消失后 0.5s 弹出。从 ProcessNewCollection 调用时刻算起:
总延迟 = NotifDelay + NotifStayDuration + NotifFadeOutDuration + CompletePromptDelay
= 0.3 + 2.0 + 0.3 + 0.5
= 3.1s
// 在 ProcessNewCollection 中(完成判定分支内)
float TotalDelay = Config.NotifDelay + Config.NotifStayDuration
+ Config.NotifFadeOutDuration + Config.CompletePromptDelay;
GetWorld()->GetTimerManager().SetTimer(CompleteTimerHandle,
this, &UCollectionManager::ShowCompletionPrompt,
TotalDelay, false);
3. 完成提示 UI(WBP_CollectionComplete):
void UCollectionManager::ShowCompletionPrompt()
{
if (!CompleteWidgetClass) return;
UCollectionCompleteWidget* CompleteWidget = CreateWidget<UCollectionCompleteWidget>(
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)
五、边界情况处理
重复获得同一物品
实现:CollectedIDs.Contains(ItemId) 在步骤 2 拦截。v0.1 交互物均为 oneShot,理论上不会重复触发同一 ItemId 的通知。即使发生,查重机制保证 progress 不超过 TotalCount,不会弹出重复的收集通知。
全部收集完成后继续收到通知
实现:v0.1 不会发生(oneShot 机制 + 最多 10 个物品)。即使发生,CollectedIDs 查重拦截,无副作用。bIsComplete 仅触发一次完成表现。
重启状态处理
实现:v0.1 无持久化。CollectionManager::BeginPlay() 中完全重置:
void UCollectionManager::BeginPlay()
{
Super::BeginPlay();
Progress = 0;
CollectedIDs.Empty();
bIsComplete = false;
// ... 创建 UI、绑定委托
}
重启后进度 UI 恢复显示 📦 杂物 0/10。
收集通知与拾取通知时序
实现:收集通知通过 NotifDelay (0.3s) 定时器延迟弹出,保证与拾取通知有时间差。若拾取通知已消失(停留时间已过),收集通知在其原位置显示。两者不会完全重叠。
7. 美术资源需求
| 资源类型 | 名称 | 数量 | 规格 | 触发时机/用途 |
|---|---|---|---|---|
| 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 或纯色方块占位。字体使用引擎默认字体。后续版本替换为正式美术资源和自定义字体。
8. 技术风险与注意事项
| 风险 | 等级 | 说明 | 缓解措施 |
|---|---|---|---|
| 通知叠放位置与拾取通知冲突 | 🟡中 | 收集通知需要与拾取通知协调屏幕位置,但两个系统独立管理各自的 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,不会发生;后续版本改为队列机制 |
注意事项
- 委托绑定时机:CollectionManager 在 BeginPlay() 中通过
GetOwner()->GetComponentByClass<UInteractionManager>()获取引用并绑定。需确保 InteractionManager 已初始化。若为 nullptr 则在 Tick 中延迟绑定(最多重试 N 帧后报错)。 - v0.1 无拾取通知 UI:执行文档中拾取通知由物品系统负责,但 v0.1 物品系统仅为委托层(InteractionManager 上的委托)。收集通知的 0.3s 延迟仍按设计实现(为拾取通知预留时间窗口),即使拾取通知 UI 暂未实现。
- 通知移除回调:UCollectionNotifWidget 淡出完成后需通知 CollectionManager 从 ActiveNotifications 中移除自身引用。使用委托或弱引用回调实现,避免循环引用。
- FName 作为 ID:收集系统使用 FName(ItemId)作为去重键,与 InteractionManager 委托广播的 ItemId 类型一致。
- TSet 线程安全:v0.1 单线程游戏,TSet 无需加锁。
9. 数据流
配置参数流
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/
│ ├── CollectionTypes.h # FCollectionConfig 结构体定义
│ ├── CollectionManager.h/.cpp # 收集系统核心组件
│ ├── CollectionNotifWidget.h/.cpp # 收集通知 UI 基类
│ ├── CollectionProgressWidget.h/.cpp # 收集进度 UI 基类
│ └── CollectionCompleteWidget.h/.cpp # 完成提示 UI 基类
├── ChildHoodCharacter.h/.cpp # 修改:添加 CollectionManager 成员和访问器
Content/
├── UI/
│ ├── 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 的委托绑定测试。