feat(player-controller): 实现v0.1角色控制系统

- 移除跳跃逻辑:删除JumpAction、DoJumpStart/End
- 调整CMC参数:MaxWalkSpeed=450, Acceleration=2250, Brake=1500, RotationRate=720
- 调整SpringArm:TargetArmLength=350, SocketOffset.Z=80, CameraLag=6, ProbeSize=12
- 添加Pitch限幅:ViewPitchMin=-30, ViewPitchMax=45, 初始俯角-15°
- 新增交互状态:bIsInteracting + SetInteracting/IsInteracting/IsRunning
- 新增交互IA:InteractAction/CancelAction/SwitchInteractAction(占位回调)
- DoMove添加交互中忽略移动输入检查

Task: P-011
This commit is contained in:
2026-06-10 17:39:37 +08:00
parent b8f27221b2
commit 3c69445d90
3 changed files with 123 additions and 44 deletions
+74 -31
View File
@@ -16,55 +16,72 @@ AChildHoodCharacter::AChildHoodCharacter()
{ {
// Set size for collision capsule // Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f); GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
// Don't rotate when the controller rotates. Let that just affect the camera. // Don't rotate when the controller rotates. Let that just affect the camera.
bUseControllerRotationPitch = false; bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false; bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false; bUseControllerRotationRoll = false;
// Configure character movement // Configure character movement — v0.1 player controller tech design parameters
GetCharacterMovement()->bOrientRotationToMovement = true; auto* CMC = GetCharacterMovement();
GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f); CMC->bOrientRotationToMovement = true; // 角色朝移动方向旋转
CMC->RotationRate = FRotator(0.0f, 720.0f, 0.0f); // 转向速度 720°/s(远快于鼠标 150°/s)
// Note: For faster iteration times these variables, and many more, can be tweaked in the Character Blueprint CMC->MaxWalkSpeed = 450.0f; // 跑步速度 4.5 m/s
// instead of recompiling to adjust them CMC->MaxAcceleration = 2250.0f; // 起步加速 0.2s (450/0.2)
GetCharacterMovement()->JumpZVelocity = 500.f; CMC->BrakingDecelerationWalking = 1500.0f; // 停止减速 0.3s (450/0.3)
GetCharacterMovement()->AirControl = 0.35f; CMC->BrakingDecelerationFalling = 1500.0f;
GetCharacterMovement()->MaxWalkSpeed = 500.f; CMC->MinAnalogWalkSpeed = 20.f;
GetCharacterMovement()->MinAnalogWalkSpeed = 20.f; // WalkableFloorAngle 默认 45°,无需修改
GetCharacterMovement()->BrakingDecelerationWalking = 2000.f; // v0.1 不跳跃,不设置 JumpZVelocity / AirControl
GetCharacterMovement()->BrakingDecelerationFalling = 1500.0f;
// Create a camera boom (pulls in towards the player if there is a collision) // Create a camera boom (pulls in towards the player if there is a collision)
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom")); CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent); CameraBoom->SetupAttachment(RootComponent);
CameraBoom->TargetArmLength = 400.0f; CameraBoom->TargetArmLength = 350.0f; // 摄像机距离 3.5m
CameraBoom->SocketOffset = FVector(0.f, 0.f, 80.f); // 摄像机高度 0.8m(头部上方)
CameraBoom->bUsePawnControlRotation = true; CameraBoom->bUsePawnControlRotation = true;
// 摄像机软跟随
CameraBoom->bEnableCameraLag = true; // 启用位置延迟跟随
CameraBoom->CameraLagSpeed = 6.0f; // 跟随速度(可调 2-8)
CameraBoom->bEnableCameraRotationLag = false; // v0.1 不启用旋转延迟
// 摄像机碰撞处理
CameraBoom->bDoCollisionTest = true; // 启用碰撞检测
CameraBoom->ProbeSize = 12.0f; // 碰撞探针半径
CameraBoom->ProbeChannel = ECC_Camera; // 使用 Camera 碰撞通道
// Create a follow camera // Create a follow camera
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera")); FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
FollowCamera->bUsePawnControlRotation = false; FollowCamera->bUsePawnControlRotation = false;
// Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)
// are set in the derived blueprint asset named ThirdPersonCharacter (to avoid direct content references in C++)
} }
void AChildHoodCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) void AChildHoodCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{ {
// Set up action bindings // Set up action bindings
if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent)) { if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent)) {
// Jumping
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
// Moving // Moving
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AChildHoodCharacter::Move); 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); EnhancedInputComponent->BindAction(MouseLookAction, ETriggerEvent::Triggered, this, &AChildHoodCharacter::Look);
// Looking // Interaction IAs — bind to placeholder callbacks; interaction system will hook into these
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AChildHoodCharacter::Look); 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 else
{ {
@@ -90,8 +107,30 @@ void AChildHoodCharacter::Look(const FInputActionValue& Value)
DoLook(LookAxisVector.X, LookAxisVector.Y); DoLook(LookAxisVector.X, LookAxisVector.Y);
} }
void AChildHoodCharacter::OnInteract(const FInputActionValue& Value)
{
// v0.1 placeholder — interaction system will provide implementation
// Interaction system hooks in via blueprint or future C++ delegate
}
void AChildHoodCharacter::OnCancel(const FInputActionValue& Value)
{
// v0.1 placeholder — interaction system will provide implementation
}
void AChildHoodCharacter::OnSwitchInteract(const FInputActionValue& Value)
{
// v0.1 placeholder — interaction system will provide implementation
}
void AChildHoodCharacter::DoMove(float Right, float Forward) void AChildHoodCharacter::DoMove(float Right, float Forward)
{ {
// P1: 交互中忽略移动输入
if (bIsInteracting)
{
return;
}
if (GetController() != nullptr) if (GetController() != nullptr)
{ {
// find out which way is forward // find out which way is forward
@@ -101,10 +140,10 @@ void AChildHoodCharacter::DoMove(float Right, float Forward)
// get forward vector // get forward vector
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X); const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
// get right vector // get right vector
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y); const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// add movement // add movement
AddMovementInput(ForwardDirection, Forward); AddMovementInput(ForwardDirection, Forward);
AddMovementInput(RightDirection, Right); AddMovementInput(RightDirection, Right);
} }
@@ -120,14 +159,18 @@ void AChildHoodCharacter::DoLook(float Yaw, float Pitch)
} }
} }
void AChildHoodCharacter::DoJumpStart() void AChildHoodCharacter::SetInteracting(bool bValue)
{ {
// signal the character to jump bIsInteracting = bValue;
Jump();
} }
void AChildHoodCharacter::DoJumpEnd() bool AChildHoodCharacter::IsInteracting() const
{ {
// signal the character to stop jumping return bIsInteracting;
StopJumping(); }
bool AChildHoodCharacter::IsRunning() const
{
return GetVelocity().SizeSquared() > 100.f // 速度 > 10 cm/s 阈值
&& GetCharacterMovement()->IsMovingOnGround();
} }
+36 -13
View File
@@ -30,12 +30,8 @@ class AChildHoodCharacter : public ACharacter
/** Follow camera */ /** Follow camera */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true")) UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
UCameraComponent* FollowCamera; UCameraComponent* FollowCamera;
protected:
/** Jump Input Action */ protected:
UPROPERTY(EditAnywhere, Category="Input")
UInputAction* JumpAction;
/** Move Input Action */ /** Move Input Action */
UPROPERTY(EditAnywhere, Category="Input") UPROPERTY(EditAnywhere, Category="Input")
@@ -49,10 +45,25 @@ protected:
UPROPERTY(EditAnywhere, Category="Input") UPROPERTY(EditAnywhere, Category="Input")
UInputAction* MouseLookAction; UInputAction* MouseLookAction;
/** Interact Input Action (E key) — delegates to interaction system */
UPROPERTY(EditAnywhere, Category="Input")
UInputAction* InteractAction;
/** Cancel Input Action (ESC key) — delegates to interaction system */
UPROPERTY(EditAnywhere, Category="Input")
UInputAction* CancelAction;
/** Switch Interact Target Input Action (Tab / Mouse Wheel) — delegates to interaction system */
UPROPERTY(EditAnywhere, Category="Input")
UInputAction* SwitchInteractAction;
/** Whether the character is currently in an interaction */
bool bIsInteracting = false;
public: public:
/** Constructor */ /** Constructor */
AChildHoodCharacter(); AChildHoodCharacter();
protected: protected:
@@ -67,6 +78,15 @@ protected:
/** Called for looking input */ /** Called for looking input */
void Look(const FInputActionValue& Value); void Look(const FInputActionValue& Value);
/** Called for interact input (E) — placeholder, delegates to interaction system */
void OnInteract(const FInputActionValue& Value);
/** Called for cancel input (ESC) — placeholder, delegates to interaction system */
void OnCancel(const FInputActionValue& Value);
/** Called for switch interact target input (Tab/Wheel) — placeholder, delegates to interaction system */
void OnSwitchInteract(const FInputActionValue& Value);
public: public:
/** Handles move inputs from either controls or UI interfaces */ /** Handles move inputs from either controls or UI interfaces */
@@ -77,13 +97,17 @@ public:
UFUNCTION(BlueprintCallable, Category="Input") UFUNCTION(BlueprintCallable, Category="Input")
virtual void DoLook(float Yaw, float Pitch); virtual void DoLook(float Yaw, float Pitch);
/** Handles jump pressed inputs from either controls or UI interfaces */ /** Set interaction state — called by interaction system */
UFUNCTION(BlueprintCallable, Category="Input") UFUNCTION(BlueprintCallable, Category="Interaction")
virtual void DoJumpStart(); void SetInteracting(bool bValue);
/** Handles jump pressed inputs from either controls or UI interfaces */ /** Query: is the character currently interacting? */
UFUNCTION(BlueprintCallable, Category="Input") UFUNCTION(BlueprintCallable, BlueprintPure, Category="Interaction")
virtual void DoJumpEnd(); bool IsInteracting() const;
/** Query: is the character currently running (moving on ground)? */
UFUNCTION(BlueprintCallable, BlueprintPure, Category="Interaction")
bool IsRunning() const;
public: public:
@@ -93,4 +117,3 @@ public:
/** Returns FollowCamera subobject **/ /** Returns FollowCamera subobject **/
FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; } FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
}; };
@@ -13,6 +13,19 @@ void AChildHoodPlayerController::BeginPlay()
{ {
Super::BeginPlay(); Super::BeginPlay();
// C3: 设置摄像机俯仰角限制(垂直旋转 +45°/-30°)
if (PlayerCameraManager)
{
PlayerCameraManager->ViewPitchMin = -30.0f; // 向下看最多 30°
PlayerCameraManager->ViewPitchMax = 45.0f; // 向上看最多 45°
}
// C1: 初始俯角 -15°(摄像机默认略向下看向角色)
if (GetControlRotation().Pitch == 0.f)
{
SetControlRotation(FRotator(-15.0f, GetControlRotation().Yaw, GetControlRotation().Roll));
}
// only spawn touch controls on local player controllers // only spawn touch controls on local player controllers
if (ShouldUseTouchControls() && IsLocalPlayerController()) if (ShouldUseTouchControls() && IsLocalPlayerController())
{ {