Files
ChildHood/Source/ChildHood/Interaction/InteractableComponent.cpp
T
starmen1265 237885e3b8 fix(interaction): 修复编译错误和交互系统集成问题
- SystemDriver: EffectManager改为值持有,去掉TUniquePtr和条件编译
- SystemDriver: 补充EngineUtils.h include修复TActorIterator编译错误
- GameMode: BeginPlay自动Spawn ASystemDriver
- InteractableActor: 改为abstract基类,子类自行创建交互组件
- DoorActor: 创建DoorInteractableComp替代基类组件
- EffectManager: 实现ExecuteToggleDoor调用实际门开关逻辑
- InteractableComponent/DoorComponent: 修复GetGameInstance路径和调用方式
- EffectManager: Broadcast改为Execute(单播委托)
2026-06-12 00:51:00 +08:00

196 lines
6.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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->MarkItemCollected(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->IsItemCollected(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);
}
}