diff --git a/Config/DefaultPHYInput.ini b/Config/DefaultPHYInput.ini index b021401..e35487e 100644 --- a/Config/DefaultPHYInput.ini +++ b/Config/DefaultPHYInput.ini @@ -3,3 +3,4 @@ DefaultMappingPriority=0 bRouteInputThroughGenericInputSystem=True bBlockGameplayInputWhenUIFocused=True DefaultMoveInputProcessorClass="/Script/PHY.PHYInputProcessor_Move" +DefaultLookInputProcessorClass="/Script/PHY.PHYInputProcessor_Look" diff --git a/Source/PHY/Private/Input/PHYInputProcessor_Look.cpp b/Source/PHY/Private/Input/PHYInputProcessor_Look.cpp new file mode 100644 index 0000000..ba09c91 --- /dev/null +++ b/Source/PHY/Private/Input/PHYInputProcessor_Look.cpp @@ -0,0 +1,83 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "Input/PHYInputProcessor_Look.h" + +#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYInputProcessor_Look) + +#include "Characters/PHYPlayerCharacter.h" +#include "GIPS_InputSystemComponent.h" +#include "PHYGameplayTags.h" +#include "Player/PHYPlayerController.h" + +UPHYInputProcessor_Look::UPHYInputProcessor_Look() +{ + InputTags.AddTag(PHYGameplayTags::Input_Look); + TriggerEvents = { + ETriggerEvent::Started, + ETriggerEvent::Triggered, + ETriggerEvent::Ongoing, + ETriggerEvent::Completed, + ETriggerEvent::Canceled + }; +} + +bool UPHYInputProcessor_Look::CheckCanHandleInput_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag, const ETriggerEvent TriggerEvent) const +{ + (void)ActionData; + (void)TriggerEvent; + + if (!IC || InputTag != PHYGameplayTags::Input_Look) + { + return false; + } + + const APHYPlayerCharacter* PlayerCharacter = Cast(IC->GetControlledPawn()); + return PlayerCharacter && PlayerCharacter->GetController(); +} + +void UPHYInputProcessor_Look::HandleInputTriggered_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const +{ + (void)InputTag; + RouteLookInput(IC, ActionData, ETriggerEvent::Triggered); +} + +void UPHYInputProcessor_Look::HandleInputStarted_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const +{ + (void)InputTag; + RouteLookInput(IC, ActionData, ETriggerEvent::Started); +} + +void UPHYInputProcessor_Look::HandleInputOngoing_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const +{ + (void)InputTag; + RouteLookInput(IC, ActionData, ETriggerEvent::Ongoing); +} + +void UPHYInputProcessor_Look::HandleInputCanceled_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const +{ + (void)InputTag; + RouteLookInput(IC, ActionData, ETriggerEvent::Canceled); +} + +void UPHYInputProcessor_Look::HandleInputCompleted_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const +{ + (void)InputTag; + RouteLookInput(IC, ActionData, ETriggerEvent::Completed); +} + +FString UPHYInputProcessor_Look::GetEditorFriendlyName_Implementation() const +{ + return TEXT("PHY Look"); +} + +bool UPHYInputProcessor_Look::RouteLookInput(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->RouteLookInputFromProcessor(ActionData, TriggerEvent) : false; +} diff --git a/Source/PHY/Private/Player/PHYPlayerController.cpp b/Source/PHY/Private/Player/PHYPlayerController.cpp index 76b349a..38cd958 100644 --- a/Source/PHY/Private/Player/PHYPlayerController.cpp +++ b/Source/PHY/Private/Player/PHYPlayerController.cpp @@ -105,7 +105,10 @@ void APHYPlayerController::OnReceivedInput(const FInputActionInstance& ActionDat if (InputTag == PHYGameplayTags::Input_Look) { - HandleLookInput(ActionData, TriggerEvent); + if (!ConsumeLookProcessorGuard(TriggerEvent)) + { + RouteLookInput(ActionData, TriggerEvent); + } return; } @@ -127,6 +130,18 @@ bool APHYPlayerController::RouteMoveInputFromProcessor(const FInputActionInstanc return RouteMoveInput(ActionData, TriggerEvent); } +bool APHYPlayerController::RouteLookInput(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent) +{ + return HandleLookInput(ActionData, TriggerEvent); +} + +bool APHYPlayerController::RouteLookInputFromProcessor(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent) +{ + bLookInputHandledByProcessor = true; + LastLookInputProcessorTriggerEvent = TriggerEvent; + return RouteLookInput(ActionData, TriggerEvent); +} + bool APHYPlayerController::HandleMoveInput(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent) { APHYPlayerCharacter* PHYCharacter = GetPHYPlayerCharacter(); @@ -169,6 +184,20 @@ bool APHYPlayerController::ConsumeMoveProcessorGuard(const ETriggerEvent Trigger return false; } +bool APHYPlayerController::ConsumeLookProcessorGuard(const ETriggerEvent TriggerEvent) +{ + if (bLookInputHandledByProcessor && LastLookInputProcessorTriggerEvent == TriggerEvent) + { + bLookInputHandledByProcessor = false; + LastLookInputProcessorTriggerEvent = ETriggerEvent::None; + return true; + } + + bLookInputHandledByProcessor = false; + LastLookInputProcessorTriggerEvent = 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_Look.h b/Source/PHY/Public/Input/PHYInputProcessor_Look.h new file mode 100644 index 0000000..ab13a48 --- /dev/null +++ b/Source/PHY/Public/Input/PHYInputProcessor_Look.h @@ -0,0 +1,48 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "GIPS_InputProcessor.h" +#include "PHYInputProcessor_Look.generated.h" + +/** + * @brief PHY 视角输入处理器。 + * + * 该处理器处理 `Input.Look`,并把二维视角输入值路由到当前玩家控制器。 + */ +UCLASS(BlueprintType, Blueprintable, EditInlineNew, DefaultToInstanced) +class PHY_API UPHYInputProcessor_Look : public UGIPS_InputProcessor +{ + GENERATED_BODY() + +public: + /** @brief 构造默认响应的输入 Tag 和触发事件。 */ + UPHYInputProcessor_Look(); + +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 RouteLookInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, ETriggerEvent TriggerEvent) const; +}; diff --git a/Source/PHY/Public/PHYConfigSettings.h b/Source/PHY/Public/PHYConfigSettings.h index 1e97de2..69fd32f 100644 --- a/Source/PHY/Public/PHYConfigSettings.h +++ b/Source/PHY/Public/PHYConfigSettings.h @@ -59,6 +59,10 @@ public: /** @brief 默认移动输入处理器类,用于创建 GenericInputSystem 控制配置。 */ UPROPERTY(Config) TSoftClassPtr DefaultMoveInputProcessorClass; + + /** @brief 默认视角输入处理器类,用于创建 GenericInputSystem 控制配置。 */ + UPROPERTY(Config) + TSoftClassPtr DefaultLookInputProcessorClass; }; /** diff --git a/Source/PHY/Public/Player/PHYPlayerController.h b/Source/PHY/Public/Player/PHYPlayerController.h index 8fc7f81..755fbe2 100644 --- a/Source/PHY/Public/Player/PHYPlayerController.h +++ b/Source/PHY/Public/Player/PHYPlayerController.h @@ -56,6 +56,12 @@ public: /** @brief 从 GenericInputSystem 处理器路由移动输入,并阻止同一事件被广播 fallback 重复执行。 */ bool RouteMoveInputFromProcessor(const FInputActionInstance& ActionData, ETriggerEvent TriggerEvent); + /** @brief 从输入处理器或控制器 fallback 路由视角输入。 */ + bool RouteLookInput(const FInputActionInstance& ActionData, ETriggerEvent TriggerEvent); + + /** @brief 从 GenericInputSystem 处理器路由视角输入,并阻止同一事件被广播 fallback 重复执行。 */ + bool RouteLookInputFromProcessor(const FInputActionInstance& ActionData, ETriggerEvent TriggerEvent); + protected: /** @brief 绑定 GenericInputSystem 输入委托。 */ virtual void BindInputEvents(); @@ -73,6 +79,9 @@ protected: /** @brief 查询并消费移动处理器留下的重复执行保护。 */ virtual bool ConsumeMoveProcessorGuard(ETriggerEvent TriggerEvent); + /** @brief 查询并消费视角处理器留下的重复执行保护。 */ + virtual bool ConsumeLookProcessorGuard(ETriggerEvent TriggerEvent); + /** @brief GenericInputSystem 路由组件。 */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="PHY|Input") TObjectPtr InputSystemComponent; @@ -100,4 +109,12 @@ protected: /** @brief 最近一次由输入处理器处理的 Move 触发事件。 */ UPROPERTY(Transient) ETriggerEvent LastMoveInputProcessorTriggerEvent = ETriggerEvent::None; + + /** @brief 输入处理器是否已经处理了最近一次 Look 输入。 */ + UPROPERTY(Transient) + bool bLookInputHandledByProcessor = false; + + /** @brief 最近一次由输入处理器处理的 Look 触发事件。 */ + UPROPERTY(Transient) + ETriggerEvent LastLookInputProcessorTriggerEvent = ETriggerEvent::None; };