From 6af0b1c23a2fd44e8e89dc9e6f5ef129dbbb00ec Mon Sep 17 00:00:00 2001 From: Jamie Greunbaum Date: Sun, 17 Sep 2023 15:10:50 -0400 Subject: [PATCH] Working on implementing combo release actions in a way that works reliably. --- .../Components/ComboManagerComponent.cpp | 49 ++++++++++++++++--- .../InputBufferLocalPlayerSubsystem.cpp | 18 +++++-- .../Public/Components/ComboManagerComponent.h | 9 ++-- .../Public/InputBufferLocalPlayerSubsystem.h | 4 +- 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/Source/ComboInput/Private/Components/ComboManagerComponent.cpp b/Source/ComboInput/Private/Components/ComboManagerComponent.cpp index 83a1a9d..bc3a1f0 100644 --- a/Source/ComboInput/Private/Components/ComboManagerComponent.cpp +++ b/Source/ComboInput/Private/Components/ComboManagerComponent.cpp @@ -37,7 +37,22 @@ void UComboManagerComponent::BeginPlay() } -void UComboManagerComponent::ActivateComboInput(const UComboInputAsset *Input) +void UComboManagerComponent::HandleComboInput(const UComboInputAsset *Input, const EComboActionTriggerEvent &TriggerEvent) +{ + switch (TriggerEvent) + { + case EComboActionTriggerEvent::Activated: + this->ActivateComboAction(Input); + break; + case EComboActionTriggerEvent::Released: + this->ReleaseComboAction(Input); + break; + default: + return; + } +} + +void UComboManagerComponent::ActivateComboAction(const UComboInputAsset *Input) { /************ DEBUG ************/ for (const TPair, float> &Pair : this->DEBUG__UnlockTimers) @@ -73,16 +88,18 @@ void UComboManagerComponent::ActivateComboInput(const UComboInputAsset *Input) UE_LOG(LogComboManagerComponent, Verbose, TEXT("%s activated"), *ActionData->ComboAction->ActionName.ToString()); } // Otherwise, see if we have a fallback we can use. - else if (const TObjectPtr &ComboAction = *this->FallbackActions.Find(Input)) + else if (const TObjectPtr *ComboAction = this->FallbackActions.Find(Input)) { this->ResetCombo(); - this->BroadcastDelegates(ComboAction, EComboActionTriggerEvent::Activated); - UE_LOG(LogComboManagerComponent, Verbose, TEXT("Fallback action %s activated"), *ComboAction->ActionName.ToString()); + this->BroadcastDelegates(*ComboAction, EComboActionTriggerEvent::Activated); + UE_LOG(LogComboManagerComponent, Verbose, TEXT("Fallback action %s activated"), *(*ComboAction)->ActionName.ToString()); } - // Simply do nothing if there is no action to be performed. + // If we haven't found an action to perform, reset the combo and activate using the default start node. else { - UE_LOG(LogComboManagerComponent, Verbose, TEXT("No branch found for this action")); + this->ActiveNode = nullptr; + this->ActivateComboAction(Input); + return; } } void UComboManagerComponent::DEBUG__UnlockAction(TObjectPtr Unlock) @@ -92,6 +109,26 @@ void UComboManagerComponent::DEBUG__UnlockAction(TObjectPtrUnlockComboInput(Unlock); } +void UComboManagerComponent::ReleaseComboAction(const class UComboInputAsset *Input) +{ + const TObjectPtr CurrentNode = (this->ActiveNode ? this->ActiveNode : this->DefaultStartNode); + checkf(CurrentNode, TEXT("No combo sequence nodes available.")); + + const FComboSequenceAction *ActionData = CurrentNode->ComboBranch.Find(Input); + // If this node has an action we can release, then release it. + if (ActionData && ActionData->ComboAction) + { + this->BroadcastDelegates(ActionData->ComboAction, EComboActionTriggerEvent::Released); + UE_LOG(LogComboManagerComponent, Verbose, TEXT("%s released"), *ActionData->ComboAction->ActionName.ToString()); + } + // Otherwise, see if we have a fallback we can use. + else if (const TObjectPtr *ComboAction = this->FallbackActions.Find(Input)) + { + this->BroadcastDelegates(*ComboAction, EComboActionTriggerEvent::Released); + UE_LOG(LogComboManagerComponent, Verbose, TEXT("Fallback action %s released"), *(*ComboAction)->ActionName.ToString()); + } +} + void UComboManagerComponent::BeginNodeTransition(const UComboSequenceNode *NextNode) { diff --git a/Source/ComboInput/Private/InputBufferLocalPlayerSubsystem.cpp b/Source/ComboInput/Private/InputBufferLocalPlayerSubsystem.cpp index 712696c..5669957 100644 --- a/Source/ComboInput/Private/InputBufferLocalPlayerSubsystem.cpp +++ b/Source/ComboInput/Private/InputBufferLocalPlayerSubsystem.cpp @@ -19,7 +19,7 @@ void UInputBufferLocalPlayerSubsystem::Initialize(FSubsystemCollectionBase &Coll void UInputBufferLocalPlayerSubsystem::AttachComboManager(UComboManagerComponent *ComboManager, UEnhancedInputComponent *InputComponent) { // Get the player character and try to connect to its combo manager. - this->NewComboInput.BindUObject(ComboManager, &UComboManagerComponent::ActivateComboInput); + this->OnNewComboInput.BindUObject(ComboManager, &UComboManagerComponent::HandleComboInput); // Get all unique EnhancedInput actions bound to combo input actions. const UInputBufferSubsystemGlobalSettings *Settings = GetDefault(); @@ -82,10 +82,12 @@ void UInputBufferLocalPlayerSubsystem::ActivateComboInput(const UComboInputAsset // overwriting a previous combo input with a multi-press combo input. if (bNewSupercedesActive || !bNewInputLocked) { + // Set the combo input as active. + this->InputBufferActive = ComboInput; + + // If we have inputs to lock, prepare the buffer. if (!ComboInput->LockedComboInputs.IsEmpty()) { - // Set the combo input as active, and copy its lock data. - this->InputBufferActive = ComboInput; this->LockedComboInputs = ComboInput->LockedComboInputs; this->InputBufferHold = nullptr; @@ -97,7 +99,7 @@ void UInputBufferLocalPlayerSubsystem::ActivateComboInput(const UComboInputAsset UE_LOG(LogInputBufferLocalPlayerSubsystem, Verbose, TEXT("%s is active and won't lock inputs."), *ComboInput->ComboInputName.ToString()); } - this->NewComboInput.Execute(ComboInput); + this->OnNewComboInput.Execute(ComboInput, EComboActionTriggerEvent::Activated); } else { @@ -147,6 +149,14 @@ void UInputBufferLocalPlayerSubsystem::UnlockComboInput(const UComboInputAsset * void UInputBufferLocalPlayerSubsystem::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 + // with the action is not already buffered for another future activation. + if (this->InputBufferActive && this->InputBufferActive != this->InputBufferHold && this->InputBufferActive->MatchesInputAction(Action)) + { + this->OnNewComboInput.Execute(this->InputBufferActive, EComboActionTriggerEvent::Released); + } + + // Prepare to expire any buffered combo inputs this->ExpiringActions.Add(Action); const UInputBufferSubsystemGlobalSettings *Settings = GetDefault(); this->GetWorld()->GetTimerManager().SetTimer(this->InputReleaseExpirationTimerHandle, this, &UInputBufferLocalPlayerSubsystem::ExpireBufferedActions, Settings->InputReleaseExpirationTimerLength); diff --git a/Source/ComboInput/Public/Components/ComboManagerComponent.h b/Source/ComboInput/Public/Components/ComboManagerComponent.h index 1593cdb..07db8e3 100644 --- a/Source/ComboInput/Public/Components/ComboManagerComponent.h +++ b/Source/ComboInput/Public/Components/ComboManagerComponent.h @@ -45,7 +45,7 @@ public: virtual void BeginPlay() override; UFUNCTION(BlueprintCallable) - void ActivateComboInput(const class UComboInputAsset *Input); + void HandleComboInput(const class UComboInputAsset *Input, const EComboActionTriggerEvent &TriggerEvent); void BindAction(UObject *ObjectToBindTo, FName FunctionName, const class UComboAction *ComboAction, const EComboActionTriggerEvent &TriggerEvent) { @@ -66,8 +66,8 @@ protected: UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) FComboOffsetMap OffsetMap; - // A list of inputs to map to actions by default. If the active combo sequence node - // doesn't handle this action, this list will be used as a fallback whenever possible. + // 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. UPROPERTY(BlueprintReadOnly, EditDefaultsOnly) TMap, TObjectPtr> FallbackActions; @@ -78,6 +78,9 @@ protected: FComboActionHandlerDelegate OnComboAction; private: + void ActivateComboAction(const class UComboInputAsset *Input); + void ReleaseComboAction(const class UComboInputAsset *Input); + void BeginNodeTransition(const class UComboSequenceNode *NextNode); void FinishTransition(); void ResetCombo(); diff --git a/Source/ComboInput/Public/InputBufferLocalPlayerSubsystem.h b/Source/ComboInput/Public/InputBufferLocalPlayerSubsystem.h index 584eb2f..2b214ed 100644 --- a/Source/ComboInput/Public/InputBufferLocalPlayerSubsystem.h +++ b/Source/ComboInput/Public/InputBufferLocalPlayerSubsystem.h @@ -9,7 +9,7 @@ DECLARE_LOG_CATEGORY_EXTERN(LogInputBufferLocalPlayerSubsystem, Log, All); -DECLARE_DELEGATE_OneParam(FNewComboInput, const class UComboInputAsset*); +DECLARE_DELEGATE_TwoParams(FNewComboInput, const class UComboInputAsset*, const EComboActionTriggerEvent &); /** @@ -52,7 +52,7 @@ private: TSet MostRecentActions; TSet ExpiringActions; - FNewComboInput NewComboInput; + FNewComboInput OnNewComboInput; FTimerHandle MultiPressTimerHandle; FTimerHandle InputReleaseExpirationTimerHandle;