第一次提交

This commit is contained in:
不明不惑
2026-03-03 01:23:02 +08:00
commit 3e434877e8
1053 changed files with 102411 additions and 0 deletions

View File

@@ -0,0 +1,546 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "GIPS_InputSystemComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/Pawn.h"
#include "GameFramework/PlayerController.h"
#include "Engine/LocalPlayer.h"
#include "GIPS_LogChannels.h"
#include "GIPS_InputConfig.h"
#include "GIPS_InputControlSetup.h"
#include "GIPS_InputFunctionLibrary.h"
#include "Engine/World.h"
#include "Misc/DataValidation.h"
UGIPS_InputSystemComponent::UGIPS_InputSystemComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
}
void UGIPS_InputSystemComponent::OnRegister()
{
Super::OnRegister();
const UWorld* World = GetWorld();
if (World->IsGameWorld())
{
APlayerController* PCOwner = GetOwner<APlayerController>();
APawn* PawnOwner = GetOwner<APawn>();
OwnerType = PCOwner ? EGIPS_OwnerType::PC : EGIPS_OwnerType::Pawn;
if (OwnerType == EGIPS_OwnerType::Pawn)
{
if (ensure(PawnOwner))
{
PawnOwner->ReceiveRestartedDelegate.AddDynamic(this, &UGIPS_InputSystemComponent::OnPawnRestarted);
PawnOwner->ReceiveControllerChangedDelegate.AddDynamic(this, &UGIPS_InputSystemComponent::OnControllerChanged);
// If our pawn has an input component we were added after restart
if (PawnOwner->InputComponent)
{
OnPawnRestarted(PawnOwner);
}
}
}
if (OwnerType == EGIPS_OwnerType::PC)
{
if (ensure(PCOwner))
{
// TODO 支持放到PC上。
}
}
}
}
void UGIPS_InputSystemComponent::OnUnregister()
{
const UWorld* World = GetWorld();
if (World && World->IsGameWorld())
{
CleanupInputComponent();
if (OwnerType == EGIPS_OwnerType::Pawn)
{
APawn* PawnOwner = GetOwner<APawn>();
PawnOwner->ReceiveRestartedDelegate.RemoveAll(this);
PawnOwner->ReceiveControllerChangedDelegate.RemoveAll(this);
}
if (OwnerType == EGIPS_OwnerType::PC)
{
APlayerController* PCOwner = GetOwner<APlayerController>();
}
}
Super::OnUnregister();
}
APawn* UGIPS_InputSystemComponent::GetControlledPawn() const
{
if (OwnerType == EGIPS_OwnerType::Pawn)
{
return GetOwner<APawn>();
}
if (OwnerType == EGIPS_OwnerType::PC)
{
APlayerController* PC = GetOwner<APlayerController>();
return PC ? PC->GetPawn() : nullptr;
}
return nullptr;
}
UGIPS_InputSystemComponent* UGIPS_InputSystemComponent::GetInputSystemComponent(const AActor* Actor)
{
return IsValid(Actor) ? Actor->FindComponentByClass<UGIPS_InputSystemComponent>() : nullptr;
}
bool UGIPS_InputSystemComponent::FindInputSystemComponent(const AActor* Actor, UGIPS_InputSystemComponent*& Component)
{
Component = GetInputSystemComponent(Actor);
return Component != nullptr;
}
void UGIPS_InputSystemComponent::OnSetupPlayerInputComponent_Implementation(UEnhancedInputComponent* NewInputComponent)
{
BindInputActions();
}
void UGIPS_InputSystemComponent::OnCleanupPlayerInputComponent_Implementation(UEnhancedInputComponent* PrevInputComponent)
{
}
void UGIPS_InputSystemComponent::OnPawnRestarted(APawn* Pawn)
{
if (ensure(Pawn && Pawn == GetOwner()) && Pawn->InputComponent)
{
GIPS_CLOG(Verbose, "cleanup and setup input for Pawn: %s", Pawn ? *Pawn->GetName() : TEXT("NONE"))
CleanupInputComponent();
if (Pawn->InputComponent)
{
SetupInputComponent(Pawn->InputComponent);
}
}
}
void UGIPS_InputSystemComponent::OnControllerChanged(APawn* Pawn, AController* OldController, AController* NewController)
{
// Only handle releasing, restart is a better time to handle binding
if (ensure(Pawn && Pawn == GetOwner()) && OldController)
{
GIPS_CLOG(Verbose, "cleanup input component due to controller change. %s", Pawn ? *Pawn->GetName() : TEXT("NONE"))
CleanupInputComponent(OldController);
}
}
void UGIPS_InputSystemComponent::CleanInputActionValueBindings()
{
for (auto& Binding : InputActionValueBindings)
{
InputComponent->RemoveActionValueBinding(Binding.Value);
GIPS_CLOG(Verbose, "Clean input action value binding for InputTag:{%s}", *Binding.Key.ToString());
}
InputActionValueBindings.Empty();
}
void UGIPS_InputSystemComponent::SetupInputActionValueBindings()
{
check(InputConfig);
for (auto& Mapping : InputConfig->InputActionMappings)
{
if (Mapping.Value.bValueBinding)
{
FEnhancedInputActionValueBinding& Binding = InputComponent->BindActionValue(Mapping.Value.InputAction);
int32 BindingIndex = InputComponent->GetActionValueBindings().Find(Binding);
InputActionValueBindings.Emplace(Mapping.Key, BindingIndex);
GIPS_CLOG(Verbose, "Setup input action value binding for InputTag:{%s} ad index:{%d}", *Mapping.Key.ToString(), BindingIndex);
}
}
}
void UGIPS_InputSystemComponent::SetupInputComponent(UInputComponent* NewInputComponent)
{
InputComponent = Cast<UEnhancedInputComponent>(NewInputComponent);
if (ensureMsgf(InputComponent, TEXT("Project must use EnhancedInputComponent to support PlayerControlsComponent")))
{
UEnhancedInputLocalPlayerSubsystem* Subsystem = GetEnhancedInputSubsystem();
if (Subsystem && InputMappingContext)
{
Subsystem->AddMappingContext(InputMappingContext, InputPriority);
}
CleanInputActionValueBindings();
SetupInputActionValueBindings();
GIPS_CLOG(Verbose, "Setup for Pawn/PC: %s", GetOwner() ? *GetOwner()->GetName() : TEXT("NONE"))
OnSetupPlayerInputComponent(InputComponent);
SetupInputComponentEvent.Broadcast(InputComponent);
}
}
void UGIPS_InputSystemComponent::CleanupInputComponent(AController* OldController)
{
UEnhancedInputLocalPlayerSubsystem* Subsystem = GetEnhancedInputSubsystem(OldController);
if (Subsystem && InputComponent)
{
OnCleanupPlayerInputComponent(InputComponent);
CleanupInputComponentEvent.Broadcast(InputComponent);
if (InputMappingContext)
{
Subsystem->RemoveMappingContext(InputMappingContext);
}
CleanInputActionValueBindings();
}
InputComponent = nullptr;
}
UEnhancedInputLocalPlayerSubsystem* UGIPS_InputSystemComponent::GetEnhancedInputSubsystem(AController* OldController) const
{
if (OwnerType == EGIPS_OwnerType::Pawn && !GetOwner<APawn>())
{
return nullptr;
}
const APawn* PawnOwner = GetOwner<APawn>();
const APlayerController* PC = PawnOwner ? PawnOwner->GetController<APlayerController>() : GetOwner<APlayerController>();
if (!PC)
{
PC = Cast<APlayerController>(OldController);
if (!PC)
{
return nullptr;
}
}
const ULocalPlayer* LP = PC->GetLocalPlayer();
if (!LP)
{
return nullptr;
}
return LP->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
}
void UGIPS_InputSystemComponent::BindInputActions()
{
check(InputConfig);
for (auto& Pair : InputConfig->InputActionMappings)
{
// Generic binding.
InputComponent->BindAction(Pair.Value.InputAction, ETriggerEvent::Triggered, this, &ThisClass::InputActionCallback, Pair.Key, ETriggerEvent::Triggered);
InputComponent->BindAction(Pair.Value.InputAction, ETriggerEvent::Started, this, &ThisClass::InputActionCallback, Pair.Key, ETriggerEvent::Started);
InputComponent->BindAction(Pair.Value.InputAction, ETriggerEvent::Ongoing, this, &ThisClass::InputActionCallback, Pair.Key, ETriggerEvent::Ongoing);
InputComponent->BindAction(Pair.Value.InputAction, ETriggerEvent::Completed, this, &ThisClass::InputActionCallback, Pair.Key, ETriggerEvent::Completed);
InputComponent->BindAction(Pair.Value.InputAction, ETriggerEvent::Canceled, this, &ThisClass::InputActionCallback, Pair.Key, ETriggerEvent::Canceled);
}
}
UGIPS_InputControlSetup* UGIPS_InputSystemComponent::GetCurrentInputSetup() const
{
if (InputControlSetups.IsValidIndex(InputControlSetups.Num() - 1))
{
return InputControlSetups[InputControlSetups.Num() - 1];
}
return nullptr;
}
UGIPS_InputConfig* UGIPS_InputSystemComponent::GetInputConfig() const
{
return InputConfig;
}
void UGIPS_InputSystemComponent::PushInputSetup(UGIPS_InputControlSetup* NewSetup)
{
if (!InputControlSetups.Contains(NewSetup))
{
InputControlSetups.Push(NewSetup);
}
}
void UGIPS_InputSystemComponent::PopInputSetup()
{
if (InputControlSetups.Num() > 1)
{
InputControlSetups.Pop();
}
}
bool UGIPS_InputSystemComponent::CheckInputAllowed(FGameplayTag InputTag, ETriggerEvent TriggerEvent)
{
FInputActionInstance ActionData;
return CheckInputAllowed(ActionData, InputTag, TriggerEvent);
}
bool UGIPS_InputSystemComponent::CheckInputAllowed(const FInputActionInstance& ActionData, FGameplayTag InputTag, ETriggerEvent TriggerEvent)
{
if (UGIPS_InputControlSetup* Setup = GetCurrentInputSetup())
{
return Setup->CheckInput(this, ActionData, InputTag, TriggerEvent);
}
return true;
}
void UGIPS_InputSystemComponent::InputActionCallback(const FInputActionInstance& ActionData, FGameplayTag InputTag, ETriggerEvent TriggerEvent)
{
if (InputTag.IsValid())
{
GIPS_CLOG(VeryVerbose, "Input(%s) triggered with event(%s) and value(%s)", *UGIPS_InputFunctionLibrary::GetLastTagName(InputTag).ToString(), *InputTag.ToString(),
*UGIPS_InputFunctionLibrary::GetTriggerEventString(TriggerEvent), *ActionData.GetValue().ToString());
if (!bProcessingInputExternally && CheckInputAllowed(ActionData, InputTag, TriggerEvent))
{
ProcessInput(ActionData, InputTag, TriggerEvent);
}
LastInputActionValues.Emplace(InputTag, ActionData.GetValue());
OnReceivedInput.Broadcast(ActionData, InputTag, TriggerEvent);
}
}
void UGIPS_InputSystemComponent::ProcessInput(const FInputActionInstance& ActionData, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent)
{
if (UGIPS_InputControlSetup* Setup = GetCurrentInputSetup())
{
Setup->HandleInput(this, ActionData, InputTag, TriggerEvent);
}
}
UInputAction* UGIPS_InputSystemComponent::GetInputActionOfInputTag(FGameplayTag InputTag) const
{
if (InputTag.IsValid() && InputConfig->InputActionMappings.Contains(InputTag))
return InputConfig->InputActionMappings[InputTag].InputAction;
return nullptr;
}
FInputActionValue UGIPS_InputSystemComponent::GetInputActionValueOfInputTag(FGameplayTag InputTag) const
{
if (InputComponent)
{
if (UInputAction* IA = GetInputActionOfInputTag(InputTag))
{
return InputComponent->GetBoundActionValue(IA);
}
}
return FInputActionValue();
}
FInputActionValue UGIPS_InputSystemComponent::GetLastInputActionValueOfInputTag(FGameplayTag InputTag) const
{
if (InputTag.IsValid() && LastInputActionValues.Contains(InputTag))
{
return LastInputActionValues[InputTag];
}
return FInputActionValue();
}
void UGIPS_InputSystemComponent::RegisterPassedInputEntry(const FGIPS_BufferedInput& InputEntry)
{
if (PassedInputEntries.Num() >= MaxInputEntriesNum)
{
PassedInputEntries.RemoveAtSwap(0);
}
PassedInputEntries.Add(InputEntry);
}
void UGIPS_InputSystemComponent::RegisterBlockedInputEntry(const FGIPS_BufferedInput& InputEntry)
{
if (BlockedInputEntries.Num() >= MaxInputEntriesNum)
{
BlockedInputEntries.RemoveAtSwap(0);
}
BlockedInputEntries.Add(InputEntry);
}
void UGIPS_InputSystemComponent::RegisterBufferedInputEntry(const FGIPS_BufferedInput& InputEntry)
{
if (BufferedInputEntries.Num() >= MaxInputEntriesNum)
{
BufferedInputEntries.RemoveAtSwap(0);
}
BufferedInputEntries.Add(InputEntry);
}
#pragma region InputBuffer
bool UGIPS_InputSystemComponent::TrySaveInput(const FInputActionInstance& ActionData, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent)
{
if (ActiveBufferWindows.IsEmpty())
{
// No any buffer window.
return false;
}
TArray<FGameplayTag> ActiveBufferWindowNames;
ActiveBufferWindows.GetKeys(ActiveBufferWindowNames);
// To see if any active buffer window can accept this input.
int32 Counter{0};
for (FGameplayTag& ActiveBufferWindowName : ActiveBufferWindowNames)
{
if (TrySaveAsBufferedInput(ActiveBufferWindowName, ActionData, InputTag, TriggerEvent))
{
Counter++;
}
}
return Counter > 0;
}
void UGIPS_InputSystemComponent::FireBufferedInput()
{
ProcessInput(CurrentBufferedInput.ActionData, CurrentBufferedInput.InputTag, CurrentBufferedInput.TriggerEvent);
OnFireBufferedInput.Broadcast(CurrentBufferedInput.ActionData, CurrentBufferedInput.InputTag, CurrentBufferedInput.TriggerEvent);
ResetBufferedInput();
CloseActiveInputBufferWindows();
}
void UGIPS_InputSystemComponent::OpenInputBufferWindow(FGameplayTag BufferWindowName)
{
if (!BufferWindowName.IsValid())
{
GIPS_CLOG(Warning, "invalid buffer name.");
return;
}
if (ActiveBufferWindows.Contains(BufferWindowName))
{
GIPS_CLOG(Warning, "Can't Open buffer window(%s) as it already active!", *BufferWindowName.ToString());
return;
}
if (!ActiveBufferWindows.Contains(BufferWindowName))
{
if (const FGIPS_InputBufferWindow* Window = InputConfig->InputBufferDefinitions.FindByKey(BufferWindowName))
{
ActiveBufferWindows.FindOrAdd(BufferWindowName);
GIPS_CLOG(Verbose, "Open buffer window:%s", *BufferWindowName.ToString());
InputBufferWindowStateChangedEvent.Broadcast(BufferWindowName, true);
}
}
}
void UGIPS_InputSystemComponent::CloseInputBufferWindow(FGameplayTag BufferWindowName)
{
if (ActiveBufferWindows.Contains(BufferWindowName))
{
CurrentBufferedInput = ActiveBufferWindows[BufferWindowName];
if (CurrentBufferedInput.InputTag.IsValid())
{
GIPS_CLOG(Verbose, "Fire buffered input(:%s,TriggerEvent:%s) from Window(%s)", *CurrentBufferedInput.InputTag.ToString(),
*UGIPS_InputFunctionLibrary::GetTriggerEventString(CurrentBufferedInput.TriggerEvent), *BufferWindowName.ToString());
FireBufferedInput();
}
ActiveBufferWindows.Remove(BufferWindowName);
GIPS_CLOG(Verbose, "Close buffer window:%s", *BufferWindowName.ToString());
InputBufferWindowStateChangedEvent.Broadcast(BufferWindowName, false);
}
}
void UGIPS_InputSystemComponent::CloseActiveInputBufferWindows()
{
ActiveBufferWindows.Empty();
}
FGIPS_BufferedInput UGIPS_InputSystemComponent::GetLastBufferedInput() const
{
return LastBufferedInput;
}
TMap<FGameplayTag, FGIPS_BufferedInput> UGIPS_InputSystemComponent::GetActiveBufferWindows() const
{
return ActiveBufferWindows;
}
void UGIPS_InputSystemComponent::ResetBufferedInput()
{
LastBufferedInput = CurrentBufferedInput;
CurrentBufferedInput = FGIPS_BufferedInput();
}
bool UGIPS_InputSystemComponent::TrySaveAsBufferedInput(const FGameplayTag BufferWindowName, const FInputActionInstance& ActionData, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent)
{
if (!ActiveBufferWindows.Contains(BufferWindowName))
return false;
FGIPS_BufferedInput& BufferedInput = ActiveBufferWindows[BufferWindowName];
const FGIPS_InputBufferWindow* Definition = InputConfig->InputBufferDefinitions.FindByKey(BufferWindowName);
if (Definition == nullptr)
return false;
const int32 AllowedInputIndex = Definition->IndexOfAllowedInput(InputTag, TriggerEvent);
if (AllowedInputIndex == INDEX_NONE)
return false;
// Instance buffering.
if (Definition->BufferType == EGIPS_InputBufferType::Instant)
{
BufferedInput.InputTag = InputTag;
BufferedInput.ActionData = ActionData;
BufferedInput.TriggerEvent = TriggerEvent;
CurrentBufferedInput = BufferedInput;
GIPS_CLOG(Verbose, "Instantly fire buffered input(%s,TriggerEvent:%s) from Window(%s)", *InputTag.ToString(), *UGIPS_InputFunctionLibrary::GetTriggerEventString(TriggerEvent),
*BufferWindowName.ToString());
FireBufferedInput();
ActiveBufferWindows.Remove(BufferWindowName);
return true;
}
if (BufferedInput.InputTag.IsValid() && Definition->BufferType == EGIPS_InputBufferType::HighestPriority)
{
const int32 ExistingInputIndex = Definition->IndexOfAllowedInput(BufferedInput.InputTag, BufferedInput.TriggerEvent);
if (ExistingInputIndex != INDEX_NONE && AllowedInputIndex < ExistingInputIndex)
{
GIPS_CLOG(Verbose, "Record new buffered input(%s,TriggerEvent:%s) in Window(%s),Before was input(%s,TriggerEvent:%s)", *InputTag.ToString(),
*UGIPS_InputFunctionLibrary::GetTriggerEventString(TriggerEvent), *BufferWindowName.ToString(),
*BufferedInput.InputTag.ToString(),
*UGIPS_InputFunctionLibrary::GetTriggerEventString(BufferedInput.TriggerEvent));
BufferedInput.InputTag = InputTag;
BufferedInput.ActionData = ActionData;
BufferedInput.TriggerEvent = TriggerEvent;
return true;
}
}
GIPS_CLOG(Verbose, "Record buffered input(%s,TriggerEvent:%s) in Window(%s)", *InputTag.ToString(),
*UGIPS_InputFunctionLibrary::GetTriggerEventString(TriggerEvent), *BufferWindowName.ToString());
BufferedInput.InputTag = InputTag;
BufferedInput.ActionData = ActionData;
BufferedInput.TriggerEvent = TriggerEvent;
return true;
}
#pragma endregion
#pragma region DataValidation
#if WITH_EDITOR
EDataValidationResult UGIPS_InputSystemComponent::IsDataValid(FDataValidationContext& Context) const
{
if (!InputConfig)
{
Context.AddError(FText::FromString(TEXT("InputConfig is required.")));
return EDataValidationResult::Invalid;
}
if (InputControlSetups.IsEmpty())
{
Context.AddError(FText::FromString(TEXT("At least one InputControlSetup is required.")));
return EDataValidationResult::Invalid;
}
return Super::IsDataValid(Context);
}
#endif
#pragma endregion