Initial commit.
This commit is contained in:
commit
2e5aae7f67
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
Binaries/
|
||||
Intermediate/
|
||||
32
ComboInput.uplugin
Normal file
32
ComboInput.uplugin
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "0.1.0.0",
|
||||
"FriendlyName": "Combo Input",
|
||||
"Description": "A set of components and classes for capturing Enhanced Input actions for buffering actions and stringing them into complex combos.",
|
||||
"Category": "Input",
|
||||
"CreatedBy": "Jamie Greunbaum",
|
||||
"EngineVersion": "5.2.0",
|
||||
"CanContainContent": true,
|
||||
"IsBetaVersion": true,
|
||||
"IsExperimentalVersion": true,
|
||||
"Installed": true,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "ComboInput",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "PreDefault"
|
||||
},
|
||||
{
|
||||
"Name": "ComboInputEditor",
|
||||
"Type": "Editor",
|
||||
"LoadingPhase": "Default"
|
||||
}
|
||||
],
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "EnhancedInput",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Icon128.png
Normal file
BIN
Resources/Icon128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
74
Source/ComboInput/ComboInput.Build.cs
Normal file
74
Source/ComboInput/ComboInput.Build.cs
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class ComboInput : ModuleRules
|
||||
{
|
||||
public ComboInput(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
bLegacyPublicIncludePaths = false;
|
||||
ShadowVariableWarningLevel = WarningLevel.Error;
|
||||
|
||||
PublicIncludePaths.AddRange
|
||||
(
|
||||
new string[]
|
||||
{
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateIncludePaths.AddRange
|
||||
(
|
||||
new string[]
|
||||
{
|
||||
"ComboInput/Private"
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange
|
||||
(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"UMG",
|
||||
|
||||
"EnhancedInput",
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange
|
||||
(
|
||||
new string[]
|
||||
{
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"GameplayTags",
|
||||
"DeveloperSettings",
|
||||
"UMG",
|
||||
"Projects"
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
if (Target.bBuildEditor)
|
||||
{
|
||||
PrivateDependencyModuleNames.Add("UnrealEd");
|
||||
}
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange
|
||||
(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
24
Source/ComboInput/Private/ComboInput.cpp
Normal file
24
Source/ComboInput/Private/ComboInput.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
|
||||
|
||||
#include "ComboInput.h"
|
||||
|
||||
#include "GameplayTagsManager.h"
|
||||
#include "Interfaces/IPluginManager.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FComboInputModule"
|
||||
|
||||
|
||||
void FComboInputModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
||||
}
|
||||
|
||||
void FComboInputModule::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.
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FComboInputModule, ComboInput)
|
||||
@ -0,0 +1,97 @@
|
||||
// ©2022 Batty Bovine Productions, LLC. All Rights Reserved.
|
||||
|
||||
#include "Components/ComboManagerComponent.h"
|
||||
|
||||
#include "Components/InputBufferComponent.h"
|
||||
#include "Interfaces/ComboHandlerInterface.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogComboManagerComponent);
|
||||
|
||||
|
||||
UComboManagerComponent::UComboManagerComponent()
|
||||
{
|
||||
PrimaryComponentTick.bStartWithTickEnabled = false;
|
||||
PrimaryComponentTick.bTickEvenWhenPaused = false;
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
}
|
||||
|
||||
void UComboManagerComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
const AActor *OwningActor = this->GetOwner();
|
||||
if (const IComboHandlerInterface *ComboHandler = Cast<IComboHandlerInterface>(OwningActor))
|
||||
{
|
||||
if (UInputBufferComponent *InputBuffer = IComboHandlerInterface::Execute_GetInputBuffer(OwningActor))
|
||||
{
|
||||
this->AttachedInputBuffer = InputBuffer;
|
||||
this->AttachedInputBuffer->NewComboInput.BindUObject(this, &UComboManagerComponent::ComboInputReceived);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UComboManagerComponent::ComboInputReceived(const UComboInputAsset *Input)
|
||||
{
|
||||
/************ DEBUG ************/
|
||||
for (const TPair<TObjectPtr<const UComboInputAsset>, float> &Pair : this->DEBUG__UnlockTimers)
|
||||
{
|
||||
if (!this->DEBUG__TimerHandles.Contains(Pair.Key))
|
||||
{
|
||||
this->DEBUG__TimerHandles.Add(Pair.Key);
|
||||
}
|
||||
this->GetWorld()->GetTimerManager().ClearTimer(this->DEBUG__TimerHandles[Pair.Key]);
|
||||
this->GetWorld()->GetTimerManager().SetTimer(this->DEBUG__TimerHandles[Pair.Key], FTimerDelegate::CreateUObject(this, &UComboManagerComponent::DEBUG__UnlockAction, Pair.Key), Pair.Value, false);
|
||||
}
|
||||
/********** END DEBUG **********/
|
||||
|
||||
// If we received an offset input, perform the offset here and then leave.
|
||||
if (Input == this->OffsetInput)
|
||||
{
|
||||
this->ActiveNode = this->PreviousNode;
|
||||
this->GetWorld()->GetTimerManager().ClearTimer(this->FinishTransitionTimer);
|
||||
UE_LOG(LogComboManagerComponent, Verbose, TEXT("Combo has been offset by %s"), *Input->ComboInputName.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
const TObjectPtr<const UComboSequenceNode> CurrentNode = (this->ActiveNode ? this->ActiveNode : this->DefaultStartNode);
|
||||
checkf(CurrentNode, TEXT("No combo sequence nodes available."));
|
||||
|
||||
if (CurrentNode->ComboBranch.Contains(Input))
|
||||
{
|
||||
const FComboSequenceAction &ActionData = CurrentNode->ComboBranch[Input];
|
||||
if (ActionData.ComboAction)
|
||||
{
|
||||
this->BeginNodeTransition(ActionData.NextNode);
|
||||
|
||||
UE_LOG(LogComboManagerComponent, Verbose, TEXT("%s activated"), *ActionData.ComboAction->ActionName.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogComboManagerComponent, Verbose, TEXT("No branch found for this action"));
|
||||
}
|
||||
}
|
||||
void UComboManagerComponent::DEBUG__UnlockAction(TObjectPtr<const UComboInputAsset> Unlock)
|
||||
{
|
||||
this->AttachedInputBuffer->UnlockComboInput(Unlock);
|
||||
}
|
||||
|
||||
|
||||
void UComboManagerComponent::BeginNodeTransition(const UComboSequenceNode *NextNode)
|
||||
{
|
||||
this->PreviousNode = this->ActiveNode;
|
||||
this->ActiveNode = NextNode;
|
||||
this->GetWorld()->GetTimerManager().SetTimer(this->FinishTransitionTimer, this, &UComboManagerComponent::FinishTransition, 0.5f);
|
||||
|
||||
this->GetWorld()->GetTimerManager().SetTimer(this->DEBUG__ResetComboTimer, this, &UComboManagerComponent::DEBUG__ResetCombo, 1.0f);
|
||||
}
|
||||
void UComboManagerComponent::DEBUG__ResetCombo()
|
||||
{
|
||||
this->ActiveNode = this->PreviousNode = nullptr;
|
||||
}
|
||||
|
||||
void UComboManagerComponent::FinishTransition()
|
||||
{
|
||||
this->PreviousNode = this->ActiveNode;
|
||||
}
|
||||
191
Source/ComboInput/Private/Components/InputBufferComponent.cpp
Normal file
191
Source/ComboInput/Private/Components/InputBufferComponent.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
// ©2022 Batty Bovine Productions, LLC. All Rights Reserved.
|
||||
|
||||
#include "Components/InputBufferComponent.h"
|
||||
|
||||
#include "Components/InputComponent.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogInputBufferComponent);
|
||||
|
||||
|
||||
UInputBufferComponent::UInputBufferComponent()
|
||||
{
|
||||
PrimaryComponentTick.bStartWithTickEnabled = false;
|
||||
PrimaryComponentTick.bTickEvenWhenPaused = false;
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
}
|
||||
|
||||
void UInputBufferComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// Get all unique EnhancedInput actions bound to combo input actions.
|
||||
TSet<const UInputAction*> InputActionsToBind;
|
||||
for (const UComboInputAsset *ComboInput : this->ComboActions)
|
||||
{
|
||||
for (const UInputAction *InputAction : ComboInput->ActionGroup)
|
||||
{
|
||||
InputActionsToBind.Add(InputAction);
|
||||
}
|
||||
}
|
||||
|
||||
APlayerController *Controller = Cast<APlayerController>(this->GetOwner());
|
||||
checkf(Controller, TEXT("No player controller found as owner of %s"), *this->GetName());
|
||||
|
||||
if (this->EnhancedInputComponent = Cast<UEnhancedInputComponent>(Controller->InputComponent))
|
||||
{
|
||||
// Bind the input actions we found to the buffer management functions.
|
||||
for (const UInputAction *InputAction : InputActionsToBind)
|
||||
{
|
||||
this->EnhancedInputComponent->BindAction(InputAction, ETriggerEvent::Started, this, &UInputBufferComponent::AddActionToBuffer, InputAction);
|
||||
this->EnhancedInputComponent->BindAction(InputAction, ETriggerEvent::Completed, this, &UInputBufferComponent::ExpireAction, InputAction);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogInputBufferComponent, Error, TEXT("Parent of %s is not a UEnhancedInputComponent type."), *this->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UInputBufferComponent::AddActionToBuffer(const FInputActionValue &Value, const class UInputAction *Action)
|
||||
{
|
||||
this->MostRecentActions.Add(Action);
|
||||
this->ExpiringActions.Remove(Action);
|
||||
|
||||
// Find any combo input that matches this action, plus buffered actions.
|
||||
for (const UComboInputAsset *Combo : this->ComboActions)
|
||||
{
|
||||
if (Combo->MatchesInputActions(this->MostRecentActions))
|
||||
{
|
||||
this->ActivateComboInput(Combo);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this->GetWorld()->GetTimerManager().SetTimer(this->MultiPressTimerHandle, this, &UInputBufferComponent::ClearMultiPresses, this->MultiPressTimerLength);
|
||||
}
|
||||
void UInputBufferComponent::ClearMultiPresses()
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
TArray<FString> ActionNames;
|
||||
for (const UInputAction *Action : this->MostRecentActions)
|
||||
{
|
||||
ActionNames.Add(Action->GetName());
|
||||
}
|
||||
UE_LOG(LogInputBufferComponent, Verbose, TEXT("Multi-press buffer cleared (%s)"), *FString::Join(ActionNames, TEXT(" | ")));
|
||||
#endif
|
||||
this->MostRecentActions.Empty();
|
||||
}
|
||||
|
||||
void UInputBufferComponent::ActivateComboInput(const UComboInputAsset *ComboInput)
|
||||
{
|
||||
checkf(ComboInput, TEXT("Invalid UComboInputAsset"));
|
||||
|
||||
// Make this combo input active if it isn't being locked, or if we are
|
||||
// overwriting a previous combo input with a multi-press combo input.
|
||||
const bool bMultiPressTimerActive = this->GetWorld()->GetTimerManager().IsTimerActive(this->MultiPressTimerHandle);
|
||||
const bool bComboInputLocked = this->LockedComboInputs.Contains(ComboInput);
|
||||
if (bMultiPressTimerActive || !bComboInputLocked)
|
||||
{
|
||||
if (!ComboInput->LockedComboInputs.IsEmpty())
|
||||
{
|
||||
// Set the combo input as active, and copy its lock data.
|
||||
this->InputBufferActive = ComboInput;
|
||||
this->LockedComboInputs = ComboInput->LockedComboInputs;
|
||||
|
||||
// Make sure the hold is clear if we're coming off of a multi-press action.
|
||||
if (bMultiPressTimerActive)
|
||||
{
|
||||
this->InputBufferHold = nullptr;
|
||||
}
|
||||
|
||||
//this->GetWorld()->GetTimerManager().SetTimer(this->ForceUnlockTimerHandle, this, &UInputBufferComponent::ForceUnlockComboInputs, this->ForceUnlockTimerLength);
|
||||
|
||||
UE_LOG(LogInputBufferComponent, Verbose, TEXT("%s is active."), *ComboInput->ComboInputName.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogInputBufferComponent, Verbose, TEXT("%s is active and won't lock inputs."), *ComboInput->ComboInputName.ToString());
|
||||
}
|
||||
|
||||
this->NewComboInput.Execute(ComboInput);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->InputBufferHold = ComboInput;
|
||||
|
||||
if (bComboInputLocked)
|
||||
{
|
||||
UE_LOG(LogInputBufferComponent, Verbose, TEXT("%s is locked and won't be activated yet."), *ComboInput->ComboInputName.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogInputBufferComponent, Verbose, TEXT("%s added to buffer."), *ComboInput->ComboInputName.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UInputBufferComponent::UnlockComboInput(const UComboInputAsset *Unlocked)
|
||||
{
|
||||
// Remove the newly-unlocked asset from the locked combo inputs.
|
||||
UE_CLOG(this->LockedComboInputs.Contains(Unlocked), LogInputBufferComponent, Verbose, TEXT("%s has unlocked."), *Unlocked->ComboInputName.ToString());
|
||||
this->LockedComboInputs.Remove(Unlocked);
|
||||
|
||||
// Check if the newly unlocked combo input is in the hold.
|
||||
if (Unlocked == this->InputBufferHold)
|
||||
{
|
||||
const UComboInputAsset *OriginalActive = this->InputBufferActive;
|
||||
|
||||
// Activate the held combo input.
|
||||
const UComboInputAsset *HeldAsset = this->InputBufferHold;
|
||||
this->InputBufferHold = nullptr;
|
||||
if (HeldAsset)
|
||||
{
|
||||
this->ActivateComboInput(HeldAsset);
|
||||
}
|
||||
|
||||
UE_LOG(LogInputBufferComponent, Verbose, TEXT("%s has expired."), *OriginalActive->ComboInputName.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
void UInputBufferComponent::ExpireAction(const FInputActionValue &Value, const class UInputAction *Action)
|
||||
{
|
||||
this->ExpiringActions.Add(Action);
|
||||
this->GetWorld()->GetTimerManager().SetTimer(this->InputReleaseExpirationTimerHandle, this, &UInputBufferComponent::ExpireBufferedActions, this->InputReleaseExpirationTimerLength);
|
||||
}
|
||||
void UInputBufferComponent::ExpireBufferedActions()
|
||||
{
|
||||
// Only bother dealing with this if there's something to deal with in the first place.
|
||||
if (!this->ExpiringActions.IsEmpty())
|
||||
{
|
||||
UE_SUPPRESS(LogInputBufferComponent, Verbose,
|
||||
{
|
||||
TArray<FString> ActionNames;
|
||||
for (const UInputAction *Action : this->ExpiringActions)
|
||||
{
|
||||
ActionNames.Add(Action->GetName());
|
||||
}
|
||||
UE_LOG(LogInputBufferComponent, Verbose, TEXT("Released actions expired (%s)"), *FString::Join(ActionNames, TEXT(" | ")));
|
||||
}
|
||||
);
|
||||
|
||||
// If there is an action in the hold, check if it's related
|
||||
// to our current released buttons, and if so cancel it.
|
||||
if (this->InputBufferHold)
|
||||
{
|
||||
for (const UInputAction *Action : this->ExpiringActions)
|
||||
{
|
||||
if (this->InputBufferHold->MatchesInputAction(Action))
|
||||
{
|
||||
UE_LOG(LogInputBufferComponent, Verbose, TEXT("%s has been cancelled."), *this->InputBufferHold->ComboInputName.ToString());
|
||||
this->InputBufferHold = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->ExpiringActions.Empty();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
// ©2022 Batty Bovine Productions, LLC. All Rights Reserved.
|
||||
|
||||
#include "Interfaces/ComboHandlerInterface.h"
|
||||
|
||||
#include "Components/ComboManagerComponent.h"
|
||||
#include "Components/InputBufferComponent.h"
|
||||
|
||||
|
||||
UInputBufferComponent *IComboHandlerInterface::GetInputBuffer_Implementation() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UComboManagerComponent *IComboHandlerInterface::GetComboManager_Implementation() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
37
Source/ComboInput/Public/ComboInput.h
Normal file
37
Source/ComboInput/Public/ComboInput.h
Normal file
@ -0,0 +1,37 @@
|
||||
// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
|
||||
class FComboInputModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Singleton-like access to this module's interface. This is just for convenience!
|
||||
* Beware of calling this during the shutdown phase, though. Your module might have been unloaded already.
|
||||
*
|
||||
* @return Returns singleton instance, loading the module on demand if needed
|
||||
*/
|
||||
static FComboInputModule &Get()
|
||||
{
|
||||
return FModuleManager::LoadModuleChecked<FComboInputModule>("ComboInput");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true.
|
||||
*
|
||||
* @return True if the module is loaded and ready to use
|
||||
*/
|
||||
static bool IsAvailable()
|
||||
{
|
||||
return FModuleManager::Get().IsModuleLoaded("ComboInput");
|
||||
}
|
||||
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
};
|
||||
90
Source/ComboInput/Public/Components/ComboManagerComponent.h
Normal file
90
Source/ComboInput/Public/Components/ComboManagerComponent.h
Normal file
@ -0,0 +1,90 @@
|
||||
// ©2022 Batty Bovine Productions, LLC. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "Engine/DataAsset.h"
|
||||
|
||||
#include "ComboManagerComponent.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogComboManagerComponent, Log, All);
|
||||
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct COMBOINPUT_API FComboSequenceAction
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
// Action to perform when the associated combo sequence node is activated.
|
||||
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
||||
TObjectPtr<const class UComboAction> ComboAction;
|
||||
|
||||
// Sequence node to switch to once this action is complete.
|
||||
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
||||
TObjectPtr<const class UComboSequenceNode> NextNode;
|
||||
};
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class COMBOINPUT_API UComboAction : public UDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Human-readable name of this combo action.
|
||||
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
||||
FName ActionName;
|
||||
};
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class COMBOINPUT_API UComboSequenceNode : public UDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintReadOnly, EditAnywhere)
|
||||
TMap<const class UComboInputAsset*, struct FComboSequenceAction> ComboBranch;
|
||||
};
|
||||
|
||||
UCLASS(BlueprintType, ClassGroup=(Input), meta=(BlueprintSpawnableComponent))
|
||||
class COMBOINPUT_API UComboManagerComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UComboManagerComponent();
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
protected:
|
||||
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly)
|
||||
TObjectPtr<const class UComboSequenceNode> DefaultStartNode;
|
||||
|
||||
// This input will be recognised as an offset input, which cancels a node transition
|
||||
// if activated early enough in the transition. This locks the current place in the
|
||||
// combo and allows this combo string to be continued after this input has executed.
|
||||
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly)
|
||||
TObjectPtr<const class UComboInputAsset> OffsetInput;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly)
|
||||
TMap<TObjectPtr<const class UComboInputAsset>, float> DEBUG__UnlockTimers;
|
||||
|
||||
private:
|
||||
void ComboInputReceived(const class UComboInputAsset *Input);
|
||||
|
||||
void BeginNodeTransition(const class UComboSequenceNode *NextNode);
|
||||
void FinishTransition();
|
||||
|
||||
void DEBUG__UnlockAction(TObjectPtr<const class UComboInputAsset> Unlock);
|
||||
void DEBUG__ResetCombo();
|
||||
|
||||
const class UComboSequenceNode *ActiveNode = nullptr;
|
||||
const class UComboSequenceNode *PreviousNode = nullptr;
|
||||
|
||||
TObjectPtr<class UInputBufferComponent> AttachedInputBuffer;
|
||||
|
||||
FTimerHandle FinishTransitionTimer;
|
||||
FTimerHandle DEBUG__ResetComboTimer;
|
||||
|
||||
TMap<TObjectPtr<const class UComboInputAsset>, FTimerHandle> DEBUG__TimerHandles;
|
||||
};
|
||||
122
Source/ComboInput/Public/Components/InputBufferComponent.h
Normal file
122
Source/ComboInput/Public/Components/InputBufferComponent.h
Normal file
@ -0,0 +1,122 @@
|
||||
// ©2022 Batty Bovine Productions, LLC. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "EnhancedInputComponent.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
|
||||
#include "InputBufferComponent.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogInputBufferComponent, Log, All);
|
||||
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class COMBOINPUT_API UComboInputAsset : public UDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
bool MatchesInputAction(const class UInputAction* Action) const
|
||||
{
|
||||
if (this->ActionGroup.Num() == 1 && this->ActionGroup.Contains(Action))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool MatchesInputActions(TSet<const class UInputAction *> Actions) const
|
||||
{
|
||||
if (this->ActionGroup.Num() == Actions.Num())
|
||||
{
|
||||
for (const UInputAction *Action : Actions)
|
||||
{
|
||||
if (!this->ActionGroup.Contains(Action))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Human-readable name of this combo input.
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
FName ComboInputName;
|
||||
|
||||
// Combined actions that add up to this combo input when activated
|
||||
// within a short time of one another. If only one is present, then
|
||||
// this combo input asset will simply represent that action.
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
TSet<TObjectPtr<const class UInputAction>> ActionGroup;
|
||||
|
||||
// Combo inputs that should be prevented from occurring during this
|
||||
// action. These will be locked when the action is broadcast, and
|
||||
// should be unlocked by the receiving actor by sending an unlock
|
||||
// event.
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
TSet<TObjectPtr<const class UComboInputAsset>> LockedComboInputs;
|
||||
};
|
||||
|
||||
|
||||
UCLASS(BlueprintType, ClassGroup=(Input), meta=(BlueprintSpawnableComponent))
|
||||
class COMBOINPUT_API UInputBufferComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UInputBufferComponent();
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void UnlockComboInput(const class UComboInputAsset *Unlocked);
|
||||
|
||||
// List of possible combo inputs that can be taken. A combo input is selected from this list
|
||||
// either if an action is made while the current combo is inactive, or when the previous
|
||||
// action expires.
|
||||
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly)
|
||||
TSet<const class UComboInputAsset*> ComboActions;
|
||||
|
||||
// Length of time after releasing an input to keep the associated combo action buffered before clearing it.
|
||||
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, meta=(UIMin="0.0", UIMax="0.5"))
|
||||
float InputReleaseExpirationTimerLength = 0.0666666666666666667f;
|
||||
|
||||
// Length of time within which we can recognise multiple button presses as one input.
|
||||
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, meta=(UIMin="0.02", UIMax="0.25"))
|
||||
float MultiPressTimerLength = 0.025f;
|
||||
|
||||
// Lenght of time before forcibly unlocking any locked combo inputs.
|
||||
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, meta=(UIMin="1.0", UIMax="10.0"))
|
||||
float ForceUnlockTimerLength = 5.0f;
|
||||
|
||||
DECLARE_DELEGATE_OneParam(FNewComboInput, const class UComboInputAsset*);
|
||||
FNewComboInput NewComboInput;
|
||||
|
||||
private:
|
||||
void AddActionToBuffer(const FInputActionValue &Value, const class UInputAction *Action);
|
||||
void ExpireAction(const FInputActionValue &Value, const class UInputAction *Action);
|
||||
|
||||
void ActivateComboInput(const class UComboInputAsset *ComboInput);
|
||||
|
||||
void ClearMultiPresses();
|
||||
void ExpireBufferedActions();
|
||||
|
||||
TObjectPtr<class UEnhancedInputComponent> EnhancedInputComponent;
|
||||
|
||||
// Currently active combo input.
|
||||
TObjectPtr<const class UComboInputAsset> InputBufferActive;
|
||||
|
||||
// Combo input held until the current input has expired.
|
||||
TObjectPtr<const class UComboInputAsset> InputBufferHold;
|
||||
|
||||
// Set of currently locked actions; will not be activated until an unlock signal is received.
|
||||
TSet<TObjectPtr<const class UComboInputAsset>> LockedComboInputs;
|
||||
|
||||
TSet<const class UInputAction*> MostRecentActions;
|
||||
TSet<const class UInputAction*> ExpiringActions;
|
||||
|
||||
FTimerHandle MultiPressTimerHandle;
|
||||
FTimerHandle InputReleaseExpirationTimerHandle;
|
||||
FTimerHandle ForceUnlockTimerHandle;
|
||||
};
|
||||
32
Source/ComboInput/Public/Interfaces/ComboHandlerInterface.h
Normal file
32
Source/ComboInput/Public/Interfaces/ComboHandlerInterface.h
Normal file
@ -0,0 +1,32 @@
|
||||
// ©2022 Batty Bovine Productions, LLC. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Interface.h"
|
||||
#include "ComboHandlerInterface.generated.h"
|
||||
|
||||
|
||||
// This class does not need to be modified.
|
||||
UINTERFACE(MinimalAPI, meta=(Blueprintable))
|
||||
class UComboHandlerInterface : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
/**
|
||||
* Interface for anything that handles combo inputs and contains a
|
||||
* UComboManagerComponent.
|
||||
*/
|
||||
class COMBOINPUT_API IComboHandlerInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
|
||||
class UInputBufferComponent *GetInputBuffer() const;
|
||||
virtual class UInputBufferComponent *GetInputBuffer_Implementation() const;
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
|
||||
class UComboManagerComponent *GetComboManager() const;
|
||||
virtual class UComboManagerComponent *GetComboManager_Implementation() const;
|
||||
};
|
||||
53
Source/ComboInputEditor/ComboInputEditor.build.cs
Normal file
53
Source/ComboInputEditor/ComboInputEditor.build.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class ComboInputEditor : ModuleRules
|
||||
{
|
||||
public ComboInputEditor(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
bLegacyPublicIncludePaths = false;
|
||||
ShadowVariableWarningLevel = WarningLevel.Error;
|
||||
|
||||
PrecompileForTargets = PrecompileTargetsType.None;
|
||||
bPrecompile = false;
|
||||
bUsePrecompiled = false;
|
||||
|
||||
PublicDependencyModuleNames.AddRange
|
||||
(new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"UnrealEd",
|
||||
"AssetTools"
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.AddRange
|
||||
(
|
||||
new string[]
|
||||
{
|
||||
"ComboInput",
|
||||
|
||||
"AssetTools",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"GraphEditor",
|
||||
"PropertyEditor",
|
||||
"EditorStyle",
|
||||
"Kismet",
|
||||
"KismetWidgets",
|
||||
"ApplicationCore",
|
||||
"ToolMenus",
|
||||
"DeveloperSettings",
|
||||
"Projects",
|
||||
|
||||
"BlueprintGraph",
|
||||
"InputCore",
|
||||
|
||||
"MainFrame"
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
23
Source/ComboInputEditor/Private/ComboInputEditor.cpp
Normal file
23
Source/ComboInputEditor/Private/ComboInputEditor.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
|
||||
|
||||
#include "ComboInputEditor.h"
|
||||
|
||||
#include "Interfaces/IPluginManager.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FComboInputEditorModule"
|
||||
|
||||
|
||||
void FComboInputEditorModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
||||
}
|
||||
|
||||
void FComboInputEditorModule::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.
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FComboInputEditorModule, ComboInputEditorModule)
|
||||
70
Source/ComboInputEditor/Public/ComboInputEditor.h
Normal file
70
Source/ComboInputEditor/Public/ComboInputEditor.h
Normal file
@ -0,0 +1,70 @@
|
||||
// ©2023 Batty Bovine Productions, LLC. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Modules/ModuleManager.h"
|
||||
#include "EdGraphUtilities.h"
|
||||
#include "IAssetTools.h"
|
||||
// #include "Interfaces/IHttpRequest.h"
|
||||
|
||||
// class FHttpModule;
|
||||
// class FSlateStyleSet;
|
||||
|
||||
class FComboInputEditorModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Singleton-like access to this module's interface. This is just for convenience!
|
||||
* Beware of calling this during the shutdown phase, though. Your module might have been unloaded already.
|
||||
*
|
||||
* @return Returns singleton instance, loading the module on demand if needed
|
||||
*/
|
||||
static FComboInputEditorModule &Get()
|
||||
{
|
||||
return FModuleManager::LoadModuleChecked<FComboInputEditorModule>( "ComboInputEditor" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true.
|
||||
*
|
||||
* @return True if the module is loaded and ready to use
|
||||
*/
|
||||
static bool IsAvailable()
|
||||
{
|
||||
return FModuleManager::Get().IsModuleLoaded("ComboInputEditor");
|
||||
}
|
||||
|
||||
/* Called when the module is loaded */
|
||||
virtual void StartupModule() override;
|
||||
|
||||
/* Called when the module is unloaded */
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
private:
|
||||
|
||||
// void RegisterAssetTypeAction(IAssetTools& AssetTools, TSharedRef<IAssetTypeActions> Action);
|
||||
|
||||
// void OnGetResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
|
||||
// UFUNCTION() void SendHTTPGet();
|
||||
|
||||
// void PluginButtonClicked();
|
||||
// void RegisterMenus();
|
||||
|
||||
private:
|
||||
|
||||
// TSharedPtr<class FUICommandList> PluginCommands;
|
||||
// TSharedPtr<FSlateStyleSet> DialogueTreeSet;
|
||||
// TSharedPtr<class FMounteaDialogueGraphAssetAction> MounteaDialogueGraphAssetActions;
|
||||
// TSharedPtr<class FMounteaDialogueAdditionalDataAssetAction> MounteaDialogueAdditionalDataAssetActions;
|
||||
// TSharedPtr<class FMounteaDialogueDecoratorAssetAction> MounteaDialogueDecoratorAssetAction;
|
||||
|
||||
// TSharedPtr<struct FGraphPanelNodeFactory> GraphPanelNodeFactory_MounteaDialogueGraph;
|
||||
// TArray< TSharedPtr<IAssetTypeActions> > CreatedAssetTypeActions;
|
||||
|
||||
// EAssetTypeCategories::Type MounteaDialogueGraphAssetCategoryBit;
|
||||
// FHttpModule* Http;
|
||||
|
||||
// TArray<FName> RegisteredCustomClassLayouts;
|
||||
// TArray<FName> RegisteredCustomPropertyTypeLayout;
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user