docs(technical): 收集系统技术方案 v1.0:Effect/Action 解耦架构修订

This commit is contained in:
2026-06-11 23:08:27 +08:00
parent 8a75e682c5
commit 690cfeb29d
12 changed files with 1463 additions and 790 deletions
@@ -1,15 +1,18 @@
// ChildHood Project - 可交互物组件 实现
// ChildHood Project - 可交互物组件 实现(v1.1 Effect/Action 架构)
#include "Interaction/InteractableComponent.h"
#include "Interaction/InteractableActor.h"
#include "Interaction/InteractionManager.h"
#include "ChildHoodGameInstance.h"
#include "Interaction/PersistenceSubsystem.h"
#include "ChildHood.h"
#include "Components/SphereComponent.h"
#include "Engine/World.h"
#include "GameFramework/GameInstance.h"
#include "GameFramework/Character.h"
#include "Kismet/GameplayStatics.h"
// 前向声明对应的头文件在.cpp中include
// ASystemDriver 在 Phase 2 创建,此处前向声明即可
class ASystemDriver;
// ============================================================================
// 构造函数
@@ -36,7 +39,7 @@ void UInteractableComponent::BeginPlay()
return;
}
// --- 创建检测球 ---
// --- 创建检测球(BeginPlay 中动态创建,因为需要 Data.TriggerRadius)---
DetectionSphere = NewObject<USphereComponent>(this, USphereComponent::StaticClass(), TEXT("DetectionSphere"));
if (DetectionSphere)
{
@@ -58,14 +61,14 @@ void UInteractableComponent::BeginPlay()
}
// --- 恢复跨场景持久状态 ---
if (UChildHoodGameInstance* GI = GetWorld()->GetGameInstance<UChildHoodGameInstance>())
if (UPersistenceSubsystem* Persistence = GetGameInstance()->GetSubsystem<UPersistenceSubsystem>())
{
RestorePersistentState(GI);
RestorePersistentState(Persistence);
}
UE_LOG(LogChildHood, Log, TEXT("InteractableComponent Init: %s (Type=%s, Radius=%f)"),
UE_LOG(LogChildHood, Log, TEXT("InteractableComponent Init: %s (ItemId=%s, Radius=%.0f)"),
Data.DisplayName.IsEmpty() ? TEXT("(unnamed)") : *Data.DisplayName,
*UEnum::GetValueAsString(Data.Type),
*Data.ItemId.ToString(),
Data.TriggerRadius);
}
@@ -73,22 +76,16 @@ void UInteractableComponent::BeginPlay()
// 提示文本
// ============================================================================
FString UInteractableComponent::GetPromptText() const
FString UInteractableComponent::GetPromptText_Implementation() const
{
FString Verb = Data.InteractVerb;
// 门类型:根据当前开关状态选择动词
if (Data.Type == EInteractType::Operate && Data.OperateSubType == EOperateSubType::Door)
{
Verb = bDoorIsOpen ? Data.InteractVerbAlt : Data.InteractVerb;
}
return FString::Printf(TEXT("[E] %s %s"), *Verb, *Data.DisplayName);
return FString::Printf(TEXT("[E] %s %s"), *Data.InteractVerb, *Data.DisplayName);
}
// ============================================================================
// 消耗管理
// ============================================================================
void UInteractableComponent::Consume()
void UInteractableComponent::Consume_Implementation()
{
bIsConsumed = true;
@@ -101,27 +98,15 @@ void UInteractableComponent::Consume()
// 写入持久层
if (UWorld* World = GetWorld())
{
if (UChildHoodGameInstance* GI = World->GetGameInstance<UChildHoodGameInstance>())
if (UPersistenceSubsystem* Persistence = World->GetGameInstance()->GetSubsystem<UPersistenceSubsystem>())
{
GI->MarkItemCollected(Data.ItemId);
Persistence->MarkItemCollected(Data.ItemId);
}
}
UE_LOG(LogChildHood, Log, TEXT("InteractableComponent 已消耗: %s"), *Data.ItemId.ToString());
}
// ============================================================================
// 门状态管理
// ============================================================================
void UInteractableComponent::ToggleDoor()
{
bDoorIsOpen = !bDoorIsOpen;
UE_LOG(LogChildHood, Log, TEXT("InteractableComponent 门状态切换: %s → %s"),
*Data.ItemId.ToString(),
bDoorIsOpen ? TEXT("开") : TEXT("关"));
}
// ============================================================================
// 位置查询
// ============================================================================
@@ -136,38 +121,24 @@ FVector UInteractableComponent::GetWorldLocation() const
// 跨场景持久状态恢复
// ============================================================================
void UInteractableComponent::RestorePersistentState(UChildHoodGameInstance* GI)
void UInteractableComponent::RestorePersistentState(UPersistenceSubsystem* Persistence)
{
if (!GI) return;
if (!Persistence) return;
// oneShot 物品:已消耗 → 隐藏 + 禁用检测
if (Data.bOneShot && GI->IsItemCollected(Data.ItemId))
if (Data.bOneShot && Persistence->IsItemCollected(Data.ItemId))
{
bIsConsumed = true;
if (DetectionSphere)
{
DetectionSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
if (AInteractableActor* Owner = Cast<AInteractableActor>(GetOwner()))
if (AActor* Owner = GetOwner())
{
Owner->SetActorHiddenInGame(true);
}
UE_LOG(LogChildHood, Log, TEXT("RestorePersistentState: 已消耗物品隐藏: %s"), *Data.ItemId.ToString());
}
// 门:恢复开关状态
if (Data.Type == EInteractType::Operate && Data.OperateSubType == EOperateSubType::Door)
{
bDoorIsOpen = GI->IsDoorOpen(Data.ItemId);
// 通知 Actor 应用视觉状态(Mesh 旋转)
if (AInteractableActor* Owner = Cast<AInteractableActor>(GetOwner()))
{
Owner->OnInteractStarted(this);
}
UE_LOG(LogChildHood, Log, TEXT("RestorePersistentState: 门状态恢复: %s → %s"),
*Data.ItemId.ToString(),
bDoorIsOpen ? TEXT("开") : TEXT("关"));
}
}
// ============================================================================
@@ -193,11 +164,9 @@ void UInteractableComponent::OnDetectionOverlapBegin(UPrimitiveComponent* Overla
UE_LOG(LogChildHood, Verbose, TEXT("检测到角色进入范围: %s ← %s"),
*Data.DisplayName, *OtherActor->GetName());
// 获取角色的 InteractionManager 并注册自己
if (UInteractionManager* IM = Character->FindComponentByClass<UInteractionManager>())
{
IM->RegisterInteractable(this);
}
// Phase 2 将改为通过 ASystemDriver::Get(GetWorld())->GetInteractionManager().RegisterInteractable(this)
// 当前 Phase 1 仅记录日志,因为 FInteractionManager 尚未创建
// TODO(Phase 2): 替换为 ASystemDriver 路径
}
void UInteractableComponent::OnDetectionOverlapEnd(UPrimitiveComponent* OverlappedComponent,
@@ -213,9 +182,7 @@ void UInteractableComponent::OnDetectionOverlapEnd(UPrimitiveComponent* Overlapp
UE_LOG(LogChildHood, Verbose, TEXT("角色离开范围: %s ← %s"),
*Data.DisplayName, *OtherActor->GetName());
// 获取角色的 InteractionManager 并注销自己
if (UInteractionManager* IM = Character->FindComponentByClass<UInteractionManager>())
{
IM->UnregisterInteractable(this);
}
// Phase 2 将改为通过 ASystemDriver::Get(GetWorld())->GetInteractionManager().UnregisterInteractable(this)
// 当前 Phase 1 仅记录日志,因为 FInteractionManager 尚未创建
// TODO(Phase 2): 替换为 ASystemDriver 路径
}