8 changed files with 113 additions and 131 deletions
@@ -3,9 +3,8 @@
#include "Components/ComboManagerComponent.h" #include "Components/ComboManagerComponent.h"
#include "ComboInputAssets.h" #include "ComboInputAssets.h"
#include "EnhancedInputComponent.h"
#include "InputBufferLocalPlayerSubsystem.h"
#include "Components/InputBufferComponent.h"
#include "Engine/LocalPlayer.h" #include "Engine/LocalPlayer.h"
#include "GameFramework/Character.h" #include "GameFramework/Character.h"
#include "Kismet/GameplayStatics.h" #include "Kismet/GameplayStatics.h"
@@ -20,24 +19,8 @@ UComboManagerComponent::UComboManagerComponent()
PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bCanEverTick = false;
} }
void UComboManagerComponent::BeginPlay()
{
Super::BeginPlay();
// If this component's owner is the player character or controller, attach it to the subsystem. void UComboManagerComponent::HandleComboInput(const UComboInputAsset *Input, const EComboActionTriggerEvent &TriggerEvent)
APlayerController *PlayerController = UGameplayStatics::GetPlayerController(this, 0);
if (this->GetOwner() == PlayerController)
{
UEnhancedInputComponent *InputComponent = Cast<UEnhancedInputComponent>(PlayerController->InputComponent);
checkf(InputComponent, TEXT("Discovered player input component is not of type %s."), *UEnhancedInputComponent::StaticClass()->GetName());
UInputBufferLocalPlayerSubsystem *InputBufferSubsystem = PlayerController->GetLocalPlayer()->GetSubsystem<UInputBufferLocalPlayerSubsystem>();
InputBufferSubsystem->AttachComboManager(this, InputComponent);
}
}
void UComboManagerComponent::HandleComboInput(const UInputAction *Input, const EComboActionTriggerEvent &TriggerEvent)
{ {
switch (TriggerEvent) switch (TriggerEvent)
{ {
@@ -52,10 +35,10 @@ void UComboManagerComponent::HandleComboInput(const UInputAction *Input, const E
} }
} }
void UComboManagerComponent::ActivateComboAction(const UInputAction *Input) void UComboManagerComponent::ActivateComboAction(const UComboInputAsset *Input)
{ {
/************ DEBUG ************/ /************ DEBUG ************/
for (const TPair<TObjectPtr<const UInputAction>, float> &Pair : this->DEBUG__UnlockTimers) for (const TPair<TObjectPtr<const UComboInputAsset>, float> &Pair : this->DEBUG__UnlockTimers)
{ {
if (!this->DEBUG__TimerHandles.Contains(Pair.Key)) if (!this->DEBUG__TimerHandles.Contains(Pair.Key))
{ {
@@ -102,14 +85,16 @@ void UComboManagerComponent::ActivateComboAction(const UInputAction *Input)
return; return;
} }
} }
void UComboManagerComponent::DEBUG__UnlockAction(TObjectPtr<const UInputAction> Unlock) void UComboManagerComponent::DEBUG__UnlockAction(TObjectPtr<const UComboInputAsset> Unlock)
{ {
APlayerController *Controller = UGameplayStatics::GetPlayerController(this, 0); APlayerController *Controller = UGameplayStatics::GetPlayerController(this, 0);
UInputBufferLocalPlayerSubsystem *Subsystem = Controller->GetLocalPlayer()->GetSubsystem<UInputBufferLocalPlayerSubsystem>(); if (UInputBufferComponent *InputComponent = Cast<UInputBufferComponent>(Controller->InputComponent))
Subsystem->UnlockComboInput(Unlock); {
InputComponent->UnlockComboInput(Unlock);
}
} }
void UComboManagerComponent::ReleaseComboAction(const class UInputAction *Input) void UComboManagerComponent::ReleaseComboAction(const class UComboInputAsset *Input)
{ {
if (this->LastComboAction) if (this->LastComboAction)
{ {
@@ -1,52 +1,56 @@
// ©2022 Batty Bovine Productions, LLC. All Rights Reserved. // ©2022 Batty Bovine Productions, LLC. All Rights Reserved.
#include "InputBufferLocalPlayerSubsystem.h" #include "Components/InputBufferComponent.h"
#include "ComboInputAssets.h" #include "ComboInputAssets.h"
#include "EnhancedInputComponent.h" #include "EnhancedInputComponent.h"
#include "Kismet/GameplayStatics.h"
#include "Components/ComboManagerComponent.h" #include "Components/ComboManagerComponent.h"
#include "GlobalSettings/InputBufferSubsystemGlobalSettings.h" #include "GlobalSettings/InputBufferSubsystemGlobalSettings.h"
DEFINE_LOG_CATEGORY(LogInputBufferLocalPlayerSubsystem); DEFINE_LOG_CATEGORY(LogInputBufferComponent);
void UInputBufferLocalPlayerSubsystem::Initialize(FSubsystemCollectionBase &Collection) void UInputBufferComponent::BeginPlay()
{ {
Super::Initialize(Collection); Super::BeginPlay();
}
void UInputBufferLocalPlayerSubsystem::AttachComboManager(UComboManagerComponent *ComboManager, UEnhancedInputComponent *InputComponent) if (APlayerController *PlayerController = UGameplayStatics::GetPlayerController(this, 0))
{
// Get the player character and try to connect to its combo manager.
this->OnNewComboInput.BindUObject(ComboManager, &UComboManagerComponent::HandleComboInput);
// Get all unique EnhancedInput actions bound to combo input actions.
const UInputBufferSubsystemGlobalSettings *Settings = GetDefault<UInputBufferSubsystemGlobalSettings>();
TSet<const UInputAction*> InputActionsToBind;
for (TSoftObjectPtr<const UComboInputGroup> ComboInput : Settings->ComboActions)
{ {
for (const UInputAction *InputAction : ComboInput->ActionGroup) if (UComboManagerComponent *ComboManager = PlayerController->GetComponentByClass<UComboManagerComponent>())
{ {
InputActionsToBind.Add(InputAction); // Get the player character and try to connect to its combo manager.
this->OnNewComboInput.BindUObject(ComboManager, &UComboManagerComponent::HandleComboInput);
// Get all unique EnhancedInput actions bound to combo input actions.
const UInputBufferSubsystemGlobalSettings *Settings = GetDefault<UInputBufferSubsystemGlobalSettings>();
TSet<const UInputAction *> InputActionsToBind;
for (TSoftObjectPtr<const UComboInputAsset> ComboInput : Settings->ComboActions)
{
for (const UInputAction *InputAction : ComboInput->ActionGroup)
{
InputActionsToBind.Add(InputAction);
}
}
for (const UInputAction *InputAction : InputActionsToBind)
{
this->BindAction(InputAction, ETriggerEvent::Started, this, &UInputBufferComponent::AddActionToBuffer, InputAction);
this->BindAction(InputAction, ETriggerEvent::Completed, this, &UInputBufferComponent::ExpireAction, InputAction);
}
} }
} }
for (const UInputAction *InputAction : InputActionsToBind)
{
InputComponent->BindAction(InputAction, ETriggerEvent::Started, this, &UInputBufferLocalPlayerSubsystem::AddActionToBuffer, InputAction);
InputComponent->BindAction(InputAction, ETriggerEvent::Completed, this, &UInputBufferLocalPlayerSubsystem::ExpireAction, InputAction);
}
} }
void UInputBufferLocalPlayerSubsystem::AddActionToBuffer(const FInputActionValue &Value, const class UInputAction *Action) void UInputBufferComponent::AddActionToBuffer(const FInputActionValue &Value, const class UInputAction *Action)
{ {
this->MostRecentActions.Add(Action); this->MostRecentActions.Add(Action);
this->ExpiringActions.Remove(Action); this->ExpiringActions.Remove(Action);
// Find any combo input that matches this action, plus buffered actions. // Find any combo input that matches this action, plus buffered actions.
const UInputBufferSubsystemGlobalSettings *Settings = GetDefault<UInputBufferSubsystemGlobalSettings>(); const UInputBufferSubsystemGlobalSettings *Settings = GetDefault<UInputBufferSubsystemGlobalSettings>();
for (TSoftObjectPtr<const UComboInputGroup> ComboInput : Settings->ComboActions) for (TSoftObjectPtr<const UComboInputAsset> ComboInput : Settings->ComboActions)
{ {
if (ComboInput->MatchesInputActions(this->MostRecentActions)) if (ComboInput->MatchesInputActions(this->MostRecentActions))
{ {
@@ -56,9 +60,9 @@ void UInputBufferLocalPlayerSubsystem::AddActionToBuffer(const FInputActionValue
} }
} }
this->GetWorld()->GetTimerManager().SetTimer(this->MultiPressTimerHandle, this, &UInputBufferLocalPlayerSubsystem::ClearMultiPresses, Settings->MultiPressTimerLength); this->GetWorld()->GetTimerManager().SetTimer(this->MultiPressTimerHandle, this, &UInputBufferComponent::ClearMultiPresses, Settings->MultiPressTimerLength);
} }
void UInputBufferLocalPlayerSubsystem::ClearMultiPresses() void UInputBufferComponent::ClearMultiPresses()
{ {
#if WITH_EDITOR #if WITH_EDITOR
TArray<FString> ActionNames; TArray<FString> ActionNames;
@@ -66,13 +70,15 @@ void UInputBufferLocalPlayerSubsystem::ClearMultiPresses()
{ {
ActionNames.Add(Action->GetName()); ActionNames.Add(Action->GetName());
} }
UE_LOG(LogInputBufferLocalPlayerSubsystem, Verbose, TEXT("Multi-press buffer cleared (%s)"), *FString::Join(ActionNames, TEXT(" | "))); UE_LOG(LogInputBufferComponent, Verbose, TEXT("Multi-press buffer cleared (%s)"), *FString::Join(ActionNames, TEXT(" | ")));
#endif #endif
this->MostRecentActions.Empty(); this->MostRecentActions.Empty();
} }
void UInputBufferLocalPlayerSubsystem::ActivateComboInput(const UInputAction *ComboInput) void UInputBufferComponent::ActivateComboInput(const UComboInputAsset *ComboInput)
{ {
checkf(ComboInput, TEXT("Invalid %s."), *UComboInputAsset::StaticClass()->GetName());
const bool bNewInputLocked = this->LockedComboInputs.Contains(ComboInput); const bool bNewInputLocked = this->LockedComboInputs.Contains(ComboInput);
const bool bNewSupercedesActive = (this->InputBufferActive && this->InputBufferActive->ContainsOneOf(ComboInput->ActionGroup) && !bNewInputLocked); const bool bNewSupercedesActive = (this->InputBufferActive && this->InputBufferActive->ContainsOneOf(ComboInput->ActionGroup) && !bNewInputLocked);
@@ -82,19 +88,12 @@ void UInputBufferLocalPlayerSubsystem::ActivateComboInput(const UInputAction *Co
{ {
// Set the combo input as active. // Set the combo input as active.
this->InputBufferActive = ComboInput; this->InputBufferActive = ComboInput;
this->InputBufferHold = nullptr;
// If we have inputs to lock, prepare the buffer. const UInputBufferSubsystemGlobalSettings *Settings = GetDefault<UInputBufferSubsystemGlobalSettings>();
if (!ComboInput->LockedComboInputs.IsEmpty()) for (const TSoftObjectPtr<const UComboInputAsset> &LockedAsset : Settings->ComboActions)
{ {
this->LockedComboInputs = ComboInput->LockedComboInputs; this->LockComboInput(LockedAsset.Get());
this->InputBufferHold = nullptr;
UE_LOG(LogInputBufferLocalPlayerSubsystem, Verbose, TEXT("%s is active."), *ComboInput->ComboInputName.ToString());
}
else
{
UE_LOG(LogInputBufferLocalPlayerSubsystem, Verbose, TEXT("%s is active and won't lock inputs."), *ComboInput->ComboInputName.ToString());
} }
this->OnNewComboInput.Execute(ComboInput, EComboActionTriggerEvent::Activated); this->OnNewComboInput.Execute(ComboInput, EComboActionTriggerEvent::Activated);
@@ -105,6 +104,8 @@ void UInputBufferLocalPlayerSubsystem::ActivateComboInput(const UInputAction *Co
{ {
this->OnNewComboInput.Execute(ComboInput, EComboActionTriggerEvent::Released); this->OnNewComboInput.Execute(ComboInput, EComboActionTriggerEvent::Released);
} }
UE_LOG(LogInputBufferComponent, Verbose, TEXT("%s is active."), *ComboInput->ComboInputName.ToString());
} }
else else
{ {
@@ -112,27 +113,32 @@ void UInputBufferLocalPlayerSubsystem::ActivateComboInput(const UInputAction *Co
} }
} }
void UInputBufferLocalPlayerSubsystem::HoldComboInput(const UInputAction *ComboInput) void UInputBufferComponent::HoldComboInput(const UComboInputAsset *ComboInput)
{ {
this->InputBufferHold = ComboInput; this->InputBufferHold = ComboInput;
UE_SUPPRESS(LogInputBufferLocalPlayerSubsystem, Verbose, UE_SUPPRESS(LogInputBufferComponent, Verbose,
{ {
if (this->LockedComboInputs.Contains(ComboInput)) if (this->LockedComboInputs.Contains(ComboInput))
{ {
UE_LOG(LogInputBufferLocalPlayerSubsystem, Verbose, TEXT("%s is locked and won't be activated yet."), *ComboInput->ComboInputName.ToString()); UE_LOG(LogInputBufferComponent, Verbose, TEXT("%s is locked and won't be activated yet."), *ComboInput->ComboInputName.ToString());
} }
else else
{ {
UE_LOG(LogInputBufferLocalPlayerSubsystem, Verbose, TEXT("%s added to buffer."), *ComboInput->ComboInputName.ToString()); UE_LOG(LogInputBufferComponent, Verbose, TEXT("%s added to buffer."), *ComboInput->ComboInputName.ToString());
} }
} }
); );
} }
void UInputBufferLocalPlayerSubsystem::UnlockComboInput(const UInputAction *Unlocked) void UInputBufferComponent::LockComboInput(const UComboInputAsset *Input)
{
this->LockedComboInputs.Emplace(Input);
}
void UInputBufferComponent::UnlockComboInput(const UComboInputAsset *Unlocked)
{ {
// Remove the newly-unlocked asset from the locked combo inputs. // Remove the newly-unlocked asset from the locked combo inputs.
UE_CLOG(this->LockedComboInputs.Contains(Unlocked), LogInputBufferLocalPlayerSubsystem, Verbose, TEXT("%s has unlocked."), *Unlocked->ComboInputName.ToString()); UE_CLOG(this->LockedComboInputs.Contains(Unlocked), LogInputBufferComponent, Verbose, TEXT("%s has unlocked."), *Unlocked->ComboInputName.ToString());
this->LockedComboInputs.Remove(Unlocked); this->LockedComboInputs.Remove(Unlocked);
// Check if the newly unlocked combo input is in the hold. // Check if the newly unlocked combo input is in the hold.
@@ -141,18 +147,18 @@ void UInputBufferLocalPlayerSubsystem::UnlockComboInput(const UInputAction *Unlo
const UComboInputAsset *OriginalActive = this->InputBufferActive; const UComboInputAsset *OriginalActive = this->InputBufferActive;
// Activate the held combo input. // Activate the held combo input.
const UInputAction *HeldAsset = this->InputBufferHold; const UComboInputAsset *HeldAsset = this->InputBufferHold;
this->InputBufferHold = nullptr; this->InputBufferHold = nullptr;
if (HeldAsset) if (HeldAsset)
{ {
this->ActivateComboInput(HeldAsset); this->ActivateComboInput(HeldAsset);
} }
UE_LOG(LogInputBufferLocalPlayerSubsystem, Verbose, TEXT("%s has expired."), *OriginalActive->ComboInputName.ToString()); UE_LOG(LogInputBufferComponent, Verbose, TEXT("%s has expired."), *OriginalActive->ComboInputName.ToString());
} }
} }
void UInputBufferLocalPlayerSubsystem::ExpireAction(const FInputActionValue &Value, const class UInputAction *Action) void UInputBufferComponent::ExpireAction(const FInputActionValue &Value, const class UInputAction *Action)
{ {
// Only send a release event if we haven't already, i.e. if the combo input associated // Only send a release event if we haven't already, i.e. if the combo input associated
// with the action is not already buffered for another future activation. // with the action is not already buffered for another future activation.
@@ -164,21 +170,21 @@ void UInputBufferLocalPlayerSubsystem::ExpireAction(const FInputActionValue &Val
// Prepare to expire any buffered combo inputs // Prepare to expire any buffered combo inputs
this->ExpiringActions.Add(Action); this->ExpiringActions.Add(Action);
const UInputBufferSubsystemGlobalSettings *Settings = GetDefault<UInputBufferSubsystemGlobalSettings>(); const UInputBufferSubsystemGlobalSettings *Settings = GetDefault<UInputBufferSubsystemGlobalSettings>();
this->GetWorld()->GetTimerManager().SetTimer(this->InputReleaseExpirationTimerHandle, this, &UInputBufferLocalPlayerSubsystem::ExpireBufferedActions, Settings->InputReleaseExpirationTimerLength); this->GetWorld()->GetTimerManager().SetTimer(this->InputReleaseExpirationTimerHandle, this, &UInputBufferComponent::ExpireBufferedActions, Settings->InputReleaseExpirationTimerLength);
} }
void UInputBufferLocalPlayerSubsystem::ExpireBufferedActions() void UInputBufferComponent::ExpireBufferedActions()
{ {
// Only bother dealing with this if there's something to deal with in the first place. // Only bother dealing with this if there's something to deal with in the first place.
if (!this->ExpiringActions.IsEmpty()) if (!this->ExpiringActions.IsEmpty())
{ {
UE_SUPPRESS(LogInputBufferLocalPlayerSubsystem, Verbose, UE_SUPPRESS(LogInputBufferComponent, Verbose,
{ {
TArray<FString> ActionNames; TArray<FString> ActionNames;
for (const UInputAction *Action : this->ExpiringActions) for (const UInputAction *Action : this->ExpiringActions)
{ {
ActionNames.Add(Action->GetName()); ActionNames.Add(Action->GetName());
} }
UE_LOG(LogInputBufferLocalPlayerSubsystem, Verbose, TEXT("Released actions expired (%s)"), *FString::Join(ActionNames, TEXT(" | "))); UE_LOG(LogInputBufferComponent, Verbose, TEXT("Released actions expired (%s)"), *FString::Join(ActionNames, TEXT(" | ")));
} }
); );
@@ -190,7 +196,7 @@ void UInputBufferLocalPlayerSubsystem::ExpireBufferedActions()
{ {
if (this->InputBufferHold->MatchesInputAction(Action)) if (this->InputBufferHold->MatchesInputAction(Action))
{ {
UE_LOG(LogInputBufferLocalPlayerSubsystem, Verbose, TEXT("%s has been cancelled."), *this->InputBufferHold->ComboInputName.ToString()); UE_LOG(LogInputBufferComponent, Verbose, TEXT("%s has been cancelled."), *this->InputBufferHold->ComboInputName.ToString());
this->InputBufferHold = nullptr; this->InputBufferHold = nullptr;
break; break;
} }
+4 -12
View File
@@ -4,7 +4,6 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "InputAction.h"
#include "Engine/DataAsset.h" #include "Engine/DataAsset.h"
#include "ComboInputAssets.generated.h" #include "ComboInputAssets.generated.h"
@@ -56,7 +55,7 @@ class COMBOINPUT_API UComboSequenceNode : public UDataAsset
public: public:
UPROPERTY(BlueprintReadOnly, EditAnywhere) UPROPERTY(BlueprintReadOnly, EditAnywhere)
TMap<const class UInputAction *, struct FComboSequenceAction> ComboBranch; TMap<const class UComboInputAsset *, struct FComboSequenceAction> ComboBranch;
}; };
/** /**
@@ -66,7 +65,7 @@ public:
* in the current ComboSequenceNode. * in the current ComboSequenceNode.
*/ */
UCLASS(BlueprintType) UCLASS(BlueprintType)
class COMBOINPUT_API UComboInputGroup : public UInputAction class COMBOINPUT_API UComboInputAsset : public UDataAsset
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -113,19 +112,12 @@ public:
} }
// Human-readable name of this combo input. // Human-readable name of this combo input.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Combo Input Group") UPROPERTY(BlueprintReadWrite, EditAnywhere)
FName ComboInputName; FName ComboInputName;
// Combined actions that add up to this combo input when activated // Combined actions that add up to this combo input when activated
// within a short time of one another. If only one is present, then // within a short time of one another. If only one is present, then
// this combo input asset will simply represent that action. // this combo input asset will simply represent that action.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Combo Input Group") UPROPERTY(BlueprintReadWrite, EditAnywhere)
TSet<const class UInputAction*> ActionGroup; TSet<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, Category="Combo Input Group")
TSet<TObjectPtr<const class UComboInputAsset>> LockedComboInputs;
}; };
@@ -31,8 +31,8 @@ struct COMBOINPUT_API FComboOffsetMap
{ {
GENERATED_BODY() GENERATED_BODY()
public: public:
UPROPERTY(BlueprintReadOnly, EditAnywhere) TObjectPtr<const class UInputAction> ComboInput; UPROPERTY(BlueprintReadOnly, EditAnywhere) TObjectPtr<const class UComboInputAsset> ComboInput;
UPROPERTY(BlueprintReadOnly, EditAnywhere) TObjectPtr<const class UComboAction> ComboAction; UPROPERTY(BlueprintReadOnly, EditAnywhere) TObjectPtr<const class UComboAction> ComboAction;
}; };
UCLASS(BlueprintType, ClassGroup=(Input), meta=(BlueprintSpawnableComponent)) UCLASS(BlueprintType, ClassGroup=(Input), meta=(BlueprintSpawnableComponent))
@@ -42,10 +42,9 @@ class COMBOINPUT_API UComboManagerComponent : public UActorComponent
public: public:
UComboManagerComponent(); UComboManagerComponent();
virtual void BeginPlay() override;
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void HandleComboInput(const class UInputAction *Input, const EComboActionTriggerEvent &TriggerEvent); void HandleComboInput(const class UComboInputAsset *Input, const EComboActionTriggerEvent &TriggerEvent);
void BindAction(UObject *ObjectToBindTo, FName FunctionName, const class UComboAction *ComboAction, const EComboActionTriggerEvent &TriggerEvent) void BindAction(UObject *ObjectToBindTo, FName FunctionName, const class UComboAction *ComboAction, const EComboActionTriggerEvent &TriggerEvent)
{ {
@@ -69,17 +68,17 @@ protected:
// A list of default input->action mappings. If you wish for an action to be handled // A list of default input->action mappings. If you wish for an action to be handled
// outside the scope of a combo sequence node, it should go here. // outside the scope of a combo sequence node, it should go here.
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) UPROPERTY(BlueprintReadOnly, EditDefaultsOnly)
TMap<TObjectPtr<const class UInputAction>, TObjectPtr<const class UComboAction>> FallbackActions; TMap<TObjectPtr<const class UComboInputAsset>, TObjectPtr<const class UComboAction>> FallbackActions;
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) UPROPERTY(BlueprintReadOnly, EditDefaultsOnly)
TMap<TObjectPtr<const class UInputAction>, float> DEBUG__UnlockTimers; TMap<TObjectPtr<const class UComboInputAsset>, float> DEBUG__UnlockTimers;
UPROPERTY(BlueprintReadWrite, BlueprintAssignable) UPROPERTY(BlueprintReadWrite, BlueprintAssignable)
FComboActionHandlerDelegate OnUnhandledAction; FComboActionHandlerDelegate OnUnhandledAction;
private: private:
void ActivateComboAction(const class UInputAction *Input); void ActivateComboAction(const class UComboInputAsset *Input);
void ReleaseComboAction(const class UInputAction *Input); void ReleaseComboAction(const class UComboInputAsset *Input);
void BeginNodeTransition(const class UComboSequenceNode *NextNode); void BeginNodeTransition(const class UComboSequenceNode *NextNode);
void FinishTransition(); void FinishTransition();
@@ -87,7 +86,7 @@ private:
void BroadcastDelegates(const class UComboAction *ComboAction, const EComboActionTriggerEvent &TriggerEvent); void BroadcastDelegates(const class UComboAction *ComboAction, const EComboActionTriggerEvent &TriggerEvent);
void DEBUG__UnlockAction(TObjectPtr<const class UInputAction> Unlock); void DEBUG__UnlockAction(TObjectPtr<const class UComboInputAsset> Unlock);
TObjectPtr<const class UComboSequenceNode> ActiveNode = nullptr; TObjectPtr<const class UComboSequenceNode> ActiveNode = nullptr;
TObjectPtr<const class UComboSequenceNode> PreviousNode = nullptr; TObjectPtr<const class UComboSequenceNode> PreviousNode = nullptr;
@@ -101,5 +100,5 @@ private:
FTimerHandle FinishTransitionTimer; FTimerHandle FinishTransitionTimer;
FTimerHandle DEBUG__ResetComboTimer; FTimerHandle DEBUG__ResetComboTimer;
TMap<TObjectPtr<const class UInputAction>, FTimerHandle> DEBUG__TimerHandles; TMap<TObjectPtr<const class UComboInputAsset>, FTimerHandle> DEBUG__TimerHandles;
}; };
@@ -3,37 +3,37 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "EnhancedInputSubsystems.h" #include "EnhancedInputComponent.h"
#include "InputBufferLocalPlayerSubsystem.generated.h" #include "InputBufferComponent.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogInputBufferLocalPlayerSubsystem, Log, All); DECLARE_LOG_CATEGORY_EXTERN(LogInputBufferComponent, Log, All);
DECLARE_DELEGATE_TwoParams(FNewComboInput, const class UInputAction*, const EComboActionTriggerEvent &); DECLARE_DELEGATE_TwoParams(FNewComboInput, const class UComboInputAsset*, const EComboActionTriggerEvent &);
/** /**
* Subsystem that handles input buffering, and passing the resulting actions to the player's ComboManagerComponent. * Subsystem that handles input buffering, and passing the resulting actions to the player's ComboManagerComponent.
*/ */
UCLASS() UCLASS()
class COMBOINPUT_API UInputBufferLocalPlayerSubsystem : public UEnhancedInputLocalPlayerSubsystem class COMBOINPUT_API UInputBufferComponent : public UEnhancedInputComponent
{ {
GENERATED_BODY() GENERATED_BODY()
public: public:
virtual void Initialize(FSubsystemCollectionBase &Collection) override; virtual void BeginPlay() override;
void AttachComboManager(class UComboManagerComponent *ComboManager, class UEnhancedInputComponent *InputComponent);
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void UnlockComboInput(const class UInputAction *Unlocked); void LockComboInput(const class UComboInputAsset *Input);
UFUNCTION(BlueprintCallable)
void UnlockComboInput(const class UComboInputAsset *Unlocked);
private: private:
void AddActionToBuffer(const FInputActionValue &Value, const class UInputAction *Action); void AddActionToBuffer(const FInputActionValue &Value, const class UInputAction *Action);
void ExpireAction(const FInputActionValue &Value, const class UInputAction *Action); void ExpireAction(const FInputActionValue &Value, const class UInputAction *Action);
void ActivateComboInput(const class UInputAction *ComboInput); void ActivateComboInput(const class UComboInputAsset *ComboInput);
void HoldComboInput(const class UInputAction *ComboInput); void HoldComboInput(const class UComboInputAsset *ComboInput);
void ClearMultiPresses(); void ClearMultiPresses();
void ExpireBufferedActions(); void ExpireBufferedActions();
@@ -41,13 +41,13 @@ private:
TObjectPtr<class UEnhancedInputComponent> EnhancedInputComponent; TObjectPtr<class UEnhancedInputComponent> EnhancedInputComponent;
// Currently active combo input. // Currently active combo input.
TObjectPtr<const class UInputAction> InputBufferActive; TObjectPtr<const class UComboInputAsset> InputBufferActive;
// Combo input held until the current input has expired. // Combo input held until the current input has expired.
TObjectPtr<const class UInputAction> InputBufferHold; TObjectPtr<const class UComboInputAsset> InputBufferHold;
// Set of currently locked actions; will not be activated until an unlock signal is received. // Set of currently locked actions; will not be activated until an unlock signal is received.
TSet<TObjectPtr<const class UInputAction>> LockedComboInputs; TSet<TObjectPtr<const class UComboInputAsset>> LockedComboInputs;
TSet<const class UInputAction*> MostRecentActions; TSet<const class UInputAction*> MostRecentActions;
TSet<const class UInputAction*> ExpiringActions; TSet<const class UInputAction*> ExpiringActions;
@@ -20,7 +20,7 @@ public:
// either if an action is made while the current combo is inactive, or when the previous // either if an action is made while the current combo is inactive, or when the previous
// action expires. // action expires.
UPROPERTY(Config, BlueprintReadOnly, EditDefaultsOnly) UPROPERTY(Config, BlueprintReadOnly, EditDefaultsOnly)
TSet<TSoftObjectPtr<const class UComboInputGroup>> ComboActions; TSet<TSoftObjectPtr<const class UComboInputAsset>> ComboActions;
// Length of time after releasing an input to keep the associated combo action buffered before clearing it. // Length of time after releasing an input to keep the associated combo action buffered before clearing it.
UPROPERTY(Config, BlueprintReadOnly, EditDefaultsOnly, meta=(UIMin="0.0", UIMax="0.5", ClampMin="0.0", ClampMax="0.5")) UPROPERTY(Config, BlueprintReadOnly, EditDefaultsOnly, meta=(UIMin="0.0", UIMax="0.5", ClampMin="0.0", ClampMax="0.5"))
@@ -38,14 +38,14 @@ public:
virtual UClass *GetSupportedClass() const override { return UComboSequenceNode::StaticClass(); } virtual UClass *GetSupportedClass() const override { return UComboSequenceNode::StaticClass(); }
}; };
class FAssetTypeActions_ComboInputGroup : public FAssetTypeActions_DataAsset class FAssetTypeActions_ComboInputAsset : public FAssetTypeActions_DataAsset
{ {
public: public:
virtual FText GetName() const override { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_ComboInputGroup", "Combo Input Asset"); } virtual FText GetName() const override { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_ComboInputAsset", "Combo Input Asset"); }
virtual uint32 GetCategories() override { return FComboInputEditorModule::GetInputAssetsCategory(); } virtual uint32 GetCategories() override { return FComboInputEditorModule::GetInputAssetsCategory(); }
virtual FColor GetTypeColor() const override { return FColor(255, 127, 255); } virtual FColor GetTypeColor() const override { return FColor(255, 127, 255); }
virtual FText GetAssetDescription(const FAssetData &AssetData) const override { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_ComboInputGroupDesc", "This maps a sequence of button inputs from EnhancedInput to a combo action that can be used to execute a sequence of moves.This gets sent from the input buffer subsystem to the player controller's ComboManagerComponent, which executes the associated action in the current ComboSequenceNode."); } virtual FText GetAssetDescription(const FAssetData &AssetData) const override { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_ComboInputAssetDesc", "This maps a sequence of button inputs from EnhancedInput to a combo action that can be used to execute a sequence of moves.This gets sent from the input buffer subsystem to the player controller's ComboManagerComponent, which executes the associated action in the current ComboSequenceNode."); }
virtual UClass *GetSupportedClass() const override { return UComboInputGroup::StaticClass(); } virtual UClass *GetSupportedClass() const override { return UComboInputAsset::StaticClass(); }
}; };
@@ -94,24 +94,24 @@ UObject *UComboSequenceNode_Factory::FactoryCreateNew(UClass *Class, UObject *In
} }
UComboInputGroup_Factory::UComboInputGroup_Factory(const FObjectInitializer &ObjectInitializer) UComboInputAsset_Factory::UComboInputAsset_Factory(const FObjectInitializer &ObjectInitializer)
: Super(ObjectInitializer) : Super(ObjectInitializer)
{ {
SupportedClass = UComboInputGroup::StaticClass(); SupportedClass = UComboInputAsset::StaticClass();
bEditAfterNew = true; bEditAfterNew = true;
bCreateNew = true; bCreateNew = true;
} }
UObject *UComboInputGroup_Factory::FactoryCreateNew(UClass *Class, UObject *InParent, FName Name, EObjectFlags Flags, UObject *Context, FFeedbackContext *Warn) UObject *UComboInputAsset_Factory::FactoryCreateNew(UClass *Class, UObject *InParent, FName Name, EObjectFlags Flags, UObject *Context, FFeedbackContext *Warn)
{ {
if (this->ComboInputGroupClass != nullptr) if (this->ComboInputAssetClass != nullptr)
{ {
return NewObject<UComboInputGroup>(InParent, this->ComboInputGroupClass, Name, Flags | RF_Transactional, Context); return NewObject<UComboInputAsset>(InParent, this->ComboInputAssetClass, Name, Flags | RF_Transactional, Context);
} }
else else
{ {
check(Class->IsChildOf(UComboInputGroup::StaticClass())); check(Class->IsChildOf(UComboInputAsset::StaticClass()));
return NewObject<UComboInputGroup>(InParent, Class, Name, Flags | RF_Transactional, Context); return NewObject<UComboInputAsset>(InParent, Class, Name, Flags | RF_Transactional, Context);
} }
} }
@@ -142,8 +142,8 @@ public:
this->Set("ClassIcon.ComboSequenceNode", new IMAGE_BRUSH_SVG("Icons/ComboSequenceNode_16", Icon16)); this->Set("ClassIcon.ComboSequenceNode", new IMAGE_BRUSH_SVG("Icons/ComboSequenceNode_16", Icon16));
this->Set("ClassThumbnail.ComboSequenceNode", new IMAGE_BRUSH_SVG("Icons/ComboSequenceNode_64", Icon64)); this->Set("ClassThumbnail.ComboSequenceNode", new IMAGE_BRUSH_SVG("Icons/ComboSequenceNode_64", Icon64));
this->Set("ClassIcon.ComboInputGroup", new IMAGE_BRUSH_SVG("Icons/ComboInputGroup_16", Icon16)); this->Set("ClassIcon.ComboInputAsset", new IMAGE_BRUSH_SVG("Icons/ComboInputAsset_16", Icon16));
this->Set("ClassThumbnail.ComboInputGroup", new IMAGE_BRUSH_SVG("Icons/ComboInputGroup_64", Icon64)); this->Set("ClassThumbnail.ComboInputAsset", new IMAGE_BRUSH_SVG("Icons/ComboInputAsset_64", Icon64));
} }
}; };
@@ -156,7 +156,7 @@ void FComboInputEditorModule::StartupModule()
FComboInputEditorModule::ComboAssetsCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("Input")), LOCTEXT("InputAssetsCategory", "Input")); FComboInputEditorModule::ComboAssetsCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("Input")), LOCTEXT("InputAssetsCategory", "Input"));
this->RegisterAssetTypeActions(AssetTools, MakeShareable(new FAssetTypeActions_ComboAction)); this->RegisterAssetTypeActions(AssetTools, MakeShareable(new FAssetTypeActions_ComboAction));
this->RegisterAssetTypeActions(AssetTools, MakeShareable(new FAssetTypeActions_ComboSequenceNode)); this->RegisterAssetTypeActions(AssetTools, MakeShareable(new FAssetTypeActions_ComboSequenceNode));
this->RegisterAssetTypeActions(AssetTools, MakeShareable(new FAssetTypeActions_ComboInputGroup)); this->RegisterAssetTypeActions(AssetTools, MakeShareable(new FAssetTypeActions_ComboInputAsset));
// Make a new style set for Combo Input, which will register any custom icons for the types in this plugin // Make a new style set for Combo Input, which will register any custom icons for the types in this plugin
StyleSet = MakeShared<FComboInputSlateStyle>(); StyleSet = MakeShared<FComboInputSlateStyle>();
@@ -42,15 +42,15 @@ public:
}; };
UCLASS() UCLASS()
class COMBOINPUTEDITOR_API UComboInputGroup_Factory : public UFactory class COMBOINPUTEDITOR_API UComboInputAsset_Factory : public UFactory
{ {
GENERATED_BODY() GENERATED_BODY()
public: public:
UComboInputGroup_Factory(const class FObjectInitializer &ObjectInitializer); UComboInputAsset_Factory(const class FObjectInitializer &ObjectInitializer);
UPROPERTY(EditAnywhere, Category="Combo Input") UPROPERTY(EditAnywhere, Category="Combo Input")
TSubclassOf<class UComboInputGroup> ComboInputGroupClass; TSubclassOf<class UComboInputAsset> ComboInputAssetClass;
virtual UObject *FactoryCreateNew(UClass *Class, UObject *InParent, FName Name, EObjectFlags Flags, UObject *Context, FFeedbackContext *Warn) override; virtual UObject *FactoryCreateNew(UClass *Class, UObject *InParent, FName Name, EObjectFlags Flags, UObject *Context, FFeedbackContext *Warn) override;
}; };