From 281db9a3750b6b7a8b3ddc4fe1163abd211c6556 Mon Sep 17 00:00:00 2001 From: cit110 <840418418@qq.com> Date: Sun, 26 Apr 2026 00:49:21 +0800 Subject: [PATCH] Add move input processor --- Config/DefaultPHYInput.ini | 1 + .../Private/Input/PHYInputProcessor_Move.cpp | 83 +++++++++++++++++++ .../Private/Player/PHYPlayerController.cpp | 31 ++++++- .../PHY/Public/Input/PHYInputProcessor_Move.h | 48 +++++++++++ Source/PHY/Public/PHYConfigSettings.h | 7 ++ .../PHY/Public/Player/PHYPlayerController.h | 17 ++++ 6 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 Source/PHY/Private/Input/PHYInputProcessor_Move.cpp create mode 100644 Source/PHY/Public/Input/PHYInputProcessor_Move.h diff --git a/Config/DefaultPHYInput.ini b/Config/DefaultPHYInput.ini index 35b426e..b021401 100644 --- a/Config/DefaultPHYInput.ini +++ b/Config/DefaultPHYInput.ini @@ -2,3 +2,4 @@ DefaultMappingPriority=0 bRouteInputThroughGenericInputSystem=True bBlockGameplayInputWhenUIFocused=True +DefaultMoveInputProcessorClass="/Script/PHY.PHYInputProcessor_Move" diff --git a/Source/PHY/Private/Input/PHYInputProcessor_Move.cpp b/Source/PHY/Private/Input/PHYInputProcessor_Move.cpp new file mode 100644 index 0000000..ae3aa87 --- /dev/null +++ b/Source/PHY/Private/Input/PHYInputProcessor_Move.cpp @@ -0,0 +1,83 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "Input/PHYInputProcessor_Move.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYInputProcessor_Move) + +#include "Characters/PHYPlayerCharacter.h" +#include "GIPS_InputSystemComponent.h" +#include "PHYGameplayTags.h" +#include "Player/PHYPlayerController.h" + +UPHYInputProcessor_Move::UPHYInputProcessor_Move() +{ + InputTags.AddTag(PHYGameplayTags::Input_Move); + TriggerEvents = { + ETriggerEvent::Started, + ETriggerEvent::Triggered, + ETriggerEvent::Ongoing, + ETriggerEvent::Completed, + ETriggerEvent::Canceled + }; +} + +bool UPHYInputProcessor_Move::CheckCanHandleInput_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag, const ETriggerEvent TriggerEvent) const +{ + (void)ActionData; + (void)TriggerEvent; + + if (!IC || InputTag != PHYGameplayTags::Input_Move) + { + return false; + } + + const APHYPlayerCharacter* PlayerCharacter = Cast(IC->GetControlledPawn()); + return PlayerCharacter && PlayerCharacter->GetController(); +} + +void UPHYInputProcessor_Move::HandleInputTriggered_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const +{ + (void)InputTag; + RouteMoveInput(IC, ActionData, ETriggerEvent::Triggered); +} + +void UPHYInputProcessor_Move::HandleInputStarted_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const +{ + (void)InputTag; + RouteMoveInput(IC, ActionData, ETriggerEvent::Started); +} + +void UPHYInputProcessor_Move::HandleInputOngoing_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const +{ + (void)InputTag; + RouteMoveInput(IC, ActionData, ETriggerEvent::Ongoing); +} + +void UPHYInputProcessor_Move::HandleInputCanceled_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const +{ + (void)InputTag; + RouteMoveInput(IC, ActionData, ETriggerEvent::Canceled); +} + +void UPHYInputProcessor_Move::HandleInputCompleted_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const +{ + (void)InputTag; + RouteMoveInput(IC, ActionData, ETriggerEvent::Completed); +} + +FString UPHYInputProcessor_Move::GetEditorFriendlyName_Implementation() const +{ + return TEXT("PHY Move"); +} + +bool UPHYInputProcessor_Move::RouteMoveInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent) const +{ + if (!IC) + { + return false; + } + + APHYPlayerCharacter* PlayerCharacter = Cast(IC->GetControlledPawn()); + APHYPlayerController* PlayerController = PlayerCharacter ? PlayerCharacter->GetController() : nullptr; + return PlayerController ? PlayerController->RouteMoveInputFromProcessor(ActionData, TriggerEvent) : false; +} diff --git a/Source/PHY/Private/Player/PHYPlayerController.cpp b/Source/PHY/Private/Player/PHYPlayerController.cpp index d9c5e0d..76b349a 100644 --- a/Source/PHY/Private/Player/PHYPlayerController.cpp +++ b/Source/PHY/Private/Player/PHYPlayerController.cpp @@ -96,7 +96,10 @@ void APHYPlayerController::OnReceivedInput(const FInputActionInstance& ActionDat { if (InputTag == PHYGameplayTags::Input_Move) { - HandleMoveInput(ActionData, TriggerEvent); + if (!ConsumeMoveProcessorGuard(TriggerEvent)) + { + RouteMoveInput(ActionData, TriggerEvent); + } return; } @@ -112,6 +115,18 @@ void APHYPlayerController::OnReceivedInput(const FInputActionInstance& ActionDat } } +bool APHYPlayerController::RouteMoveInput(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent) +{ + return HandleMoveInput(ActionData, TriggerEvent); +} + +bool APHYPlayerController::RouteMoveInputFromProcessor(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent) +{ + bMoveInputHandledByProcessor = true; + LastMoveInputProcessorTriggerEvent = TriggerEvent; + return RouteMoveInput(ActionData, TriggerEvent); +} + bool APHYPlayerController::HandleMoveInput(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent) { APHYPlayerCharacter* PHYCharacter = GetPHYPlayerCharacter(); @@ -140,6 +155,20 @@ bool APHYPlayerController::HandleMoveInput(const FInputActionInstance& ActionDat return true; } +bool APHYPlayerController::ConsumeMoveProcessorGuard(const ETriggerEvent TriggerEvent) +{ + if (bMoveInputHandledByProcessor && LastMoveInputProcessorTriggerEvent == TriggerEvent) + { + bMoveInputHandledByProcessor = false; + LastMoveInputProcessorTriggerEvent = ETriggerEvent::None; + return true; + } + + bMoveInputHandledByProcessor = false; + LastMoveInputProcessorTriggerEvent = ETriggerEvent::None; + return false; +} + bool APHYPlayerController::HandleLookInput(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent) { CachedLookInput = IsInputReleased(TriggerEvent) ? FVector2D::ZeroVector : ExtractAxis2D(ActionData.GetValue()); diff --git a/Source/PHY/Public/Input/PHYInputProcessor_Move.h b/Source/PHY/Public/Input/PHYInputProcessor_Move.h new file mode 100644 index 0000000..ba58e65 --- /dev/null +++ b/Source/PHY/Public/Input/PHYInputProcessor_Move.h @@ -0,0 +1,48 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "GIPS_InputProcessor.h" +#include "PHYInputProcessor_Move.generated.h" + +/** + * @brief PHY 移动输入处理器。 + * + * 该处理器处理 `Input.Move`,并把二维输入值路由到当前玩家控制器和玩家角色。 + */ +UCLASS(BlueprintType, Blueprintable, EditInlineNew, DefaultToInstanced) +class PHY_API UPHYInputProcessor_Move : public UGIPS_InputProcessor +{ + GENERATED_BODY() + +public: + /** @brief 构造默认响应的输入 Tag 和触发事件。 */ + UPHYInputProcessor_Move(); + +protected: + /** @brief 检查当前输入组件是否能处理移动输入。 */ + virtual bool CheckCanHandleInput_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag, ETriggerEvent TriggerEvent) const override; + + /** @brief 处理 Triggered 移动输入。 */ + virtual void HandleInputTriggered_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const override; + + /** @brief 处理 Started 移动输入。 */ + virtual void HandleInputStarted_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const override; + + /** @brief 处理 Ongoing 移动输入。 */ + virtual void HandleInputOngoing_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const override; + + /** @brief 处理 Canceled 移动输入。 */ + virtual void HandleInputCanceled_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const override; + + /** @brief 处理 Completed 移动输入。 */ + virtual void HandleInputCompleted_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const override; + + /** @brief 返回编辑器里显示的处理器名称。 */ + virtual FString GetEditorFriendlyName_Implementation() const override; + +private: + /** @brief 将移动输入发送给玩家控制器。 */ + bool RouteMoveInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, ETriggerEvent TriggerEvent) const; +}; diff --git a/Source/PHY/Public/PHYConfigSettings.h b/Source/PHY/Public/PHYConfigSettings.h index d1fb199..1e97de2 100644 --- a/Source/PHY/Public/PHYConfigSettings.h +++ b/Source/PHY/Public/PHYConfigSettings.h @@ -4,8 +4,11 @@ #include "CoreMinimal.h" #include "UObject/Object.h" +#include "UObject/SoftObjectPtr.h" #include "PHYConfigSettings.generated.h" +class UGIPS_InputProcessor; + /** * @brief PHY 项目核心配置。 */ @@ -52,6 +55,10 @@ public: /** @brief UI 聚焦时是否阻断 Gameplay 输入。 */ UPROPERTY(Config) bool bBlockGameplayInputWhenUIFocused = true; + + /** @brief 默认移动输入处理器类,用于创建 GenericInputSystem 控制配置。 */ + UPROPERTY(Config) + TSoftClassPtr DefaultMoveInputProcessorClass; }; /** diff --git a/Source/PHY/Public/Player/PHYPlayerController.h b/Source/PHY/Public/Player/PHYPlayerController.h index 53df992..8fc7f81 100644 --- a/Source/PHY/Public/Player/PHYPlayerController.h +++ b/Source/PHY/Public/Player/PHYPlayerController.h @@ -50,6 +50,12 @@ public: UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Input") FVector GetCachedMovementControlInput() const { return FVector(CachedMovementInput.X, CachedMovementInput.Y, 0.0f); } + /** @brief 从输入处理器或控制器 fallback 路由移动输入。 */ + bool RouteMoveInput(const FInputActionInstance& ActionData, ETriggerEvent TriggerEvent); + + /** @brief 从 GenericInputSystem 处理器路由移动输入,并阻止同一事件被广播 fallback 重复执行。 */ + bool RouteMoveInputFromProcessor(const FInputActionInstance& ActionData, ETriggerEvent TriggerEvent); + protected: /** @brief 绑定 GenericInputSystem 输入委托。 */ virtual void BindInputEvents(); @@ -64,6 +70,9 @@ protected: /** @brief 处理视角输入。 */ virtual bool HandleLookInput(const FInputActionInstance& ActionData, ETriggerEvent TriggerEvent); + /** @brief 查询并消费移动处理器留下的重复执行保护。 */ + virtual bool ConsumeMoveProcessorGuard(ETriggerEvent TriggerEvent); + /** @brief GenericInputSystem 路由组件。 */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|Input") TObjectPtr InputSystemComponent; @@ -83,4 +92,12 @@ protected: /** @brief UGC 相机管理器读取的旋转输入缓存。 */ UPROPERTY(Transient) FRotator CachedRotationInput = FRotator::ZeroRotator; + + /** @brief 输入处理器是否已经处理了最近一次 Move 输入。 */ + UPROPERTY(Transient) + bool bMoveInputHandledByProcessor = false; + + /** @brief 最近一次由输入处理器处理的 Move 触发事件。 */ + UPROPERTY(Transient) + ETriggerEvent LastMoveInputProcessorTriggerEvent = ETriggerEvent::None; };