196 lines
6.3 KiB
C++
196 lines
6.3 KiB
C++
// ChildHood Project - 可交互物组件 实现(v1.1 Effect/Action 架构)
|
||
|
||
#include "Interaction/InteractableComponent.h"
|
||
#include "Interaction/InteractableActor.h"
|
||
#include "Interaction/PersistenceSubsystem.h"
|
||
#include "SystemDriver.h"
|
||
#include "Interaction/InteractionManager.h"
|
||
#include "ChildHood.h"
|
||
|
||
#include "Components/SphereComponent.h"
|
||
#include "Engine/World.h"
|
||
#include "Engine/GameInstance.h"
|
||
#include "GameFramework/Character.h"
|
||
|
||
// ============================================================================
|
||
// 构造函数
|
||
// ============================================================================
|
||
|
||
UInteractableComponent::UInteractableComponent()
|
||
{
|
||
// 不需要 Tick,检测由碰撞事件驱动
|
||
PrimaryComponentTick.bCanEverTick = false;
|
||
}
|
||
|
||
// ============================================================================
|
||
// 生命周期
|
||
// ============================================================================
|
||
|
||
void UInteractableComponent::BeginPlay()
|
||
{
|
||
Super::BeginPlay();
|
||
|
||
AActor* Owner = GetOwner();
|
||
if (!Owner)
|
||
{
|
||
UE_LOG(LogChildHood, Warning, TEXT("InteractableComponent: 无 Owner,跳过初始化"));
|
||
return;
|
||
}
|
||
|
||
// --- 创建检测球(BeginPlay 中动态创建,因为需要 Data.TriggerRadius)---
|
||
DetectionSphere = NewObject<USphereComponent>(this, USphereComponent::StaticClass(), TEXT("DetectionSphere"));
|
||
if (DetectionSphere)
|
||
{
|
||
DetectionSphere->SetupAttachment(Owner->GetRootComponent());
|
||
DetectionSphere->RegisterComponent();
|
||
DetectionSphere->SetSphereRadius(Data.TriggerRadius);
|
||
DetectionSphere->SetCollisionProfileName(TEXT("OverlapAllDynamic"));
|
||
// 关闭物理模拟,仅用于 Overlap 检测
|
||
DetectionSphere->SetGenerateOverlapEvents(true);
|
||
DetectionSphere->SetCanEverAffectNavigation(false);
|
||
|
||
// 绑定碰撞事件
|
||
DetectionSphere->OnComponentBeginOverlap.AddDynamic(this, &UInteractableComponent::OnDetectionOverlapBegin);
|
||
DetectionSphere->OnComponentEndOverlap.AddDynamic(this, &UInteractableComponent::OnDetectionOverlapEnd);
|
||
}
|
||
else
|
||
{
|
||
UE_LOG(LogChildHood, Error, TEXT("InteractableComponent: DetectionSphere 创建失败"));
|
||
}
|
||
|
||
// --- 恢复跨场景持久状态 ---
|
||
UGameInstance* GI = Owner->GetGameInstance();
|
||
if (UPersistenceSubsystem* Persistence = GI ? GI->GetSubsystem<UPersistenceSubsystem>() : nullptr)
|
||
{
|
||
RestorePersistentState(Persistence);
|
||
}
|
||
|
||
UE_LOG(LogChildHood, Log, TEXT("InteractableComponent Init: %s (ItemId=%s, Radius=%.0f)"),
|
||
Data.DisplayName.IsEmpty() ? TEXT("(unnamed)") : *Data.DisplayName,
|
||
*Data.ItemId.ToString(),
|
||
Data.TriggerRadius);
|
||
}
|
||
|
||
// ============================================================================
|
||
// 提示文本
|
||
// ============================================================================
|
||
|
||
FString UInteractableComponent::GetPromptText_Implementation() const
|
||
{
|
||
return FString::Printf(TEXT("[E] %s %s"), *Data.InteractVerb, *Data.DisplayName);
|
||
}
|
||
|
||
// ============================================================================
|
||
// 消耗管理
|
||
// ============================================================================
|
||
|
||
void UInteractableComponent::Consume_Implementation()
|
||
{
|
||
bIsConsumed = true;
|
||
|
||
// 禁用检测球,不再触发 Overlap
|
||
if (DetectionSphere)
|
||
{
|
||
DetectionSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||
}
|
||
|
||
// 写入持久层
|
||
if (UGameInstance* GI = GetOwner() ? GetOwner()->GetGameInstance() : nullptr)
|
||
{
|
||
if (UPersistenceSubsystem* Persistence = GI->GetSubsystem<UPersistenceSubsystem>())
|
||
{
|
||
Persistence->MarkInteractableConsumed(Data.ItemId);
|
||
}
|
||
}
|
||
|
||
UE_LOG(LogChildHood, Log, TEXT("InteractableComponent 已消耗: %s"), *Data.ItemId.ToString());
|
||
}
|
||
|
||
// ============================================================================
|
||
// 位置查询
|
||
// ============================================================================
|
||
|
||
FVector UInteractableComponent::GetWorldLocation() const
|
||
{
|
||
AActor* Owner = GetOwner();
|
||
return Owner ? Owner->GetActorLocation() : FVector::ZeroVector;
|
||
}
|
||
|
||
// ============================================================================
|
||
// 跨场景持久状态恢复
|
||
// ============================================================================
|
||
|
||
void UInteractableComponent::RestorePersistentState(UPersistenceSubsystem* Persistence)
|
||
{
|
||
if (!Persistence) return;
|
||
|
||
// oneShot 物品:已消耗 → 隐藏 + 禁用检测
|
||
if (Data.bOneShot && Persistence->IsInteractableConsumed(Data.ItemId))
|
||
{
|
||
bIsConsumed = true;
|
||
if (DetectionSphere)
|
||
{
|
||
DetectionSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||
}
|
||
if (AActor* Owner = GetOwner())
|
||
{
|
||
Owner->SetActorHiddenInGame(true);
|
||
}
|
||
UE_LOG(LogChildHood, Log, TEXT("RestorePersistentState: 已消耗物品隐藏: %s"), *Data.ItemId.ToString());
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 碰撞回调
|
||
// ============================================================================
|
||
|
||
void UInteractableComponent::OnDetectionOverlapBegin(UPrimitiveComponent* OverlappedComponent,
|
||
AActor* OtherActor,
|
||
UPrimitiveComponent* OtherComp,
|
||
int32 OtherBodyIndex,
|
||
bool bFromSweep,
|
||
const FHitResult& SweepResult)
|
||
{
|
||
if (!OtherActor) return;
|
||
|
||
// 只响应 Character(玩家)的进入
|
||
ACharacter* Character = Cast<ACharacter>(OtherActor);
|
||
if (!Character) return;
|
||
|
||
// 已消耗的交互物不再注册
|
||
if (bIsConsumed) return;
|
||
|
||
UE_LOG(LogChildHood, Verbose, TEXT("检测到角色进入范围: %s ← %s"),
|
||
*Data.DisplayName, *OtherActor->GetName());
|
||
|
||
// 通过 ASystemDriver 注册到 InteractionManager
|
||
if (ASystemDriver* Driver = ASystemDriver::Get(this))
|
||
{
|
||
Driver->GetInteractionManager().RegisterInteractable(this);
|
||
}
|
||
else
|
||
{
|
||
UE_LOG(LogChildHood, Warning, TEXT("InteractableComponent: ASystemDriver 不存在,无法注册 %s"), *Data.DisplayName);
|
||
}
|
||
}
|
||
|
||
void UInteractableComponent::OnDetectionOverlapEnd(UPrimitiveComponent* OverlappedComponent,
|
||
AActor* OtherActor,
|
||
UPrimitiveComponent* OtherComp,
|
||
int32 OtherBodyIndex)
|
||
{
|
||
if (!OtherActor) return;
|
||
|
||
ACharacter* Character = Cast<ACharacter>(OtherActor);
|
||
if (!Character) return;
|
||
|
||
UE_LOG(LogChildHood, Verbose, TEXT("角色离开范围: %s ← %s"),
|
||
*Data.DisplayName, *OtherActor->GetName());
|
||
|
||
// 通过 ASystemDriver 从 InteractionManager 注销
|
||
if (ASystemDriver* Driver = ASystemDriver::Get(this))
|
||
{
|
||
Driver->GetInteractionManager().UnregisterInteractable(this);
|
||
}
|
||
}
|