第一次提交
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class GenericInputSystem : ModuleRules
|
||||
{
|
||||
public GenericInputSystem(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"EnhancedInput",
|
||||
"GameplayTags"
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"InputCore",
|
||||
"GameplayDebugger"
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Actions/GIPS_AsyncAction_ListenInputEvent.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "GIPS_InputSystemComponent.h"
|
||||
|
||||
UGIPS_AsyncAction_ListenInputEvent* UGIPS_AsyncAction_ListenInputEvent::ListenInputEvent(UObject* WorldContextObject, UGIPS_InputSystemComponent* InputSystemComponent,
|
||||
FGameplayTagContainer InputTagsToListen, TArray<ETriggerEvent> EventsToListen, bool bListenForBufferedInput,
|
||||
bool bExactMatch)
|
||||
{
|
||||
if (!IsValid(InputSystemComponent))
|
||||
{
|
||||
FFrame::KismetExecutionMessage(TEXT("ListenInputEvent was passed a null InputSystemComponent"), ELogVerbosity::Error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull);
|
||||
|
||||
UGIPS_AsyncAction_ListenInputEvent* Action = NewObject<UGIPS_AsyncAction_ListenInputEvent>();
|
||||
Action->Input = InputSystemComponent;
|
||||
Action->InputTags = InputTagsToListen;
|
||||
Action->bForBufferedInput = bListenForBufferedInput;
|
||||
Action->TriggerEvents = EventsToListen;
|
||||
Action->RegisterWithGameInstance(World);
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
void UGIPS_AsyncAction_ListenInputEvent::Activate()
|
||||
{
|
||||
if (bForBufferedInput)
|
||||
{
|
||||
Input->OnFireBufferedInput.AddDynamic(this, &ThisClass::HandleInput);
|
||||
}
|
||||
else
|
||||
{
|
||||
Input->OnReceivedInput.AddDynamic(this, &ThisClass::HandleInput);
|
||||
}
|
||||
}
|
||||
|
||||
void UGIPS_AsyncAction_ListenInputEvent::HandleInput(const FInputActionInstance& ActionData, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent)
|
||||
{
|
||||
if (bExact ? InputTags.HasTagExact(InputTag) : InputTags.HasTag(InputTag))
|
||||
{
|
||||
if (TriggerEvents.Contains(TriggerEvent))
|
||||
{
|
||||
OnReceivedInput.Broadcast(ActionData, InputTag, TriggerEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGIPS_AsyncAction_ListenInputEvent::Cancel()
|
||||
{
|
||||
if (Input.IsValid())
|
||||
{
|
||||
if (bForBufferedInput)
|
||||
{
|
||||
if (Input->OnFireBufferedInput.IsAlreadyBound(this, &ThisClass::HandleInput))
|
||||
{
|
||||
Input->OnFireBufferedInput.RemoveDynamic(this, &ThisClass::HandleInput);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Input->OnFireBufferedInput.IsAlreadyBound(this, &ThisClass::HandleInput))
|
||||
{
|
||||
Input->OnFireBufferedInput.RemoveDynamic(this, &ThisClass::HandleInput);
|
||||
}
|
||||
}
|
||||
Super::Cancel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "GIPS_GameplayDebugger.h"
|
||||
|
||||
#if WITH_GAMEPLAY_DEBUGGER
|
||||
#include "GIPS_InputConfig.h"
|
||||
#include "GIPS_InputControlSetup.h"
|
||||
#include "GIPS_InputFunctionLibrary.h"
|
||||
#include "GIPS_InputSystemComponent.h"
|
||||
#include "Engine/Canvas.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
|
||||
|
||||
FGIPS_GameplayDebuggerCategory_Input::FGIPS_GameplayDebuggerCategory_Input()
|
||||
{
|
||||
SetDataPackReplication(&DataPack);
|
||||
const FName KeyNameOne{"One"};
|
||||
const FName KeyNameTwo{"Two"};
|
||||
const FName KeyNameThree{"Three"};
|
||||
const FName KeyNameFour{"Four"};
|
||||
|
||||
BindKeyPress(KeyNameOne, FGameplayDebuggerInputModifier::Shift, this, &FGIPS_GameplayDebuggerCategory_Input::OnShowInputBuffersToggle, EGameplayDebuggerInputMode::Local);
|
||||
BindKeyPress(KeyNameTwo, FGameplayDebuggerInputModifier::Shift, this, &FGIPS_GameplayDebuggerCategory_Input::OnShowPassedInputEntriesToggle, EGameplayDebuggerInputMode::Local);
|
||||
BindKeyPress(KeyNameThree, FGameplayDebuggerInputModifier::Shift, this, &FGIPS_GameplayDebuggerCategory_Input::OnShowBlockedInputEntriesToggle, EGameplayDebuggerInputMode::Local);
|
||||
BindKeyPress(KeyNameFour, FGameplayDebuggerInputModifier::Shift, this, &FGIPS_GameplayDebuggerCategory_Input::OnShowBufferedInputEntriesToggle, EGameplayDebuggerInputMode::Local);
|
||||
}
|
||||
|
||||
void FGIPS_GameplayDebuggerCategory_Input::CollectData(APlayerController* OwnerPC, AActor* DebugActor)
|
||||
{
|
||||
if (const UGIPS_InputSystemComponent* InputSystem = UGIPS_InputSystemComponent::GetInputSystemComponent(DebugActor))
|
||||
{
|
||||
if (UGIPS_InputControlSetup* InputControlSetup = InputSystem->GetCurrentInputSetup())
|
||||
{
|
||||
if (UGIPS_InputConfig* InputConfig = InputSystem->GetInputConfig())
|
||||
{
|
||||
DataPack.ActorName = OwnerPC->GetPawn()->GetName();
|
||||
DataPack.InputConfig = InputConfig->GetName();
|
||||
DataPack.InputControlSetup = InputControlSetup->GetName();
|
||||
|
||||
TMap<FGameplayTag, FGIPS_BufferedInput> ActiveWindows = InputSystem->GetActiveBufferWindows();
|
||||
|
||||
for (const FGIPS_InputBufferWindow& Definition : InputConfig->InputBufferDefinitions)
|
||||
{
|
||||
FRepData::FInputBuffersDebug ItemData;
|
||||
|
||||
ItemData.WindowName = UGIPS_InputFunctionLibrary::GetLastTagName(Definition.Tag);
|
||||
ItemData.bIsActive = ActiveWindows.Contains(Definition.Tag);
|
||||
ItemData.InputTagName = ItemData.bIsActive && ActiveWindows[Definition.Tag].InputTag.IsValid()
|
||||
? UGIPS_InputFunctionLibrary::GetLastTagName(ActiveWindows[Definition.Tag].InputTag)
|
||||
: NAME_None;
|
||||
ItemData.InputEvent = ItemData.bIsActive ? ActiveWindows[Definition.Tag].TriggerEvent : ETriggerEvent::None;
|
||||
|
||||
DataPack.InputBuffers.Add(ItemData);
|
||||
}
|
||||
FGIPS_BufferedInput Input = InputSystem->GetLastBufferedInput();
|
||||
DataPack.BufferedInputTag = Input.InputTag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FGIPS_GameplayDebuggerCategory_Input::DrawData(APlayerController* OwnerPC, FGameplayDebuggerCanvasContext& CanvasContext)
|
||||
{
|
||||
// Draw the sub-category bindings inline with the category header
|
||||
{
|
||||
CanvasContext.CursorX += 200.0f;
|
||||
CanvasContext.CursorY -= CanvasContext.GetLineHeight();
|
||||
const TCHAR* Active = TEXT("{green}");
|
||||
const TCHAR* Inactive = TEXT("{grey}");
|
||||
CanvasContext.Printf(TEXT("InputBuffers [%s%s{white}]\t PassedInputEntries [%s%s{white}]\t BlockedInputEntries [%s%s{white}]\t BufferedInputEntries [%s%s{white}]\t"),
|
||||
bShowInputBuffers ? Active : Inactive, *GetInputHandlerDescription(0),
|
||||
bShowPassedInputEntries ? Active : Inactive, *GetInputHandlerDescription(1),
|
||||
bShowBlockedInputEntries ? Active : Inactive, *GetInputHandlerDescription(2),
|
||||
bShowBufferedInputEntries ? Active : Inactive, *GetInputHandlerDescription(3)
|
||||
);
|
||||
}
|
||||
|
||||
if (LastDrawDataEndSize <= 0.0f)
|
||||
{
|
||||
// Default to the full frame size
|
||||
LastDrawDataEndSize = CanvasContext.Canvas->SizeY - CanvasContext.CursorY - CanvasContext.CursorX;
|
||||
}
|
||||
|
||||
constexpr FLinearColor BackgroundColor(0.1f, 0.1f, 0.1f, 0.8f);
|
||||
const FVector2D BackgroundPos{CanvasContext.CursorX, CanvasContext.CursorY};
|
||||
const FVector2D BackgroundSize(CanvasContext.Canvas->SizeX - (2.0f * CanvasContext.CursorX), LastDrawDataEndSize);
|
||||
|
||||
// Draw a transparent dark background so that the text is easier to look at
|
||||
FCanvasTileItem Background(FVector2D(0.0f), BackgroundSize, BackgroundColor);
|
||||
Background.BlendMode = SE_BLEND_Translucent;
|
||||
|
||||
CanvasContext.DrawItem(Background, BackgroundPos.X, BackgroundPos.Y);
|
||||
|
||||
CanvasContext.Printf(TEXT("{white}Actor name: {yellow}%s \t{white}Config: {yellow}%s \t{white}Control: {yellow}%s"), *DataPack.ActorName, *DataPack.InputConfig, *DataPack.InputControlSetup);
|
||||
|
||||
if (bShowInputBuffers)
|
||||
{
|
||||
DrawInputBuffers(CanvasContext, OwnerPC);
|
||||
}
|
||||
|
||||
DrawInputEntries(CanvasContext, OwnerPC);
|
||||
}
|
||||
|
||||
void FGIPS_GameplayDebuggerCategory_Input::FRepData::Serialize(FArchive& Ar)
|
||||
{
|
||||
Ar << ActorName;
|
||||
Ar << InputConfig;
|
||||
Ar << InputControlSetup;
|
||||
}
|
||||
|
||||
TSharedRef<FGameplayDebuggerCategory> FGIPS_GameplayDebuggerCategory_Input::MakeInstance()
|
||||
{
|
||||
return MakeShareable(new FGIPS_GameplayDebuggerCategory_Input());
|
||||
}
|
||||
|
||||
void FGIPS_GameplayDebuggerCategory_Input::OnShowInputBuffersToggle()
|
||||
{
|
||||
bShowInputBuffers = !bShowInputBuffers;
|
||||
}
|
||||
|
||||
void FGIPS_GameplayDebuggerCategory_Input::OnShowPassedInputEntriesToggle()
|
||||
{
|
||||
bShowPassedInputEntries = !bShowPassedInputEntries;
|
||||
}
|
||||
|
||||
void FGIPS_GameplayDebuggerCategory_Input::OnShowBlockedInputEntriesToggle()
|
||||
{
|
||||
bShowBlockedInputEntries = !bShowBlockedInputEntries;
|
||||
}
|
||||
|
||||
void FGIPS_GameplayDebuggerCategory_Input::OnShowBufferedInputEntriesToggle()
|
||||
{
|
||||
bShowBufferedInputEntries = !bShowBufferedInputEntries;
|
||||
}
|
||||
|
||||
void FGIPS_GameplayDebuggerCategory_Input::DrawInputBuffers(FGameplayDebuggerCanvasContext& CanvasContext, const APlayerController* OwnerPC) const
|
||||
{
|
||||
const float CanvasWidth = CanvasContext.Canvas->SizeX;
|
||||
|
||||
int32 NumActive = 0;
|
||||
for (const FRepData::FInputBuffersDebug& ItemData : DataPack.InputBuffers)
|
||||
{
|
||||
NumActive += ItemData.bIsActive;
|
||||
}
|
||||
|
||||
// Measure the individual string sizes, so that we can size the columns properly
|
||||
// We're picking a long-enough name for the object name sizes
|
||||
constexpr float Padding = 10.0f;
|
||||
static float WindowNameSize = 0.0f, ActiveNameSize = 0.0f, InputTagSize = 0.0f, InputEventSize = 0.0f;
|
||||
|
||||
if (WindowNameSize <= 0.0f)
|
||||
{
|
||||
float TempSizeY = 0.0f;
|
||||
|
||||
// We have to actually use representative strings because of the kerning
|
||||
CanvasContext.MeasureString(TEXT("WindowName: Combo"), WindowNameSize, TempSizeY);
|
||||
CanvasContext.MeasureString(TEXT("Active: False"), ActiveNameSize, TempSizeY);
|
||||
CanvasContext.MeasureString(TEXT("InputTag: InputTag.Attack"), InputTagSize, TempSizeY);
|
||||
CanvasContext.MeasureString(TEXT("InputEvent: Triggered"), InputEventSize, TempSizeY);
|
||||
|
||||
WindowNameSize += Padding;
|
||||
}
|
||||
|
||||
const float ColumnWidth = WindowNameSize * 2 + ActiveNameSize + InputTagSize + InputEventSize;
|
||||
const int NumColumns = FMath::Max(1, FMath::FloorToInt(CanvasWidth / ColumnWidth));
|
||||
|
||||
CanvasContext.Print(TEXT("Input Buffers:"));
|
||||
CanvasContext.CursorX += 200.0f;
|
||||
CanvasContext.CursorY -= CanvasContext.GetLineHeight();
|
||||
CanvasContext.Printf(TEXT("Legend: {yellow}Total [%d] {cyan}Active [%d]"), DataPack.InputBuffers.Num(), NumActive);
|
||||
|
||||
CanvasContext.CursorX += Padding;
|
||||
|
||||
for (const FRepData::FInputBuffersDebug& ItemData : DataPack.InputBuffers)
|
||||
{
|
||||
float CursorX = CanvasContext.CursorX;
|
||||
float CursorY = CanvasContext.CursorY;
|
||||
// Print positions manually to align them properly
|
||||
CanvasContext.PrintAt(CursorX + WindowNameSize * 0, CursorY, ItemData.bIsActive ? FColor::Cyan : FColor::Yellow, ItemData.WindowName.ToString());
|
||||
CanvasContext.PrintAt(CursorX + WindowNameSize * 1, CursorY, FString::Printf(TEXT("{grey}active: {white}%.35s"), ItemData.bIsActive ? TEXT("True") : TEXT("False")));
|
||||
CanvasContext.PrintAt(CursorX + WindowNameSize * 2, CursorY, FString::Printf(TEXT("{grey}InputTag: {white}%s"), *ItemData.InputTagName.ToString()));
|
||||
CanvasContext.PrintAt(CursorX + WindowNameSize * 3, CursorY,
|
||||
FString::Printf(TEXT("{grey}InputEvent: {white}%s"), *UGIPS_InputFunctionLibrary::GetTriggerEventString(ItemData.InputEvent)));
|
||||
|
||||
// PrintAt would have reset these values, restore them.
|
||||
CanvasContext.CursorX = CursorX + (CanvasWidth / NumColumns);
|
||||
CanvasContext.CursorY = CursorY;
|
||||
|
||||
// If we're going to overflow, go to the next line...
|
||||
if (CanvasContext.CursorX + ColumnWidth >= CanvasWidth)
|
||||
{
|
||||
CanvasContext.MoveToNewLine();
|
||||
CanvasContext.CursorX += Padding;
|
||||
}
|
||||
}
|
||||
|
||||
// End the row with a newline
|
||||
if (CanvasContext.CursorX != CanvasContext.DefaultX)
|
||||
{
|
||||
CanvasContext.MoveToNewLine();
|
||||
}
|
||||
|
||||
CanvasContext.Printf(TEXT("{grey}Last triggered input: {white}%s"), *(DataPack.BufferedInputTag.IsValid() ? DataPack.BufferedInputTag.GetTagName().ToString() : TEXT("None")));
|
||||
|
||||
// End the category with a newline to separate from the other categories
|
||||
CanvasContext.MoveToNewLine();
|
||||
}
|
||||
|
||||
void FGIPS_GameplayDebuggerCategory_Input::DrawInputEntries(FGameplayDebuggerCanvasContext& CanvasContext, const APlayerController* OwnerPC) const
|
||||
{
|
||||
const UGIPS_InputSystemComponent* InputSystem = UGIPS_InputSystemComponent::GetInputSystemComponent(FindLocalDebugActor());
|
||||
if (InputSystem == nullptr || (!bShowPassedInputEntries && !bShowBlockedInputEntries && !bShowBufferedInputEntries))
|
||||
return;
|
||||
|
||||
const float CanvasWidth = CanvasContext.Canvas->SizeX;
|
||||
|
||||
constexpr float Padding = 10.0f;
|
||||
|
||||
constexpr float ColumnSize = 150;
|
||||
constexpr float ColumnWidth = ColumnSize * 3;
|
||||
const int NumColumns = FMath::Max(1, FMath::FloorToInt(CanvasContext.Canvas->SizeX / ColumnWidth));
|
||||
|
||||
auto DrawEntries = [&](const FString& Header, const TArray<FGIPS_BufferedInput>& Entries)
|
||||
{
|
||||
CanvasContext.Printf(TEXT("%s"), *Header);
|
||||
|
||||
CanvasContext.CursorX += Padding;
|
||||
|
||||
for (int i = 0; i < Entries.Num(); ++i)
|
||||
{
|
||||
const FGIPS_BufferedInput& Entry = Entries[i];
|
||||
float CursorX = CanvasContext.CursorX;
|
||||
float CursorY = CanvasContext.CursorY;
|
||||
// Print positions manually to align them properly
|
||||
CanvasContext.PrintAt(CursorX + ColumnSize * 0, CursorY, i == Entries.Num() - 1 ? FColor::Cyan : FColor::Yellow,
|
||||
Entry.InputTag.IsValid() ? *UGIPS_InputFunctionLibrary::GetLastTagName(Entry.InputTag).ToString() : TEXT("None"));
|
||||
CanvasContext.PrintAt(CursorX + ColumnSize * 1, CursorY, FString::Printf(TEXT("{grey}TriggerEvent: {white}%s"), *UGIPS_InputFunctionLibrary::GetTriggerEventString(Entry.TriggerEvent)));
|
||||
CanvasContext.PrintAt(CursorX + ColumnSize * 2, CursorY, FString::Printf(TEXT("{grey}ActionValue: {white}%s"), *Entry.ActionData.GetValue().ToString()));
|
||||
|
||||
// PrintAt would have reset these values, restore them.
|
||||
CanvasContext.CursorX = CursorX + (CanvasWidth / NumColumns);
|
||||
CanvasContext.CursorY = CursorY;
|
||||
|
||||
CanvasContext.MoveToNewLine();
|
||||
CanvasContext.CursorX += Padding;
|
||||
}
|
||||
|
||||
// End the row with a newline
|
||||
if (CanvasContext.CursorX != CanvasContext.DefaultX)
|
||||
{
|
||||
CanvasContext.MoveToNewLine();
|
||||
}
|
||||
};
|
||||
|
||||
if (bShowPassedInputEntries)
|
||||
{
|
||||
auto Entries = InputSystem->GetPassedInputEntries();
|
||||
DrawEntries(TEXT("Passed Inputs:"), Entries);
|
||||
}
|
||||
|
||||
if (bShowBlockedInputEntries)
|
||||
{
|
||||
auto Entries = InputSystem->GetBlockedInputEntries();
|
||||
DrawEntries(TEXT("Blocked Inputs:"), Entries);
|
||||
}
|
||||
|
||||
if (bShowBufferedInputEntries)
|
||||
{
|
||||
auto Entries = InputSystem->GetBufferedInputEntries();
|
||||
DrawEntries(TEXT("Buffered Inputs:"), Entries);
|
||||
}
|
||||
|
||||
// End the category with a newline to separate from the other categories
|
||||
CanvasContext.MoveToNewLine();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,94 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "GIPS_InputChecker.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "GameplayTagAssetInterface.h"
|
||||
#include "GIPS_InputSystemComponent.h"
|
||||
|
||||
bool UGIPS_InputChecker::CheckInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag, ETriggerEvent TriggerEvent) const
|
||||
{
|
||||
return DoCheckInput(IC, ActionData, InputTag, TriggerEvent);
|
||||
}
|
||||
|
||||
bool UGIPS_InputChecker::DoCheckInput_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag& InputTag,
|
||||
const ETriggerEvent& TriggerEvent) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
FGameplayTagContainer UGIPS_InputChecker_TagRelationship::GetActorTags_Implementation(UGIPS_InputSystemComponent* IC) const
|
||||
{
|
||||
FGameplayTagContainer Tags;
|
||||
if (const IGameplayTagAssetInterface* TagAssetInterface = Cast<IGameplayTagAssetInterface>(IC->GetOwner()))
|
||||
{
|
||||
TagAssetInterface->GetOwnedGameplayTags(Tags);
|
||||
}
|
||||
return Tags;
|
||||
}
|
||||
|
||||
bool UGIPS_InputChecker_TagRelationship::DoCheckInput_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag& InputTag,
|
||||
const ETriggerEvent& TriggerEvent) const
|
||||
{
|
||||
const FGameplayTagContainer ActorOwnedTags = GetActorTags(IC);
|
||||
|
||||
for (const FGIPS_InputTagRelationship& Relationship : InputTagRelationships)
|
||||
{
|
||||
// TagQuery > TagRequirements.
|
||||
if (Relationship.ActorTagQuery.IsEmpty() || !Relationship.ActorTagQuery.Matches(ActorOwnedTags))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool bNotAllowed = false;
|
||||
bool bBlocked = false;
|
||||
|
||||
if (!Relationship.AllowedInputs.IsEmpty())
|
||||
{
|
||||
int32 Index = Relationship.IndexOfAllowedInput(InputTag, TriggerEvent);
|
||||
if (Index == INDEX_NONE)
|
||||
{
|
||||
bNotAllowed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Relationship.BlockedInputs.IsEmpty())
|
||||
{
|
||||
int32 Index = Relationship.IndexOfBlockedInput(InputTag, TriggerEvent);
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
bBlocked = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bNotAllowed || bBlocked)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
#include "UObject/ObjectSaveContext.h"
|
||||
|
||||
void UGIPS_InputChecker_TagRelationship::PreSave(FObjectPreSaveContext SaveContext)
|
||||
{
|
||||
for (FGIPS_InputTagRelationship& InputTagRelationship : InputTagRelationships)
|
||||
{
|
||||
InputTagRelationship.EditorFriendlyName = InputTagRelationship.ActorTagQuery.GetDescription();
|
||||
// TArray<FGameplayTag> TagArray;
|
||||
// InputTagRelationship.InputTagsAllowed.GetGameplayTagArray(TagArray);
|
||||
// InputTagRelationship.AllowedInputs.Empty();
|
||||
// for (FGameplayTag Tag : TagArray)
|
||||
// {
|
||||
// FGIPS_AllowedInput AllowedInput;
|
||||
// AllowedInput.TriggerEvents.Empty();
|
||||
// AllowedInput.InputTag = Tag;
|
||||
// InputTagRelationship.AllowedInputs.Add(AllowedInput);
|
||||
// }
|
||||
}
|
||||
Super::PreSave(SaveContext);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "GIPS_InputConfig.h"
|
||||
|
||||
|
||||
|
||||
#if WITH_EDITOR
|
||||
#include "UObject/ObjectSaveContext.h"
|
||||
#include "Misc/DataValidation.h"
|
||||
|
||||
void UGIPS_InputConfig::PreSave(FObjectPreSaveContext SaveContext)
|
||||
{
|
||||
Super::PreSave(SaveContext);
|
||||
}
|
||||
|
||||
EDataValidationResult UGIPS_InputConfig::IsDataValid(FDataValidationContext& Context) const
|
||||
{
|
||||
if (InputActionMappings.IsEmpty())
|
||||
{
|
||||
Context.AddError(FText::FromString(TEXT("InputActionMappings can't be empty!")));
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
return Super::IsDataValid(Context);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,133 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "GIPS_InputControlSetup.h"
|
||||
|
||||
#include "GIPS_LogChannels.h"
|
||||
#include "GIPS_InputChecker.h"
|
||||
#include "GIPS_InputSystemComponent.h"
|
||||
#include "GIPS_InputFunctionLibrary.h"
|
||||
#include "Misc/DataValidation.h"
|
||||
|
||||
void UGIPS_InputControlSetup::HandleInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent) const
|
||||
{
|
||||
TArray<TObjectPtr<UGIPS_InputProcessor>> Processors = FilterInputProcessors(InputTag, TriggerEvent);
|
||||
for (int32 i = 0; i < Processors.Num(); i++)
|
||||
{
|
||||
UGIPS_InputProcessor* Processor = Processors[i];
|
||||
if (Processor->CanHandleInput(IC, ActionData, InputTag, TriggerEvent))
|
||||
{
|
||||
Processor->HandleInput(IC, ActionData, InputTag, TriggerEvent);
|
||||
if (InputProcessorExecutionType == EGIPS_InputProcessorExecutionType::FirstOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ShouldDebug(InputTag, TriggerEvent))
|
||||
{
|
||||
GIPS_OWNED_CLOG(IC, Verbose, "Input:%s can't be handled by processor:%s at index(%d), TriggerEvent:%s", *InputTag.ToString(), *Processor->GetClass()->GetName(),
|
||||
i, *UGIPS_InputFunctionLibrary::GetTriggerEventString(TriggerEvent));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UGIPS_InputControlSetup::CheckInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent)
|
||||
{
|
||||
if (InternalCheckInput(IC, ActionData, InputTag, TriggerEvent))
|
||||
{
|
||||
if (ShouldDebug(InputTag, TriggerEvent))
|
||||
{
|
||||
GIPS_OWNED_CLOG(IC, Verbose, "Input:%s passed,TriggerEvent:%s", *InputTag.ToString(), *UGIPS_InputFunctionLibrary::GetTriggerEventString(TriggerEvent));
|
||||
}
|
||||
IC->RegisterPassedInputEntry({InputTag, ActionData, TriggerEvent});
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bEnableInputBuffer)
|
||||
{
|
||||
if (IC->TrySaveInput(ActionData, InputTag, TriggerEvent))
|
||||
{
|
||||
if (ShouldDebug(InputTag, TriggerEvent))
|
||||
{
|
||||
GIPS_OWNED_CLOG(IC, Verbose, "Input:%s buffered,TriggerEvent:%s", *InputTag.ToString(), *UGIPS_InputFunctionLibrary::GetTriggerEventString(TriggerEvent));
|
||||
}
|
||||
IC->RegisterBufferedInputEntry({InputTag, ActionData, TriggerEvent});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ShouldDebug(InputTag, TriggerEvent))
|
||||
{
|
||||
GIPS_OWNED_CLOG(IC, Verbose, "Input:%s blocked,TriggerEvent:%s", *InputTag.ToString(), *UGIPS_InputFunctionLibrary::GetTriggerEventString(TriggerEvent));
|
||||
}
|
||||
|
||||
IC->RegisterBlockedInputEntry({InputTag, ActionData, TriggerEvent});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UGIPS_InputControlSetup::ShouldDebug(const FGameplayTag& InputTag, const ETriggerEvent& TriggerEvent) const
|
||||
{
|
||||
return bEnableInputDebug && (DebugInputTags.IsEmpty() || DebugInputTags.HasTagExact(InputTag)) && (DebugTriggerEvents.IsEmpty() || DebugTriggerEvents.Contains(TriggerEvent));
|
||||
}
|
||||
|
||||
bool UGIPS_InputControlSetup::InternalCheckInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent)
|
||||
{
|
||||
if (InputCheckers.IsEmpty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int32 AlwaysAllowedInputIndex = INDEX_NONE;
|
||||
|
||||
for (int32 i = 0; i < AlwaysAllowedInputs.Num(); i++)
|
||||
{
|
||||
if (AlwaysAllowedInputs[i].InputTag == InputTag && (AlwaysAllowedInputs[i].TriggerEvents.IsEmpty() || AlwaysAllowedInputs[i].TriggerEvents.Contains(TriggerEvent)))
|
||||
{
|
||||
AlwaysAllowedInputIndex = i;
|
||||
}
|
||||
}
|
||||
if (AlwaysAllowedInputIndex != INDEX_NONE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const UGIPS_InputChecker* Checker : InputCheckers)
|
||||
{
|
||||
if (Checker == nullptr)
|
||||
continue;
|
||||
if (!Checker->CheckInput(IC, ActionData, InputTag, TriggerEvent))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TArray<TObjectPtr<UGIPS_InputProcessor>> UGIPS_InputControlSetup::FilterInputProcessors(const FGameplayTag& InputTag, const ETriggerEvent& TriggerEvent) const
|
||||
{
|
||||
return InputProcessors.FilterByPredicate([&](TObjectPtr<UGIPS_InputProcessor> Processor)
|
||||
{
|
||||
return Processor && !Processor->InputTags.IsEmpty() && Processor->InputTags.HasTagExact(InputTag) && Processor->TriggerEvents.Contains(TriggerEvent);
|
||||
});
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
EDataValidationResult UGIPS_InputControlSetup::IsDataValid(FDataValidationContext& Context) const
|
||||
{
|
||||
for (int32 i = 0; i < InputProcessors.Num(); i++)
|
||||
{
|
||||
if (InputProcessors[i] == nullptr)
|
||||
{
|
||||
Context.AddError(FText::FromString(FString::Format(TEXT("Invalid processor at index:{0}"), {i})));
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
if (InputProcessors[i]->InputTags.IsEmpty())
|
||||
{
|
||||
Context.AddWarning(FText::FromString(FString::Format(TEXT("Invalid processor at index:{0} has empty InputTags!!!"), {i})));
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
}
|
||||
return Super::IsDataValid(Context);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "GIPS_InputFunctionLibrary.h"
|
||||
|
||||
#include "GameplayTagsManager.h"
|
||||
|
||||
FInputActionValue UGIPS_InputFunctionLibrary::GetInputActionValue(const FInputActionInstance& ActionDataData)
|
||||
{
|
||||
return ActionDataData.GetValue();
|
||||
}
|
||||
|
||||
FName UGIPS_InputFunctionLibrary::GetLastTagName(FGameplayTag Tag)
|
||||
{
|
||||
if (!Tag.IsValid())
|
||||
{
|
||||
return FName(TEXT("Invalid Tag"));
|
||||
}
|
||||
|
||||
TArray<FName> TagNames;
|
||||
|
||||
UGameplayTagsManager::Get().SplitGameplayTagFName(Tag, TagNames);
|
||||
|
||||
if (TagNames.IsEmpty())
|
||||
{
|
||||
return FName(TEXT("Invalid Tag"));
|
||||
}
|
||||
|
||||
return TagNames.Last();
|
||||
}
|
||||
|
||||
FString UGIPS_InputFunctionLibrary::GetSimpleStringOfTags(FGameplayTagContainer Tags)
|
||||
{
|
||||
return Tags.ToStringSimple();
|
||||
}
|
||||
|
||||
TArray<FName> UGIPS_InputFunctionLibrary::GetLastTagNameArray(FGameplayTagContainer Tags)
|
||||
{
|
||||
TArray<FGameplayTag> TagArray;
|
||||
Tags.GetGameplayTagArray(TagArray);
|
||||
|
||||
TArray<FName> NameArray;
|
||||
for (const FGameplayTag& Tag : TagArray)
|
||||
{
|
||||
NameArray.Add(GetLastTagName(Tag));
|
||||
}
|
||||
|
||||
return NameArray;
|
||||
}
|
||||
|
||||
FString UGIPS_InputFunctionLibrary::GetLastTagNameString(FGameplayTagContainer Tags)
|
||||
{
|
||||
TArray<FGameplayTag> TagArray;
|
||||
Tags.GetGameplayTagArray(TagArray);
|
||||
|
||||
FString Output;
|
||||
for (const FGameplayTag& Tag : TagArray)
|
||||
{
|
||||
Output.Append(FString::Format(TEXT(" ({0}) "), {GetLastTagName(Tag).ToString()}));
|
||||
}
|
||||
|
||||
return Output;
|
||||
}
|
||||
|
||||
FString UGIPS_InputFunctionLibrary::GetTagQueryDescription(const FGameplayTagQuery& TagQuery)
|
||||
{
|
||||
if (TagQuery.IsEmpty())
|
||||
{
|
||||
return TEXT("Empty Query");
|
||||
}
|
||||
|
||||
return TagQuery.GetDescription();
|
||||
}
|
||||
|
||||
FString UGIPS_InputFunctionLibrary::GetTriggerEventString(ETriggerEvent TriggerEvent)
|
||||
{
|
||||
switch (TriggerEvent)
|
||||
{
|
||||
case ETriggerEvent::Started:
|
||||
return TEXT("Start");
|
||||
case ETriggerEvent::Triggered:
|
||||
return TEXT("Triggered");
|
||||
case ETriggerEvent::Canceled:
|
||||
return TEXT("Canceled");
|
||||
case ETriggerEvent::Ongoing:
|
||||
return TEXT("Ongoing");
|
||||
case ETriggerEvent::Completed:
|
||||
return TEXT("Completed");
|
||||
case ETriggerEvent::None:
|
||||
default:
|
||||
return TEXT("None");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "GIPS_InputProcessor.h"
|
||||
|
||||
#include "EnhancedInputComponent.h"
|
||||
#include "GIPS_InputChecker.h"
|
||||
#include "GIPS_InputFunctionLibrary.h"
|
||||
|
||||
|
||||
UGIPS_InputProcessor::UGIPS_InputProcessor()
|
||||
{
|
||||
}
|
||||
|
||||
bool UGIPS_InputProcessor::CanHandleInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag, ETriggerEvent TriggerEvent) const
|
||||
{
|
||||
return CheckCanHandleInput(IC, ActionData, InputTag, TriggerEvent);
|
||||
}
|
||||
|
||||
void UGIPS_InputProcessor::HandleInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag, ETriggerEvent TriggerEvent) const
|
||||
{
|
||||
switch (TriggerEvent)
|
||||
{
|
||||
case ETriggerEvent::Triggered:
|
||||
return HandleInputTriggered(IC, ActionData, InputTag);
|
||||
case ETriggerEvent::Started:
|
||||
return HandleInputStarted(IC, ActionData, InputTag);
|
||||
case ETriggerEvent::Ongoing:
|
||||
return HandleInputOngoing(IC, ActionData, InputTag);
|
||||
case ETriggerEvent::Canceled:
|
||||
return HandleInputCanceled(IC, ActionData, InputTag);
|
||||
case ETriggerEvent::Completed:
|
||||
return HandleInputCompleted(IC, ActionData, InputTag);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
FInputActionValue UGIPS_InputProcessor::GetInputActionValue(const FInputActionInstance& ActionData) const
|
||||
{
|
||||
const FInputActionValue Value = ActionData.GetValue();
|
||||
return Value;
|
||||
}
|
||||
|
||||
bool UGIPS_InputProcessor::CheckCanHandleInput_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag, ETriggerEvent TriggerEvent) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
FString UGIPS_InputProcessor::GetEditorFriendlyName_Implementation() const
|
||||
{
|
||||
return TEXT("");
|
||||
}
|
||||
|
||||
void UGIPS_InputProcessor::HandleInputCanceled_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const
|
||||
{
|
||||
}
|
||||
|
||||
void UGIPS_InputProcessor::HandleInputCompleted_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const
|
||||
{
|
||||
}
|
||||
|
||||
void UGIPS_InputProcessor::HandleInputOngoing_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const
|
||||
{
|
||||
}
|
||||
|
||||
void UGIPS_InputProcessor::HandleInputStarted_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const
|
||||
{
|
||||
}
|
||||
|
||||
void UGIPS_InputProcessor::HandleInputTriggered_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const
|
||||
{
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
#include "UObject/ObjectSaveContext.h"
|
||||
|
||||
FString UGIPS_InputProcessor::NativeGetEditorFriendlyName() const
|
||||
{
|
||||
FString BPOverride = GetEditorFriendlyName();
|
||||
|
||||
return FString::Format(TEXT("Input:{0} {1}"), {UGIPS_InputFunctionLibrary::GetLastTagNameString(InputTags),BPOverride});
|
||||
}
|
||||
|
||||
void UGIPS_InputProcessor::PreSave(FObjectPreSaveContext SaveContext)
|
||||
{
|
||||
EditorFriendlyName = NativeGetEditorFriendlyName();
|
||||
UObject::PreSave(SaveContext);
|
||||
}
|
||||
#endif
|
||||
@@ -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
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "GIPS_InputTypes.h"
|
||||
|
||||
#include "GIPS_InputFunctionLibrary.h"
|
||||
|
||||
bool FGIPS_InputBufferWindow::operator==(const FGameplayTag& OtherTag) const
|
||||
{
|
||||
return Tag == OtherTag;
|
||||
}
|
||||
|
||||
bool FGIPS_InputBufferWindow::operator!=(const FGameplayTag& OtherTag) const
|
||||
{
|
||||
return Tag != OtherTag;
|
||||
}
|
||||
|
||||
FString FGIPS_BufferedInput::ToString() const
|
||||
{
|
||||
return FString::Format(TEXT("Tag:{0},Event:{1},Source:{2}"), {
|
||||
InputTag.IsValid() ? *UGIPS_InputFunctionLibrary::GetLastTagName(InputTag).ToString() : TEXT("None"),
|
||||
*UGIPS_InputFunctionLibrary::GetTriggerEventString(TriggerEvent),
|
||||
ActionData.GetSourceAction() ? ActionData.GetSourceAction()->GetName() : TEXT("None"),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "GIPS_LogChannels.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogGIPS);
|
||||
|
||||
FString GetGIPSLogContextString(const UObject* ContextObject)
|
||||
{
|
||||
ENetRole Role = ROLE_None;
|
||||
FString RoleName = TEXT("None");
|
||||
FString Name = "None";
|
||||
|
||||
if (const AActor* Actor = Cast<AActor>(ContextObject))
|
||||
{
|
||||
Role = Actor->GetLocalRole();
|
||||
Name = Actor->GetName();
|
||||
}
|
||||
else if (const UActorComponent* Component = Cast<UActorComponent>(ContextObject))
|
||||
{
|
||||
Role = Component->GetOwnerRole();
|
||||
Name = Component->GetOwner()->GetName();
|
||||
}
|
||||
else if (IsValid(ContextObject))
|
||||
{
|
||||
Name = ContextObject->GetName();
|
||||
}
|
||||
|
||||
if (Role != ROLE_None)
|
||||
{
|
||||
RoleName = (Role == ROLE_Authority) ? TEXT("Server") : TEXT("Client");
|
||||
}
|
||||
return FString::Printf(TEXT("[%s] (%s)"), *RoleName, *Name);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "GenericInputSystem.h"
|
||||
|
||||
#if WITH_GAMEPLAY_DEBUGGER
|
||||
#include "GameplayDebugger.h"
|
||||
#include "GIPS_GameplayDebugger.h"
|
||||
#endif // WITH_GAMEPLAY_DEBUGGER
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FGenericInputSystemModule"
|
||||
|
||||
void FGenericInputSystemModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
||||
|
||||
#if WITH_GAMEPLAY_DEBUGGER
|
||||
IGameplayDebugger& GameplayDebuggerModule = IGameplayDebugger::Get();
|
||||
GameplayDebuggerModule.RegisterCategory("GenericInputSystem", IGameplayDebugger::FOnGetCategory::CreateStatic(&FGIPS_GameplayDebuggerCategory_Input::MakeInstance));
|
||||
GameplayDebuggerModule.NotifyCategoriesChanged();
|
||||
#endif // WITH_GAMEPLAY_DEBUGGER
|
||||
}
|
||||
|
||||
void FGenericInputSystemModule::ShutdownModule()
|
||||
{
|
||||
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
|
||||
// we call this function before unloading the module.
|
||||
#if WITH_GAMEPLAY_DEBUGGER
|
||||
if (IGameplayDebugger::IsAvailable())
|
||||
{
|
||||
IGameplayDebugger& GameplayDebuggerModule = IGameplayDebugger::Get();
|
||||
GameplayDebuggerModule.UnregisterCategory("GenericInputSystem");
|
||||
GameplayDebuggerModule.NotifyCategoriesChanged();
|
||||
}
|
||||
#endif // WITH_GAMEPLAY_DEBUGGER
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FGenericInputSystemModule, GenericInputSystem)
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "InputAction.h"
|
||||
#include "Engine/CancellableAsyncAction.h"
|
||||
#include "GIPS_InputTypes.h"
|
||||
#include "GIPS_AsyncAction_ListenInputEvent.generated.h"
|
||||
|
||||
class UGIPS_InputSystemComponent;
|
||||
|
||||
/**
|
||||
* Async action for listening to input events.
|
||||
* 用于监听输入事件的异步动作。
|
||||
*/
|
||||
UCLASS()
|
||||
class GENERICINPUTSYSTEM_API UGIPS_AsyncAction_ListenInputEvent : public UCancellableAsyncAction
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates an async action to listen for input events.
|
||||
* 创建一个异步动作以监听输入事件。
|
||||
* @param WorldContextObject The world context object. 世界上下文对象。
|
||||
* @param InputSystemComponent The input system component. 输入系统组件。
|
||||
* @param InputTagsToListen Tags to listen for. 要监听的标签。
|
||||
* @param EventsToListen Trigger events to listen for. 要监听的触发事件。
|
||||
* @param bListenForBufferedInput Whether to listen for buffered inputs. 是否监听缓冲输入。
|
||||
* @param bExactMatch Whether to require exact tag matching. 是否要求精确标签匹配。
|
||||
* @return The async action instance. 异步动作实例。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|Input", meta=(WorldContext = "WorldContextObject", BlueprintInternalUseOnly="true"))
|
||||
static UGIPS_AsyncAction_ListenInputEvent* ListenInputEvent(UObject* WorldContextObject, UGIPS_InputSystemComponent* InputSystemComponent,UPARAM(meta=(Categories="InputTag,GIPS.InputTag"))
|
||||
FGameplayTagContainer InputTagsToListen,
|
||||
TArray<ETriggerEvent> EventsToListen, bool bListenForBufferedInput = false, bool bExactMatch = true);
|
||||
|
||||
/**
|
||||
* Activates the async action.
|
||||
* 激活异步动作。
|
||||
*/
|
||||
virtual void Activate() override;
|
||||
|
||||
/**
|
||||
* Event triggered when an input is received.
|
||||
* 接收到输入时触发的事件。
|
||||
*/
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FGIPS_ReceivedInputSignature OnReceivedInput;
|
||||
|
||||
/**
|
||||
* Handles the input event.
|
||||
* 处理输入事件。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
*/
|
||||
UFUNCTION()
|
||||
void HandleInput(const FInputActionInstance& ActionData, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Cancels the async action.
|
||||
* 取消异步动作。
|
||||
*/
|
||||
virtual void Cancel() override;
|
||||
|
||||
/**
|
||||
* Tags to listen for.
|
||||
* 要监听的标签。
|
||||
*/
|
||||
FGameplayTagContainer InputTags;
|
||||
|
||||
/**
|
||||
* Trigger events to listen for.
|
||||
* 要监听的触发事件。
|
||||
*/
|
||||
TArray<ETriggerEvent> TriggerEvents;
|
||||
|
||||
/**
|
||||
* Whether to listen for buffered inputs.
|
||||
* 是否监听缓冲输入。
|
||||
*/
|
||||
bool bForBufferedInput{false};
|
||||
|
||||
/**
|
||||
* Whether to require exact tag matching.
|
||||
* 是否要求精确标签匹配。
|
||||
*/
|
||||
bool bExact{true};
|
||||
|
||||
/**
|
||||
* Weak reference to the input system component.
|
||||
* 输入系统组件的弱引用。
|
||||
*/
|
||||
TWeakObjectPtr<UGIPS_InputSystemComponent> Input;
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "GameplayTagContainer.h"
|
||||
|
||||
#if WITH_GAMEPLAY_DEBUGGER
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayDebuggerCategory.h"
|
||||
#include "InputTriggers.h"
|
||||
|
||||
class APlayerController;
|
||||
class AActor;
|
||||
|
||||
class FGIPS_GameplayDebuggerCategory_Input : public FGameplayDebuggerCategory
|
||||
{
|
||||
public:
|
||||
FGIPS_GameplayDebuggerCategory_Input();
|
||||
void CollectData(APlayerController* OwnerPC, AActor* DebugActor) override;
|
||||
|
||||
void DrawData(APlayerController* OwnerPC, FGameplayDebuggerCanvasContext& CanvasContext) override;
|
||||
|
||||
static TSharedRef<FGameplayDebuggerCategory> MakeInstance();
|
||||
|
||||
void OnShowInputBuffersToggle();
|
||||
void OnShowPassedInputEntriesToggle();
|
||||
void OnShowBlockedInputEntriesToggle();
|
||||
void OnShowBufferedInputEntriesToggle();
|
||||
|
||||
protected:
|
||||
void DrawInputBuffers(FGameplayDebuggerCanvasContext& CanvasContext, const APlayerController* OwnerPC) const;
|
||||
void DrawInputEntries(FGameplayDebuggerCanvasContext& CanvasContext, const APlayerController* OwnerPC) const;
|
||||
|
||||
struct FRepData
|
||||
{
|
||||
FString ActorName;
|
||||
FString InputConfig;
|
||||
FString InputControlSetup;
|
||||
|
||||
FGameplayTag BufferedInputTag;
|
||||
|
||||
struct FInputBuffersDebug
|
||||
{
|
||||
FName WindowName;
|
||||
bool bIsActive;
|
||||
FName InputTagName;
|
||||
ETriggerEvent InputEvent;
|
||||
};
|
||||
|
||||
TArray<FInputBuffersDebug> InputBuffers;
|
||||
|
||||
void Serialize(FArchive&Ar);
|
||||
};
|
||||
|
||||
FRepData DataPack;
|
||||
|
||||
private:
|
||||
|
||||
// Save off the last expected draw size so that we can draw a border around it next frame (and hope we're the same size)
|
||||
float LastDrawDataEndSize = 0.0f;
|
||||
|
||||
bool bShowInputBuffers = true;
|
||||
bool bShowPassedInputEntries = true;
|
||||
bool bShowBlockedInputEntries = true;
|
||||
bool bShowBufferedInputEntries = true;
|
||||
};
|
||||
|
||||
|
||||
#endif // WITH_GAMEPLAY_DEBUGG
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GIPS_InputTypes.h"
|
||||
#include "InputAction.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "GIPS_InputChecker.generated.h"
|
||||
|
||||
class UGIPS_InputSystemComponent;
|
||||
|
||||
/**
|
||||
* Base class for input validation, inheritable via Blueprint or C++.
|
||||
* 输入验证的基类,可通过蓝图或C++继承。
|
||||
*/
|
||||
UCLASS(Abstract, Blueprintable, BlueprintType, EditInlineNew, DefaultToInstanced, CollapseCategories, Const)
|
||||
class GENERICINPUTSYSTEM_API UGIPS_InputChecker : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Checks if an input is valid.
|
||||
* 检查输入是否有效。
|
||||
* @param IC The input system component. 输入系统组件。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
* @return True if the input is valid, false otherwise. 如果输入有效则返回true,否则返回false。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|Input")
|
||||
bool CheckInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, UPARAM(meta = (Categories="InputTag,GIPS.InputTag")) FGameplayTag InputTag,
|
||||
ETriggerEvent TriggerEvent) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Blueprint-implementable input validation logic.
|
||||
* 可通过蓝图实现的输入验证逻辑。
|
||||
* @param IC The input system component. 输入系统组件。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
* @return True if the input is valid, false otherwise. 如果输入有效则返回true,否则返回false。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="GIPS|Input", meta=(DisplayName="DoCheckInput"))
|
||||
bool DoCheckInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag& InputTag, const ETriggerEvent& TriggerEvent) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Input checker for tag-based relationships.
|
||||
* 基于标签关系的输入检查器。
|
||||
*/
|
||||
UCLASS()
|
||||
class GENERICINPUTSYSTEM_API UGIPS_InputChecker_TagRelationship : public UGIPS_InputChecker
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
/**
|
||||
* List of input tag relationships for validation.
|
||||
* 用于验证的输入标签关系列表。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input", meta=(TitleProperty="EditorFriendlyName", ShowOnlyInnerProperties))
|
||||
TArray<FGIPS_InputTagRelationship> InputTagRelationships;
|
||||
|
||||
/**
|
||||
* Gets the actor's tags for validation.
|
||||
* 获取用于验证的演员标签。
|
||||
* @param IC The input system component. 输入系统组件。
|
||||
* @return The actor's gameplay tag container. 演员的游戏标签容器。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|Input", BlueprintNativeEvent)
|
||||
FGameplayTagContainer GetActorTags(UGIPS_InputSystemComponent* IC) const;
|
||||
virtual FGameplayTagContainer GetActorTags_Implementation(UGIPS_InputSystemComponent* IC) const;
|
||||
|
||||
/**
|
||||
* Implementation of input validation logic.
|
||||
* 输入验证逻辑的实现。
|
||||
* @param IC The input system component. 输入系统组件。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
* @return True if the input is valid, false otherwise. 如果输入有效则返回true,否则返回false。
|
||||
*/
|
||||
virtual bool DoCheckInput_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag& InputTag,
|
||||
const ETriggerEvent& TriggerEvent) const override;
|
||||
|
||||
#if WITH_EDITOR
|
||||
/**
|
||||
* Called before saving the object.
|
||||
* 在保存对象之前调用。
|
||||
* @param SaveContext The save context. 保存上下文。
|
||||
*/
|
||||
virtual void PreSave(FObjectPreSaveContext SaveContext) override;
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "GIPS_InputTypes.h"
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "GIPS_InputConfig.generated.h"
|
||||
|
||||
class UInputAction;
|
||||
|
||||
/**
|
||||
* Configuration data asset for the input system component.
|
||||
* 输入系统组件的配置数据资产。
|
||||
*/
|
||||
UCLASS(Const)
|
||||
class GENERICINPUTSYSTEM_API UGIPS_InputConfig : public UDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Mapping of input tags to input action settings.
|
||||
* 输入标签到输入动作设置的映射。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input", meta=(Categories="InputTag,GIPS.InputTag", ForceInlineRow))
|
||||
TMap<FGameplayTag, FGIPS_InputActionSetting> InputActionMappings;
|
||||
|
||||
/**
|
||||
* List of defined input buffer windows.
|
||||
* 定义的输入缓冲窗口列表。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input", meta=(DisplayName="Input Buffer Windows", TitleProperty="Tag", NoElementDuplicate))
|
||||
TArray<FGIPS_InputBufferWindow> InputBufferDefinitions;
|
||||
|
||||
protected:
|
||||
#if WITH_EDITOR
|
||||
/**
|
||||
* Called before saving the object.
|
||||
* 在保存对象之前调用。
|
||||
* @param SaveContext The save context. 保存上下文。
|
||||
*/
|
||||
virtual void PreSave(FObjectPreSaveContext SaveContext) override;
|
||||
|
||||
/**
|
||||
* Validates the data asset in the editor.
|
||||
* 在编辑器中验证数据资产。
|
||||
* @param Context The data validation context. 数据验证上下文。
|
||||
* @return The validation result. 验证结果。
|
||||
*/
|
||||
virtual EDataValidationResult IsDataValid(FDataValidationContext& Context) const override;
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,143 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GIPS_InputTypes.h"
|
||||
#include "GIPS_InputControlSetup.generated.h"
|
||||
|
||||
struct FInputActionInstance;
|
||||
class UGIPS_InputChecker;
|
||||
class UGIPS_InputProcessor;
|
||||
class UGIPS_InputSystemComponent;
|
||||
|
||||
/**
|
||||
* Data asset for defining input checkers and processors.
|
||||
* 定义输入检查器和处理器的数据资产。
|
||||
*/
|
||||
UCLASS(BlueprintType, Const)
|
||||
class GENERICINPUTSYSTEM_API UGIPS_InputControlSetup : public UDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Handles input actions for the input system.
|
||||
* 为输入系统处理输入动作。
|
||||
* @param IC The input system component. 输入系统组件。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
*/
|
||||
void HandleInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent) const;
|
||||
|
||||
/**
|
||||
* Checks if an input is allowed.
|
||||
* 检查输入是否被允许。
|
||||
* @param IC The input system component. 输入系统组件。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
* @return True if the input is allowed, false otherwise. 如果输入被允许则返回true,否则返回false。
|
||||
*/
|
||||
bool CheckInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Determines if debugging is enabled for the input.
|
||||
* 确定是否为输入启用调试。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
* @return True if debugging is enabled, false otherwise. 如果启用调试则返回true,否则返回false。
|
||||
*/
|
||||
bool ShouldDebug(const FGameplayTag& InputTag, const ETriggerEvent& TriggerEvent) const;
|
||||
|
||||
/**
|
||||
* Internal logic for checking input validity.
|
||||
* 检查输入有效性的内部逻辑。
|
||||
* @param IC The input system component. 输入系统组件。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
* @return True if the input is valid, false otherwise. 如果输入有效则返回true,否则返回false。
|
||||
*/
|
||||
virtual bool InternalCheckInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent);
|
||||
|
||||
/**
|
||||
* Filters input processors based on the input tag and trigger event.
|
||||
* 根据输入标签和触发事件过滤输入处理器。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
* @return Array of filtered input processors. 过滤后的输入处理器数组。
|
||||
*/
|
||||
TArray<TObjectPtr<UGIPS_InputProcessor>> FilterInputProcessors(const FGameplayTag& InputTag, const ETriggerEvent& TriggerEvent) const;
|
||||
|
||||
/**
|
||||
* List of input events will bypass all the input checkers.
|
||||
* 此列表中的输入时间会绕过所有的输入检查器。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input", meta=(TitleProperty="InputTag"))
|
||||
TArray<FGIPS_AllowedInput> AlwaysAllowedInputs;
|
||||
|
||||
/**
|
||||
* List of input checkers to validate input events.
|
||||
* 验证输入事件的一组输入检查器。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Instanced, Category="GIPS|Input")
|
||||
TArray<TObjectPtr<UGIPS_InputChecker>> InputCheckers;
|
||||
|
||||
/**
|
||||
* If enabled, disallowed inputs are attempted to be stored in the input buffer.
|
||||
* 如果启用,不允许的输入将尝试存储在输入缓冲区中。
|
||||
* @attention Input buffering may not be needed for some setups, like UI. 对于某些设置(如UI),可能不需要输入缓冲。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input")
|
||||
bool bEnableInputBuffer{false};
|
||||
|
||||
/**
|
||||
* Controls the execution order of input processors for a single input event.
|
||||
* 控制单个输入事件的处理器执行顺序。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input")
|
||||
EGIPS_InputProcessorExecutionType InputProcessorExecutionType{EGIPS_InputProcessorExecutionType::MatchAll};
|
||||
|
||||
/**
|
||||
* List of input processors to handle allowed input events sequentially.
|
||||
* 处理允许的输入事件的一组输入处理器,按顺序执行。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Instanced, Category="GIPS|Input", meta=(TitleProperty="EditorFriendlyName", ShowOnlyInnerProperties))
|
||||
TArray<TObjectPtr<UGIPS_InputProcessor>> InputProcessors;
|
||||
|
||||
/**
|
||||
* Enables debug logging for input events.
|
||||
* 为输入事件启用调试日志。
|
||||
* @attention Requires LogGIPS to be set to VeryVerbose to take effect. 需要将LogGIPS设置为VeryVerbose才能生效。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Debug")
|
||||
bool bEnableInputDebug{false};
|
||||
|
||||
/**
|
||||
* Input tags to debug (logs all tags if empty).
|
||||
* 要调试的输入标签(如果为空则记录所有标签)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Debug", meta=(EditCondition="bEnableInputDebug", Categories="InputTag,GIPS.InputTag"))
|
||||
FGameplayTagContainer DebugInputTags{};
|
||||
|
||||
/**
|
||||
* Trigger events to debug (logs all events if empty).
|
||||
* 要调试的触发事件(如果为空则记录所有事件)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Debug", meta=(EditCondition="bEnableInputDebug"))
|
||||
TArray<ETriggerEvent> DebugTriggerEvents{ETriggerEvent::Started, ETriggerEvent::Completed};
|
||||
|
||||
public:
|
||||
#if WITH_EDITOR
|
||||
/**
|
||||
* Validates the data asset in the editor.
|
||||
* 在编辑器中验证数据资产。
|
||||
* @param Context The data validation context. 数据验证上下文。
|
||||
* @return The validation result. 验证结果。
|
||||
*/
|
||||
virtual EDataValidationResult IsDataValid(FDataValidationContext& Context) const override;
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "InputAction.h"
|
||||
#include "InputActionValue.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "GIPS_InputFunctionLibrary.generated.h"
|
||||
|
||||
/**
|
||||
* Utility functions for the Generic Input System.
|
||||
* 通用输入系统的实用功能。
|
||||
*/
|
||||
UCLASS()
|
||||
class GENERICINPUTSYSTEM_API UGIPS_InputFunctionLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Retrieves the input action value from the action instance.
|
||||
* 从输入动作实例中获取输入动作值。
|
||||
* @param ActionDataData The input action instance data. 输入动作实例数据。
|
||||
* @return The input action value. 输入动作值。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GIPS|Input", meta = (BlueprintAutocast))
|
||||
static FInputActionValue GetInputActionValue(const FInputActionInstance& ActionDataData);
|
||||
|
||||
/**
|
||||
* Gets the last tag name from a gameplay tag.
|
||||
* 从游戏标签中获取最后一个标签名称。
|
||||
* @param Tag The gameplay tag. 游戏标签。
|
||||
* @return The last tag name. 最后一个标签名称。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GIPS|Utilities", meta=(DisplayName="Get Last Tag Name(GIPS)"))
|
||||
static FName GetLastTagName(FGameplayTag Tag);
|
||||
|
||||
/**
|
||||
* Converts a gameplay tag container to a simple string.
|
||||
* 将游戏标签容器转换为简单字符串。
|
||||
* @param Tags The gameplay tag container. 游戏标签容器。
|
||||
* @return The string representation of the tags. 标签的字符串表示。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GIPS|Utilities", meta=(DisplayName="Get Simple String Of Tags(GIPS)"))
|
||||
static FString GetSimpleStringOfTags(FGameplayTagContainer Tags);
|
||||
|
||||
/**
|
||||
* Gets an array of last tag names from a tag container.
|
||||
* 从标签容器中获取最后一个标签名称的数组。
|
||||
* @param Tags The gameplay tag container. 游戏标签容器。
|
||||
* @return Array of last tag names. 最后一个标签名称的数组。
|
||||
*/
|
||||
static TArray<FName> GetLastTagNameArray(FGameplayTagContainer Tags);
|
||||
|
||||
/**
|
||||
* Gets a string of last tag names from a tag container.
|
||||
* 从标签容器中获取最后一个标签名称的字符串。
|
||||
* @param Tags The gameplay tag container. 游戏标签容器。
|
||||
* @return String of last tag names. 最后一个标签名称的字符串。
|
||||
*/
|
||||
static FString GetLastTagNameString(FGameplayTagContainer Tags);
|
||||
|
||||
/**
|
||||
* Gets a description of a gameplay tag query.
|
||||
* 获取游戏标签查询的描述。
|
||||
* @param TagQuery The gameplay tag query. 游戏标签查询。
|
||||
* @return The description of the tag query. 标签查询的描述。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GIPS|Utilities", meta=(DisplayName="Get TagQuery Description(GIPS)"))
|
||||
static FString GetTagQueryDescription(const FGameplayTagQuery& TagQuery);
|
||||
|
||||
/**
|
||||
* Converts a trigger event to a string.
|
||||
* 将触发事件转换为字符串。
|
||||
* @param TriggerEvent The trigger event. 触发事件。
|
||||
* @return The string representation of the trigger event. 触发事件的字符串表示。
|
||||
*/
|
||||
static FString GetTriggerEventString(ETriggerEvent TriggerEvent);
|
||||
};
|
||||
@@ -0,0 +1,193 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "InputAction.h"
|
||||
#include "InputActionValue.h"
|
||||
#include "GIPS_InputProcessor.generated.h"
|
||||
|
||||
class UGIPS_InputChecker;
|
||||
struct FInputActionInstance;
|
||||
enum class ETriggerEvent : uint8;
|
||||
class UGIPS_InputSystemComponent;
|
||||
|
||||
/**
|
||||
* Base class for processing input actions.
|
||||
* 处理输入动作的基类。
|
||||
*/
|
||||
UCLASS(EditInlineNew, DefaultToInstanced, CollapseCategories, Blueprintable, Const, HideDropdown)
|
||||
class GENERICINPUTSYSTEM_API UGIPS_InputProcessor : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UGIPS_InputProcessor();
|
||||
|
||||
/**
|
||||
* Indicates if the processor supports networking.
|
||||
* 指示处理器是否支持网络。
|
||||
* @return True if networking is supported, false otherwise. 如果支持网络则返回true,否则返回false。
|
||||
*/
|
||||
virtual bool IsSupportedForNetworking() const override { return true; }
|
||||
|
||||
/**
|
||||
* Checks if the processor can handle the given input.
|
||||
* 检查处理器是否可以处理给定的输入。
|
||||
* @param IC The input system component. 输入系统组件。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
* @return True if the input can be handled, false otherwise. 如果可以处理输入则返回true,否则返回false。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|Input", meta=(AutoCreateRefTerm="ActionData"))
|
||||
bool CanHandleInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, UPARAM(meta = (Categories="InputTag,GIPS.InputTag"))
|
||||
FGameplayTag InputTag,
|
||||
ETriggerEvent TriggerEvent) const;
|
||||
|
||||
/**
|
||||
* Handles the input action.
|
||||
* 处理输入动作。
|
||||
* @param IC The input system component. 输入系统组件。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|Input", meta=(AutoCreateRefTerm="ActionData"))
|
||||
void HandleInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, UPARAM(meta = (Categories="InputTag,GIPS.InputTag"))
|
||||
FGameplayTag InputTag,
|
||||
ETriggerEvent TriggerEvent) const;
|
||||
|
||||
/**
|
||||
* Tags that this processor responds to (if empty, responds to all inputs).
|
||||
* 处理器响应的输入标签(如果为空,则响应所有输入)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="InputProcessor", meta=(Categories="InputTag,GIPS.InputTag", DisplayPriority=0))
|
||||
FGameplayTagContainer InputTags;
|
||||
|
||||
/**
|
||||
* Trigger events that this processor responds to.
|
||||
* 处理器响应的触发事件。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="InputProcessor", meta=(DisplayPriority=1))
|
||||
TArray<ETriggerEvent> TriggerEvents{ETriggerEvent::Started};
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Gets the input action value (deprecated).
|
||||
* 获取输入动作值(已弃用)。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @return The input action value. 输入动作值。
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category="InputProcessor", meta=(DeprecatedFunction, DeprecationMessage="Use GetInputActionValueOfInputTag"))
|
||||
FInputActionValue GetInputActionValue(const FInputActionInstance& ActionData) const;
|
||||
|
||||
/**
|
||||
* Blueprint-implementable check for input handling.
|
||||
* 可通过蓝图实现的输入处理检查。
|
||||
* @param IC The input system component. 输入系统组件。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
* @return True if the input can be handled, false otherwise. 如果可以处理输入则返回true,否则返回false。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="InputProcessor")
|
||||
bool CheckCanHandleInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag, ETriggerEvent TriggerEvent) const;
|
||||
|
||||
/**
|
||||
* Native implementation of input handling check.
|
||||
* 输入处理检查的原生实现。
|
||||
*/
|
||||
virtual bool CheckCanHandleInput_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag, ETriggerEvent TriggerEvent) const;
|
||||
|
||||
/**
|
||||
* Handles the triggered input event.
|
||||
* 处理触发的输入事件。
|
||||
* @param IC The input system component. 输入系统组件。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="InputProcessor")
|
||||
void HandleInputTriggered(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const;
|
||||
|
||||
/**
|
||||
* Handles the started input event.
|
||||
* 处理开始的输入事件。
|
||||
* @param IC The input system component. 输入系统组件。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="InputProcessor")
|
||||
void HandleInputStarted(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const;
|
||||
|
||||
/**
|
||||
* Handles the ongoing input event.
|
||||
* 处理持续的输入事件。
|
||||
* @param IC The input system component. 输入系统组件。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="InputProcessor")
|
||||
void HandleInputOngoing(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const;
|
||||
|
||||
/**
|
||||
* Handles the canceled input event.
|
||||
* 处理取消的输入事件。
|
||||
* @param IC The input system component. 输入系统组件。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="InputProcessor")
|
||||
void HandleInputCanceled(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const;
|
||||
|
||||
/**
|
||||
* Handles the completed input event.
|
||||
* 处理完成的输入事件。
|
||||
* @param IC The input system component. 输入系统组件。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="InputProcessor")
|
||||
void HandleInputCompleted(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const;
|
||||
|
||||
/**
|
||||
* Gets a friendly name for the editor.
|
||||
* 获取编辑器友好的名称。
|
||||
* @return The editor-friendly name. 编辑器友好的名称。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="InputProcessor")
|
||||
FString GetEditorFriendlyName() const;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/**
|
||||
* Friendly name for displaying in the editor.
|
||||
* 在编辑器中显示的友好名称。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category=AlwaysHidden, Meta=(EditCondition=False, EditConditionHides))
|
||||
FString EditorFriendlyName;
|
||||
|
||||
/**
|
||||
* Description for developers in the editor.
|
||||
* 编辑器中用于开发者的描述。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category = "Editor")
|
||||
FString DevDescription;
|
||||
#endif
|
||||
|
||||
#if WITH_EDITOR
|
||||
/**
|
||||
* Native implementation to get editor-friendly name.
|
||||
* 获取编辑器友好名称的原生实现。
|
||||
* @return The editor-friendly name. 编辑器友好的名称。
|
||||
*/
|
||||
FString NativeGetEditorFriendlyName() const;
|
||||
|
||||
/**
|
||||
* Called before saving the object.
|
||||
* 在保存对象之前调用。
|
||||
* @param SaveContext The save context. 保存上下文。
|
||||
*/
|
||||
virtual void PreSave(FObjectPreSaveContext SaveContext) override;
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,550 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "EnhancedInputComponent.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "GIPS_InputProcessor.h"
|
||||
#include "GIPS_InputTypes.h"
|
||||
#include "GIPS_InputSystemComponent.generated.h"
|
||||
|
||||
class UGIPS_InputControlSetup;
|
||||
class UInputMappingContext;
|
||||
class UGIPS_InputConfig;
|
||||
class UEnhancedInputLocalPlayerSubsystem;
|
||||
|
||||
/**
|
||||
* Core component for handling input in the Generic Input System.
|
||||
* 通用输入系统中处理输入的核心组件。
|
||||
* @attention Must be attached to a Pawn or PlayerController. 必须挂载到Pawn或PlayerController上。
|
||||
*/
|
||||
UCLASS(ClassGroup=GIPS, Blueprintable, meta=(BlueprintSpawnableComponent), AutoExpandCategories=("GIPS"))
|
||||
class GENERICINPUTSYSTEM_API UGIPS_InputSystemComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
friend UGIPS_InputControlSetup;
|
||||
|
||||
public:
|
||||
enum class EGIPS_OwnerType:uint8
|
||||
{
|
||||
Pawn,
|
||||
PC,
|
||||
};
|
||||
|
||||
public:
|
||||
UGIPS_InputSystemComponent(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
//~ Begin UActorComponent interface
|
||||
/**
|
||||
* Called when the component is registered.
|
||||
* 组件注册时调用。
|
||||
*/
|
||||
virtual void OnRegister() override;
|
||||
|
||||
/**
|
||||
* Called when the component is unregistered.
|
||||
* 组件取消注册时调用。
|
||||
*/
|
||||
virtual void OnUnregister() override;
|
||||
//~ End UActorComponent interface
|
||||
|
||||
/**
|
||||
* Gets the Pawn associated with this component.
|
||||
* 获取与此组件关联的Pawn。
|
||||
* @return The associated Pawn, or the Pawn controlled by the PlayerController if the owner is a PlayerController. 如果拥有者是Pawn则返回Pawn,如果是PlayerController则返回其控制的Pawn。
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category="GIPS|Input")
|
||||
APawn* GetControlledPawn() const;
|
||||
|
||||
/**
|
||||
* Gets the input system component from an actor.
|
||||
* 从演员获取输入系统组件。
|
||||
* @param Actor The actor to query. 要查询的演员。
|
||||
* @return The input system component, or nullptr if not found. 输入系统组件,如果未找到则返回nullptr。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GIPS|Input", Meta = (DefaultToSelf="Actor"))
|
||||
static UGIPS_InputSystemComponent* GetInputSystemComponent(const AActor* Actor);
|
||||
|
||||
/**
|
||||
* Finds the input system component on an actor.
|
||||
* 在演员上查找输入系统组件。
|
||||
* @param Actor The actor to query. 要查询的演员。
|
||||
* @param Component The found component (output). 找到的组件(输出)。
|
||||
* @return True if the component was found, false otherwise. 如果找到组件则返回true,否则返回false。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "GIPS|Input", Meta = (DefaultToSelf="Actor", ExpandBoolAsExecs="ReturnValue"))
|
||||
static bool FindInputSystemComponent(const AActor* Actor, UGIPS_InputSystemComponent*& Component);
|
||||
|
||||
/**
|
||||
* Event triggered when setting up the input component.
|
||||
* 设置输入组件时触发的事件。
|
||||
*/
|
||||
UPROPERTY(BlueprintAssignable, Category="GIPS|Input")
|
||||
FGIPS_InputComponentSignature SetupInputComponentEvent;
|
||||
|
||||
/**
|
||||
* Event triggered when cleaning up the input component.
|
||||
* 清理输入组件时触发的事件。
|
||||
*/
|
||||
UPROPERTY(BlueprintAssignable, Category="GIPS|Input")
|
||||
FGIPS_InputComponentSignature CleanupInputComponentEvent;
|
||||
|
||||
/**
|
||||
* Event triggered when the input buffer window state changes.
|
||||
* 输入缓冲窗口状态改变时触发的事件。
|
||||
*/
|
||||
UPROPERTY(BlueprintAssignable, Category="GIPS|Input")
|
||||
FGIPS_InputBufferWindowStateChangedSignature InputBufferWindowStateChangedEvent;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Sets up the player input component (Blueprint-implementable).
|
||||
* 设置玩家输入组件(可通过蓝图实现)。
|
||||
* @param NewInputComponent The new input component. 新输入组件。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="GIPS|Input")
|
||||
void OnSetupPlayerInputComponent(UEnhancedInputComponent* NewInputComponent);
|
||||
virtual void OnSetupPlayerInputComponent_Implementation(UEnhancedInputComponent* NewInputComponent);
|
||||
|
||||
/**
|
||||
* Cleans up the player input component (Blueprint-implementable).
|
||||
* 清理玩家输入组件(可通过蓝图实现)。
|
||||
* @param PrevInputComponent The previous input component. 前一个输入组件。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="GIPS|Input")
|
||||
void OnCleanupPlayerInputComponent(UEnhancedInputComponent* PrevInputComponent);
|
||||
virtual void OnCleanupPlayerInputComponent_Implementation(UEnhancedInputComponent* PrevInputComponent);
|
||||
|
||||
/**
|
||||
* Called when the pawn restarts.
|
||||
* 当Pawn重启时调用。
|
||||
* @param Pawn The pawn that restarted. 重启的Pawn。
|
||||
*/
|
||||
UFUNCTION()
|
||||
virtual void OnPawnRestarted(APawn* Pawn);
|
||||
|
||||
/**
|
||||
* Called when the controller changes.
|
||||
* 当控制器更改时调用。
|
||||
* @param Pawn The associated pawn. 关联的Pawn。
|
||||
* @param OldController The previous controller. 前一个控制器。
|
||||
* @param NewController The new controller. 新控制器。
|
||||
*/
|
||||
UFUNCTION()
|
||||
virtual void OnControllerChanged(APawn* Pawn, AController* OldController, AController* NewController);
|
||||
|
||||
/**
|
||||
* Cleans up input action value bindings.
|
||||
* 清理输入动作值绑定。
|
||||
*/
|
||||
void CleanInputActionValueBindings();
|
||||
|
||||
/**
|
||||
* Sets up input action value bindings.
|
||||
* 设置输入动作值绑定。
|
||||
*/
|
||||
void SetupInputActionValueBindings();
|
||||
|
||||
/**
|
||||
* Sets up the input component.
|
||||
* 设置输入组件。
|
||||
* @param NewInputComponent The new input component. 新输入组件。
|
||||
*/
|
||||
virtual void SetupInputComponent(UInputComponent* NewInputComponent);
|
||||
|
||||
/**
|
||||
* Cleans up the input component.
|
||||
* 清理输入组件。
|
||||
* @param OldController The previous controller (optional). 前一个控制器(可选)。
|
||||
*/
|
||||
virtual void CleanupInputComponent(AController* OldController = nullptr);
|
||||
|
||||
/**
|
||||
* Gets the enhanced input subsystem.
|
||||
* 获取增强输入子系统。
|
||||
* @param OldController The previous controller (optional). 前一个控制器(可选)。
|
||||
* @return The enhanced input subsystem. 增强输入子系统。
|
||||
*/
|
||||
UEnhancedInputLocalPlayerSubsystem* GetEnhancedInputSubsystem(AController* OldController = nullptr) const;
|
||||
|
||||
/**
|
||||
* The bound input component.
|
||||
* 绑定的输入组件。
|
||||
*/
|
||||
UPROPERTY(transient)
|
||||
TObjectPtr<UEnhancedInputComponent> InputComponent;
|
||||
|
||||
/**
|
||||
* The type of owner (Pawn or PlayerController).
|
||||
* 拥有者类型(Pawn或PlayerController)。
|
||||
*/
|
||||
EGIPS_OwnerType OwnerType = EGIPS_OwnerType::PC;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Binds input actions to the component.
|
||||
* 将输入动作绑定到组件。
|
||||
*/
|
||||
void BindInputActions();
|
||||
|
||||
/**
|
||||
* Input mapping context to be added to the enhanced input subsystem.
|
||||
* 将添加到增强输入子系统的输入映射上下文。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GIPS|Input")
|
||||
TObjectPtr<UInputMappingContext> InputMappingContext;
|
||||
|
||||
/**
|
||||
* Priority for binding the input mapping context.
|
||||
* 绑定输入映射上下文的优先级。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GIPS|Input")
|
||||
int32 InputPriority = 0;
|
||||
|
||||
/**
|
||||
* Main configuration for the input system.
|
||||
* 输入系统的主配置。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, Category="GIPS|Input")
|
||||
TObjectPtr<UGIPS_InputConfig> InputConfig;
|
||||
|
||||
/**
|
||||
* Maximum number of input entries to keep.
|
||||
* 保留的最大输入记录数。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, Category="GIPS|Input", meta=(ClampMin=0, ClampMax=10))
|
||||
int32 MaxInputEntriesNum{5};
|
||||
|
||||
/**
|
||||
* If true, inputs are processed externally via OnReceivedInput event.
|
||||
* 如果为true,输入通过OnReceivedInput事件在外部处理。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, Category="GIPS|Input")
|
||||
bool bProcessingInputExternally{false};
|
||||
|
||||
/**
|
||||
* List of input control setups, with the last one being the current setup.
|
||||
* 输入控制设置列表,最后一个为当前设置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input")
|
||||
TArray<TObjectPtr<UGIPS_InputControlSetup>> InputControlSetups;
|
||||
|
||||
public:
|
||||
#pragma region InputControl
|
||||
/**
|
||||
* Gets the current input control setup.
|
||||
* 获取当前输入控制设置。
|
||||
* @return The last input control setup in the list. 列表中的最后一个输入控制设置。
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category="GIPS|Input")
|
||||
UGIPS_InputControlSetup* GetCurrentInputSetup() const;
|
||||
|
||||
/**
|
||||
* Gets the input configuration.
|
||||
* 获取输入配置。
|
||||
* @return The input configuration. 输入配置。
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category="GIPS|Input")
|
||||
UGIPS_InputConfig* GetInputConfig() const;
|
||||
|
||||
/**
|
||||
* Sets a new input control setup as the current one.
|
||||
* 将新的输入控制设置设为当前设置。
|
||||
* @param NewSetup The new input control setup. 新输入控制设置。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|Input")
|
||||
virtual void PushInputSetup(UGIPS_InputControlSetup* NewSetup);
|
||||
|
||||
/**
|
||||
* Removes the current input setup, making the last one in the list the new current setup.
|
||||
* 删除当前输入设置,使列表中的最后一个成为新的当前设置。
|
||||
* @attention Does nothing if only one setup exists. 如果只有一个设置,则无操作。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|Input")
|
||||
virtual void PopInputSetup();
|
||||
|
||||
#pragma endregion
|
||||
|
||||
/**
|
||||
* Checks if an input is allowed.
|
||||
* 检查输入是否被允许。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
* @return True if the input is allowed, false otherwise. 如果输入被允许则返回true,否则返回false。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|Input", meta=(ExpandBoolAsExecs="ReturnValue"))
|
||||
virtual bool CheckInputAllowed(UPARAM(meta = (Categories="InputTag,GIPS.InputTag"))
|
||||
FGameplayTag InputTag, ETriggerEvent TriggerEvent);
|
||||
|
||||
/**
|
||||
* Checks if an input is allowed with action data.
|
||||
* 使用动作数据检查输入是否被允许。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
* @return True if the input is allowed, false otherwise. 如果输入被允许则返回true,否则返回false。
|
||||
*/
|
||||
// UFUNCTION(BlueprintCallable, Category="GIPS|Input", meta=(ExpandBoolAsExecs="ReturnValue", AutoCreateRefTerm="ActionData"))
|
||||
virtual bool CheckInputAllowed(const FInputActionInstance& ActionData, UPARAM(meta = (Categories="InputTag,GIPS.InputTag"))
|
||||
FGameplayTag InputTag, ETriggerEvent TriggerEvent);
|
||||
|
||||
/**
|
||||
* Processes an input action.
|
||||
* 处理输入动作。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
*/
|
||||
// UFUNCTION(BlueprintCallable, Category="GIPS|Input")
|
||||
virtual void ProcessInput(const FInputActionInstance& ActionData, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent);
|
||||
|
||||
/**
|
||||
* Gets the input action associated with a tag.
|
||||
* 获取与标签关联的输入动作。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @return The associated input action. 关联的输入动作。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|Input")
|
||||
UInputAction* GetInputActionOfInputTag(UPARAM(meta = (Categories="InputTag,GIPS.InputTag"))
|
||||
FGameplayTag InputTag) const;
|
||||
|
||||
/**
|
||||
* Gets the current input action value for a tag.
|
||||
* 获取标签的当前输入动作值。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @return The current input action value. 当前输入动作值。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|Input")
|
||||
FInputActionValue GetInputActionValueOfInputTag(UPARAM(meta = (Categories="InputTag,GIPS.InputTag"))
|
||||
FGameplayTag InputTag) const;
|
||||
|
||||
/**
|
||||
* Gets the last input action value for a tag.
|
||||
* 获取标签的最后输入动作值。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @return The last input action value. 最后输入动作值。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|Input")
|
||||
FInputActionValue GetLastInputActionValueOfInputTag(UPARAM(meta = (Categories="InputTag,GIPS.InputTag"))
|
||||
FGameplayTag InputTag) const;
|
||||
|
||||
/**
|
||||
* Gets the list of passed input entries.
|
||||
* 获取通过的输入记录列表。
|
||||
* @return Array of passed input entries. 通过的输入记录数组。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|Input")
|
||||
TArray<FGIPS_BufferedInput> GetPassedInputEntries() const { return PassedInputEntries; };
|
||||
|
||||
/**
|
||||
* Registers a passed input entry.
|
||||
* 注册通过的输入记录。
|
||||
* @param InputEntry The input entry to register. 要注册的输入记录。
|
||||
*/
|
||||
virtual void RegisterPassedInputEntry(const FGIPS_BufferedInput& InputEntry);
|
||||
|
||||
/**
|
||||
* Gets the list of blocked input entries.
|
||||
* 获取阻止的输入记录列表。
|
||||
* @return Array of blocked input entries. 阻止的输入记录数组。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|Input")
|
||||
TArray<FGIPS_BufferedInput> GetBlockedInputEntries() const { return BlockedInputEntries; };
|
||||
|
||||
/**
|
||||
* Registers a blocked input entry.
|
||||
* 注册阻止的输入记录。
|
||||
* @param InputEntry The input entry to register. 要注册的输入记录。
|
||||
*/
|
||||
virtual void RegisterBlockedInputEntry(const FGIPS_BufferedInput& InputEntry);
|
||||
|
||||
/**
|
||||
* Gets the list of buffered input entries.
|
||||
* 获取缓冲的输入记录列表。
|
||||
* @return Array of buffered input entries. 缓冲的输入记录数组。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|Input")
|
||||
TArray<FGIPS_BufferedInput> GetBufferedInputEntries() const { return BufferedInputEntries; };
|
||||
|
||||
/**
|
||||
* Registers a buffered input entry.
|
||||
* 注册缓冲的输入记录。
|
||||
* @param InputEntry The input entry to register. 要注册的输入记录。
|
||||
*/
|
||||
virtual void RegisterBufferedInputEntry(const FGIPS_BufferedInput& InputEntry);
|
||||
|
||||
/**
|
||||
* Event triggered when an input action generates an event.
|
||||
* 输入动作生成事件时触发的事件。
|
||||
*/
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FGIPS_ReceivedInputSignature OnReceivedInput;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Callback for input action events.
|
||||
* 输入动作事件的回调。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
*/
|
||||
UFUNCTION()
|
||||
void InputActionCallback(const FInputActionInstance& ActionData, FGameplayTag InputTag, ETriggerEvent TriggerEvent);
|
||||
|
||||
/**
|
||||
* Mapping of input tags to action value bindings.
|
||||
* 输入标签到动作值绑定的映射。
|
||||
*/
|
||||
UPROPERTY(VisibleInstanceOnly, Category="GIPS|Input", meta=(ForceInlineRow))
|
||||
TMap<FGameplayTag, int32> InputActionValueBindings;
|
||||
|
||||
/**
|
||||
* Tracks the last action value for each input action.
|
||||
* 跟踪每个输入动作的最后动作值。
|
||||
*/
|
||||
UPROPERTY(VisibleInstanceOnly, Category="GIPS|Input", meta=(ForceInlineRow))
|
||||
TMap<FGameplayTag, FInputActionValue> LastInputActionValues;
|
||||
|
||||
/**
|
||||
* List of passed input entries.
|
||||
* 通过的输入记录列表。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category="GIPS|Input")
|
||||
TArray<FGIPS_BufferedInput> PassedInputEntries;
|
||||
|
||||
/**
|
||||
* List of blocked input entries.
|
||||
* 阻止的输入记录列表。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category="GIPS|Input")
|
||||
TArray<FGIPS_BufferedInput> BlockedInputEntries;
|
||||
|
||||
/**
|
||||
* List of buffered input entries.
|
||||
* 缓冲的输入记录列表。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category="GIPS|Input")
|
||||
TArray<FGIPS_BufferedInput> BufferedInputEntries;
|
||||
|
||||
#pragma region InputBuffer
|
||||
|
||||
public:
|
||||
/**
|
||||
* Attempts to save an input to the buffer.
|
||||
* 尝试将输入保存到缓冲区。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
* @return True if the input was saved, false otherwise. 如果输入被保存则返回true,否则返回false。
|
||||
*/
|
||||
bool TrySaveInput(const FInputActionInstance& ActionData, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent);
|
||||
|
||||
/**
|
||||
* Fires buffered input actions.
|
||||
* 触发缓冲的输入动作。
|
||||
*/
|
||||
void FireBufferedInput();
|
||||
|
||||
/**
|
||||
* Event triggered when buffered input is fired.
|
||||
* 缓冲输入触发时的事件。
|
||||
*/
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FGIPS_ReceivedInputSignature OnFireBufferedInput;
|
||||
|
||||
/**
|
||||
* Opens an input buffer window to allow input saving.
|
||||
* 开启输入缓冲窗口以允许保存输入。
|
||||
* @param BufferWindowName The name of the buffer window to open. 要开启的缓冲窗口名称。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|InputBuffer")
|
||||
virtual void OpenInputBufferWindow(UPARAM(meta=(Categories="GIPS.InputBuffer"))
|
||||
FGameplayTag BufferWindowName);
|
||||
|
||||
/**
|
||||
* Closes an input buffer window.
|
||||
* 关闭输入缓冲窗口。
|
||||
* @param BufferWindowName The name of the buffer window to close. 要关闭的缓冲窗口名称。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|InputBuffer")
|
||||
virtual void CloseInputBufferWindow(UPARAM(meta=(Categories="GIPS.InputBuffer"))
|
||||
FGameplayTag BufferWindowName);
|
||||
|
||||
/**
|
||||
* Closes all active input buffer windows.
|
||||
* 关闭所有激活的输入缓冲窗口。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GIPS|InputBuffer")
|
||||
virtual void CloseActiveInputBufferWindows();
|
||||
|
||||
/**
|
||||
* Gets the last buffered input.
|
||||
* 获取最后缓冲的输入。
|
||||
* @return The last buffered input. 最后缓冲的输入。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GIPS|InputBuffer")
|
||||
FGIPS_BufferedInput GetLastBufferedInput() const;
|
||||
|
||||
/**
|
||||
* Gets all currently active buffer windows.
|
||||
* 获取当前所有激活的缓冲窗口。
|
||||
* @return Map of active buffer windows. 激活的缓冲窗口映射。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GIPS|InputBuffer")
|
||||
TMap<FGameplayTag, FGIPS_BufferedInput> GetActiveBufferWindows() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Resets the buffered input.
|
||||
* 重置缓冲输入。
|
||||
*/
|
||||
void ResetBufferedInput();
|
||||
|
||||
/**
|
||||
* Attempts to save an input as a buffered input.
|
||||
* 尝试将输入保存为缓冲输入。
|
||||
* @param BufferWindowName The buffer window name. 缓冲窗口名称。
|
||||
* @param ActionData The input action data. 输入动作数据。
|
||||
* @param InputTag The gameplay tag for the input. 输入的游戏标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
* @return True if the input was saved, false otherwise. 如果输入被保存则返回true,否则返回false。
|
||||
*/
|
||||
bool TrySaveAsBufferedInput(const FGameplayTag BufferWindowName, const FInputActionInstance& ActionData, const FGameplayTag& InputTag, ETriggerEvent TriggerEvent);
|
||||
|
||||
/**
|
||||
* Map of currently active input buffer windows.
|
||||
* 当前激活的输入缓冲窗口映射。
|
||||
*/
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category="GIPS|InputBuffer", meta=(ForceInlineRow))
|
||||
TMap<FGameplayTag, FGIPS_BufferedInput> ActiveBufferWindows;
|
||||
|
||||
/**
|
||||
* The last fired buffered input.
|
||||
* 最后触发的缓冲输入。
|
||||
*/
|
||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category="GIPS|InputBuffer")
|
||||
FGIPS_BufferedInput LastBufferedInput;
|
||||
|
||||
/**
|
||||
* The current buffered input.
|
||||
* 当前缓冲输入。
|
||||
*/
|
||||
UPROPERTY()
|
||||
FGIPS_BufferedInput CurrentBufferedInput;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region DataValidation
|
||||
#if WITH_EDITOR
|
||||
/**
|
||||
* Validates the component data in the editor.
|
||||
* 在编辑器中验证组件数据。
|
||||
* @param Context The data validation context. 数据验证上下文。
|
||||
* @return The validation result. 验证结果。
|
||||
*/
|
||||
virtual EDataValidationResult IsDataValid(FDataValidationContext& Context) const override;
|
||||
#endif
|
||||
#pragma endregion
|
||||
};
|
||||
372
Plugins/GCS/Source/GenericInputSystem/Public/GIPS_InputTypes.h
Normal file
372
Plugins/GCS/Source/GenericInputSystem/Public/GIPS_InputTypes.h
Normal file
@@ -0,0 +1,372 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "InputAction.h"
|
||||
#include "InputTriggers.h"
|
||||
#include "Templates/TypeHash.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "GIPS_InputTypes.generated.h"
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FGIPS_InputComponentSignature, UEnhancedInputComponent*, InputComponent);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FGIPS_InputBufferWindowStateChangedSignature, FGameplayTag, BufferWindowName, bool, State);
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FGIPS_ReceivedInputSignature, const FInputActionInstance&, ActionData, const FGameplayTag&, InputTag, ETriggerEvent, TriggerEvent);
|
||||
|
||||
enum class ETriggerEvent : uint8;
|
||||
class UGIPS_InputProcessor;
|
||||
|
||||
/**
|
||||
* Defines the type of input buffering behavior.
|
||||
* 定义输入缓冲行为的类型。
|
||||
*/
|
||||
UENUM()
|
||||
enum class EGIPS_InputBufferType : uint8
|
||||
{
|
||||
/**
|
||||
* Only the last input is executed when the buffer window closes.
|
||||
* 仅在缓冲窗口关闭时执行最后按下的输入。
|
||||
*/
|
||||
LastInput,
|
||||
|
||||
/**
|
||||
* Inputs are executed immediately during the buffer window.
|
||||
* 在缓冲窗口期间立即执行输入。
|
||||
*/
|
||||
Instant,
|
||||
|
||||
/**
|
||||
* Inputs with higher priority (earlier in the list) are executed first.
|
||||
* 优先级较高的输入(列表中较早的)优先执行。
|
||||
*/
|
||||
HighestPriority
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines how input processors are executed for a single input event.
|
||||
* 定义单个输入事件的处理器执行方式。
|
||||
*/
|
||||
UENUM()
|
||||
enum class EGIPS_InputProcessorExecutionType : uint8
|
||||
{
|
||||
/**
|
||||
* All valid processors are executed sequentially in top-to-bottom order.
|
||||
* 所有有效处理器按从上到下的顺序依次执行。
|
||||
*/
|
||||
MatchAll,
|
||||
|
||||
/**
|
||||
* Only the first valid input processor is executed.
|
||||
* 仅执行第一个有效的输入处理器。
|
||||
*/
|
||||
FirstOnly
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct for defining allowed inputs and their trigger events.
|
||||
* 定义允许的输入及其触发事件的结构体。
|
||||
*/
|
||||
USTRUCT()
|
||||
struct GENERICINPUTSYSTEM_API FGIPS_AllowedInput
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* The allowed input tag.
|
||||
* 允许的输入标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input", meta=(Categories="InputTag,GIPS.InputTag"))
|
||||
FGameplayTag InputTag;
|
||||
|
||||
/**
|
||||
* Allowed trigger event types (all allowed if empty).
|
||||
* 允许的触发事件类型(如果为空则允许所有)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input")
|
||||
TArray<ETriggerEvent> TriggerEvents{ETriggerEvent::Started};
|
||||
|
||||
/**
|
||||
* Equality operator for allowed inputs.
|
||||
* 允许输入的相等比较运算符。
|
||||
*/
|
||||
friend bool operator==(const FGIPS_AllowedInput& Lhs, const FGIPS_AllowedInput& RHS)
|
||||
{
|
||||
return Lhs.InputTag == RHS.InputTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inequality operator for allowed inputs.
|
||||
* 允许输入的不相等比较运算符。
|
||||
*/
|
||||
friend bool operator!=(const FGIPS_AllowedInput& Lhs, const FGIPS_AllowedInput& RHS)
|
||||
{
|
||||
return !(Lhs == RHS);
|
||||
}
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/**
|
||||
* Description for developers in the editor.
|
||||
* 编辑器中用于开发者的描述。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category = "Editor")
|
||||
FString DevDescription;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct for defining an input buffer window.
|
||||
* 定义输入缓冲窗口的结构体。
|
||||
*/
|
||||
USTRUCT()
|
||||
struct GENERICINPUTSYSTEM_API FGIPS_InputBufferWindow
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* The tag for the buffer window.
|
||||
* 缓冲窗口的标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input", meta=(Categories="GIPS.InputBuffer"))
|
||||
FGameplayTag Tag = FGameplayTag::EmptyTag;
|
||||
|
||||
/**
|
||||
* The type of input buffering behavior.
|
||||
* 输入缓冲行为的类型。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input")
|
||||
EGIPS_InputBufferType BufferType = EGIPS_InputBufferType::LastInput;
|
||||
|
||||
/**
|
||||
* List of allowed inputs for the buffer window.
|
||||
* 缓冲窗口允许的输入列表。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input", meta=(TitleProperty="InputTag"))
|
||||
TArray<FGIPS_AllowedInput> AllowedInputs;
|
||||
|
||||
/**
|
||||
* Finds the index of an allowed input.
|
||||
* 查找允许输入的索引。
|
||||
* @param InputTag The input tag to find. 要查找的输入标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
* @return The index of the allowed input, or INDEX_NONE if not found. 允许输入的索引,如果未找到则返回INDEX_NONE。
|
||||
*/
|
||||
int32 IndexOfAllowedInput(const FGameplayTag& InputTag, const ETriggerEvent& TriggerEvent) const
|
||||
{
|
||||
for (int32 i = 0; i < AllowedInputs.Num(); i++)
|
||||
{
|
||||
if (AllowedInputs[i].InputTag == InputTag && (AllowedInputs[i].TriggerEvents.IsEmpty() || AllowedInputs[i].TriggerEvents.Contains(TriggerEvent)))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Equality operator for buffer window tags.
|
||||
* 缓冲窗口标签的相等比较运算符。
|
||||
*/
|
||||
bool operator==(const FGameplayTag& OtherTag) const;
|
||||
|
||||
/**
|
||||
* Inequality operator for buffer window tags.
|
||||
* 缓冲窗口标签的不相等比较运算符。
|
||||
*/
|
||||
bool operator!=(const FGameplayTag& OtherTag) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct for storing a single input entry.
|
||||
* 存储单个输入记录的结构体。
|
||||
*/
|
||||
USTRUCT(BlueprintType, meta=(DisplayName="GIPS Input Entry"))
|
||||
struct GENERICINPUTSYSTEM_API FGIPS_BufferedInput
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* The input tag for the entry.
|
||||
* 输入记录的输入标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GIPS|Input", meta=(Categories="InputTag,GIPS.InputTag"))
|
||||
FGameplayTag InputTag{FGameplayTag::EmptyTag};
|
||||
|
||||
/**
|
||||
* The input action data.
|
||||
* 输入动作数据。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GIPS|Input")
|
||||
FInputActionInstance ActionData;
|
||||
|
||||
/**
|
||||
* The trigger event type.
|
||||
* 触发事件类型。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GIPS|Input")
|
||||
ETriggerEvent TriggerEvent{ETriggerEvent::None};
|
||||
|
||||
/**
|
||||
* Equality operator for buffered inputs.
|
||||
* 缓冲输入的相等比较运算符。
|
||||
*/
|
||||
friend bool operator==(const FGIPS_BufferedInput& Lhs, const FGIPS_BufferedInput& RHS)
|
||||
{
|
||||
return Lhs.InputTag == RHS.InputTag
|
||||
&& Lhs.TriggerEvent == RHS.TriggerEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inequality operator for buffered inputs.
|
||||
* 缓冲输入的不相等比较运算符。
|
||||
*/
|
||||
friend bool operator!=(const FGIPS_BufferedInput& Lhs, const FGIPS_BufferedInput& RHS)
|
||||
{
|
||||
return !(Lhs == RHS);
|
||||
}
|
||||
|
||||
FGIPS_BufferedInput() = default;
|
||||
|
||||
/**
|
||||
* Constructor for buffered input.
|
||||
* 缓冲输入的构造函数。
|
||||
*/
|
||||
FGIPS_BufferedInput(const FGameplayTag& InputTag, const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent)
|
||||
: InputTag(InputTag),
|
||||
ActionData(ActionData),
|
||||
TriggerEvent(TriggerEvent)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor for buffered input.
|
||||
* 缓冲输入的复制构造函数。
|
||||
*/
|
||||
FGIPS_BufferedInput(const FGIPS_BufferedInput& Other)
|
||||
: InputTag(Other.InputTag),
|
||||
ActionData(Other.ActionData),
|
||||
TriggerEvent(Other.TriggerEvent)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Assignment operator for buffered input.
|
||||
* 缓冲输入的赋值运算符。
|
||||
*/
|
||||
FGIPS_BufferedInput& operator=(const FGIPS_BufferedInput& Other)
|
||||
{
|
||||
if (this == &Other)
|
||||
return *this;
|
||||
InputTag = Other.InputTag;
|
||||
ActionData = Other.ActionData;
|
||||
TriggerEvent = Other.TriggerEvent;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the buffered input to a string.
|
||||
* 将缓冲输入转换为字符串。
|
||||
* @return The string representation of the buffered input. 缓冲输入的字符串表示。
|
||||
*/
|
||||
FString ToString() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct for defining input action settings.
|
||||
* 定义输入动作设置的结构体。
|
||||
*/
|
||||
USTRUCT()
|
||||
struct GENERICINPUTSYSTEM_API FGIPS_InputActionSetting
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* The input action object.
|
||||
* 输入动作对象。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input")
|
||||
TObjectPtr<UInputAction> InputAction;
|
||||
|
||||
/**
|
||||
* Enables value binding for the input action.
|
||||
* 为输入动作启用值绑定。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input")
|
||||
bool bValueBinding{true};
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct for defining relationships between actor states and input permissions.
|
||||
* 定义演员状态与输入权限关系的结构体。
|
||||
*/
|
||||
USTRUCT()
|
||||
struct GENERICINPUTSYSTEM_API FGIPS_InputTagRelationship
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Query to check if the actor's tags allow the input.
|
||||
* 检查演员标签是否允许输入的查询。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input")
|
||||
FGameplayTagQuery ActorTagQuery;
|
||||
|
||||
/**
|
||||
* List of allowed inputs if the tag query is satisfied.
|
||||
* 如果标签查询满足,则允许的输入列表。
|
||||
* @note Empty means no check on if input is allowed. 留空则不检查是否允许。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input", meta=(TitleProperty="InputTag"))
|
||||
TArray<FGIPS_AllowedInput> AllowedInputs;
|
||||
|
||||
/**
|
||||
* List of blocked inputs if the tag query is satisfied.
|
||||
* 如果标签查询满足,则禁止的输入列表。
|
||||
* @note Empty means no check on if input is blocked. 留空则不检查是否阻挡。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GIPS|Input", meta=(TitleProperty="InputTag"))
|
||||
TArray<FGIPS_AllowedInput> BlockedInputs;
|
||||
|
||||
/**
|
||||
* Finds the index of an allowed input.
|
||||
* 查找允许输入的索引。
|
||||
* @param InputTag The input tag to find. 要查找的输入标签。
|
||||
* @param TriggerEvent The trigger event type. 触发事件类型。
|
||||
* @return The index of the allowed input, or INDEX_NONE if not found. 允许输入的索引,如果未找到则返回INDEX_NONE。
|
||||
*/
|
||||
int32 IndexOfAllowedInput(const FGameplayTag& InputTag, const ETriggerEvent& TriggerEvent) const
|
||||
{
|
||||
for (int32 i = 0; i < AllowedInputs.Num(); i++)
|
||||
{
|
||||
if (AllowedInputs[i].InputTag == InputTag && (AllowedInputs[i].TriggerEvents.IsEmpty() || AllowedInputs[i].TriggerEvents.Contains(TriggerEvent)))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
int32 IndexOfBlockedInput(const FGameplayTag& InputTag, const ETriggerEvent& TriggerEvent) const
|
||||
{
|
||||
for (int32 i = 0; i < BlockedInputs.Num(); i++)
|
||||
{
|
||||
if (BlockedInputs[i].InputTag == InputTag && (BlockedInputs[i].TriggerEvents.IsEmpty() || BlockedInputs[i].TriggerEvents.Contains(TriggerEvent)))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/**
|
||||
* Friendly name for displaying in the editor.
|
||||
* 在编辑器中显示的友好名称。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category=AlwaysHidden, Meta=(EditCondition=False, EditConditionHides))
|
||||
FString EditorFriendlyName;
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Logging/LogMacros.h"
|
||||
|
||||
GENERICINPUTSYSTEM_API FString GetGIPSLogContextString(const UObject* ContextObject = nullptr);
|
||||
|
||||
|
||||
GENERICINPUTSYSTEM_API DECLARE_LOG_CATEGORY_EXTERN(LogGIPS, Log, All);
|
||||
|
||||
|
||||
#define GIPS_LOG(Verbosity, Format, ...) \
|
||||
{ \
|
||||
UE_LOG(LogGIPS, Verbosity, TEXT("%S: %s"),__FUNCTION__, *FString::Printf(TEXT(Format), ##__VA_ARGS__)) \
|
||||
}
|
||||
|
||||
#define GIPS_CLOG(Verbosity, Format, ...) \
|
||||
{ \
|
||||
UE_LOG(LogGIPS, Verbosity, TEXT("%S: ctx(%s) %s"),__FUNCTION__, *GetGIPSLogContextString(this), *FString::Printf(TEXT(Format), ##__VA_ARGS__)) \
|
||||
}
|
||||
|
||||
#define GIPS_OWNED_CLOG(LogOwner,Verbosity, Format, ...) \
|
||||
{ \
|
||||
UE_LOG(LogGIPS, Verbosity, TEXT("%S: ctx(%s) %s"),__FUNCTION__, *GetGIPSLogContextString(LogOwner), *FString::Printf(TEXT(Format), ##__VA_ARGS__)) \
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
class FGenericInputSystemModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
};
|
||||
Reference in New Issue
Block a user