Files
ChildHood/Source/ChildHood/ChildHoodCharacter.cpp
T
2026-06-15 05:13:28 +08:00

271 lines
8.6 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 架构适配)
// 变更:通过 ASystemDriver 访问 FInteractionManager + FEffectManager
// 新增 BeginPlay 初始化 PromptComponent
// 删除对旧 UInteractionManager 的依赖
// Copyright Epic Games, Inc. All Rights Reserved.
#include "ChildHoodCharacter.h"
#include "Engine/LocalPlayer.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/Controller.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputActionValue.h"
#include "ChildHood.h"
#include "Interaction/InteractionPromptComponent.h"
#include "Collection/CollectionHUDComponent.h"
#include "SystemDriver.h"
#include "Interaction/InteractionManager.h"
#include "Interaction/EffectManager.h"
#include "Interaction/InteractableComponent.h"
#include "Interaction/InteractionTypes.h"
AChildHoodCharacter::AChildHoodCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
// Don't rotate when the controller rotates. Let that just affect the camera.
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
// Configure character movement — v0.1 player controller tech design parameters
auto* CMC = GetCharacterMovement();
CMC->bOrientRotationToMovement = true; // 角色朝移动方向旋转
CMC->RotationRate = FRotator(0.0f, 720.0f, 0.0f); // 转向速度 720°/s(远快于鼠标 150°/s)
CMC->MaxWalkSpeed = 450.0f; // 跑步速度 4.5 m/s
CMC->MaxAcceleration = 2250.0f; // 起步加速 0.2s (450/0.2)
CMC->BrakingDecelerationWalking = 1500.0f; // 停止减速 0.3s (450/0.3)
CMC->BrakingDecelerationFalling = 1500.0f;
CMC->MinAnalogWalkSpeed = 20.f;
// Create a camera boom (pulls in towards the player if there is a collision)
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent);
CameraBoom->TargetArmLength = 350.0f; // 摄像机距离 3.5m
CameraBoom->SocketOffset = FVector(0.f, 0.f, 80.f); // 摄像机高度 0.8m(头部上方)
CameraBoom->bUsePawnControlRotation = true;
// 摄像机软跟随
CameraBoom->bEnableCameraLag = true;
CameraBoom->CameraLagSpeed = 6.0f;
CameraBoom->bEnableCameraRotationLag = false;
// 摄像机碰撞处理
CameraBoom->bDoCollisionTest = true;
CameraBoom->ProbeSize = 12.0f;
CameraBoom->ProbeChannel = ECC_Camera;
// Create a follow camera
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
FollowCamera->bUsePawnControlRotation = false;
// 创建交互提示组件(替代旧 InteractionManager)
PromptComponent = CreateDefaultSubobject<UInteractionPromptComponent>(TEXT("PromptComponent"));
// 创建收集系统 HUD 组件(表现层,挂在角色上;Tick 兜底查找 Driver)
CollectionHUDComponent = CreateDefaultSubobject<UCollectionHUDComponent>(TEXT("CollectionHUDComponent"));
}
void AChildHoodCharacter::BeginPlay()
{
Super::BeginPlay();
// 初始化 PromptComponent — 将其与 ASystemDriver 关联
if (PromptComponent)
{
if (ASystemDriver* Driver = ASystemDriver::Get(this))
{
PromptComponent->Initialize(Driver);
}
}
// 初始化收集系统 HUD 组件(Driver 弱引用;Tick 会兜底查找,规避 BeginPlay 时序竞争)
if (CollectionHUDComponent)
{
if (ASystemDriver* Driver = ASystemDriver::Get(this))
{
CollectionHUDComponent->Initialize(Driver);
}
}
}
void AChildHoodCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
// Set up action bindings
if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent)) {
// Moving
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AChildHoodCharacter::Move);
// Looking (gamepad + mouse)
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AChildHoodCharacter::Look);
EnhancedInputComponent->BindAction(MouseLookAction, ETriggerEvent::Triggered, this, &AChildHoodCharacter::Look);
// Interaction — delegates to ASystemDriver
if (InteractAction)
{
EnhancedInputComponent->BindAction(InteractAction, ETriggerEvent::Started, this, &AChildHoodCharacter::OnInteract);
}
if (CancelAction)
{
EnhancedInputComponent->BindAction(CancelAction, ETriggerEvent::Started, this, &AChildHoodCharacter::OnCancel);
}
if (SwitchInteractAction)
{
EnhancedInputComponent->BindAction(SwitchInteractAction, ETriggerEvent::Started, this, &AChildHoodCharacter::OnSwitchInteract);
}
}
else
{
UE_LOG(LogChildHood, Error, TEXT("'%s' Failed to find an Enhanced Input component! This template is built to use the Enhanced Input system. If you intend to use the legacy system, then you will need to update this C++ file."), *GetNameSafe(this));
}
}
void AChildHoodCharacter::Move(const FInputActionValue& Value)
{
// input is a Vector2D
FVector2D MovementVector = Value.Get<FVector2D>();
// route the input
DoMove(MovementVector.X, MovementVector.Y);
}
void AChildHoodCharacter::Look(const FInputActionValue& Value)
{
// input is a Vector2D
FVector2D LookAxisVector = Value.Get<FVector2D>();
// route the input
DoLook(LookAxisVector.X, LookAxisVector.Y);
}
void AChildHoodCharacter::OnInteract(const FInputActionValue& Value)
{
ASystemDriver* Driver = ASystemDriver::Get(this);
if (!Driver) return;
auto& Mgr = Driver->GetInteractionManager();
auto& EFM = Driver->GetEffectManager();
// E 键三重功能:
// 1. Free 状态:发起交互
// 2. WaitForInput 暂停态:恢复 Effect(观察/对话类按 E 继续)
// 3. 可取消 Effect 执行中:取消 Effect
if (Mgr.GetState() == EInteractionState::Free)
{
// 发起交互
auto* Selected = Mgr.BeginInteract();
if (!Selected) return;
SetInteracting(true);
FEffectContext Ctx;
Ctx.Instigator = this;
Ctx.TargetComp = Selected;
Ctx.TargetActor = Selected->GetOwner();
EFM.PlayEffect(Selected->GetInteractableData().InteractEffect, Ctx);
}
else
{
// Executing 状态 → 尝试取消/恢复
EFM.CancelEffect();
}
}
void AChildHoodCharacter::OnCancel(const FInputActionValue& Value)
{
ASystemDriver* Driver = ASystemDriver::Get(this);
if (!Driver) return;
auto& EFM = Driver->GetEffectManager();
EFM.CancelEffect();
}
void AChildHoodCharacter::OnSwitchInteract(const FInputActionValue& Value)
{
ASystemDriver* Driver = ASystemDriver::Get(this);
if (!Driver) return;
auto& Mgr = Driver->GetInteractionManager();
// 交互中不可切换
if (Mgr.GetState() != EInteractionState::Free) return;
float DirectionValue = Value.Get<float>();
int32 Direction = (DirectionValue >= 0.f) ? 1 : -1;
Mgr.SwitchTarget(Direction);
}
void AChildHoodCharacter::DoMove(float Right, float Forward)
{
// 交互中屏蔽移动
if (bIsInteracting)
{
return;
}
if (GetController() != nullptr)
{
// find out which way is forward
const FRotator Rotation = GetController()->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// get forward vector
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
// get right vector
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// add movement
AddMovementInput(ForwardDirection, Forward);
AddMovementInput(RightDirection, Right);
}
}
void AChildHoodCharacter::DoLook(float Yaw, float Pitch)
{
if (GetController() != nullptr)
{
// add yaw and pitch input to controller
AddControllerYawInput(Yaw);
AddControllerPitchInput(Pitch);
}
}
// ============================================================================
// 交互系统方法
// ============================================================================
UInteractionPromptComponent* AChildHoodCharacter::GetPromptComponent() const
{
return PromptComponent;
}
UCollectionHUDComponent* AChildHoodCharacter::GetCollectionHUDComponent() const
{
return CollectionHUDComponent;
}
void AChildHoodCharacter::SetInteracting(bool bValue)
{
bIsInteracting = bValue;
}
bool AChildHoodCharacter::IsInteracting() const
{
return bIsInteracting;
}
bool AChildHoodCharacter::IsRunning() const
{
return GetVelocity().SizeSquared() > 100.f // 速度 > 10 cm/s 阈值
&& GetCharacterMovement()->IsMovingOnGround();
}