docs(technical): 收集系统技术方案 v1.0:Effect/Action 解耦架构修订
This commit is contained in:
@@ -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 路径
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user