Skip to content

Input buffer #260

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#include "Animation/GBFAnimNotifyState_InputBuffer.h"

#include "GBFLog.h"

#include <Components/SkeletalMeshComponent.h>

void UGBFAnimNotifyState_InputBuffer::NotifyBegin( USkeletalMeshComponent * mesh_component, UAnimSequenceBase * animation, float total_duration, const FAnimNotifyEventReference & event_reference )
{
Super::NotifyBegin( mesh_component, animation, total_duration, event_reference );

if ( auto * ability_input_buffer_component = GetAbilityInputBufferComponent( mesh_component ) )
{
ability_input_buffer_component->StartMonitoring( InputTagsToCheck, TriggerPriority );
}
}

void UGBFAnimNotifyState_InputBuffer::NotifyEnd( USkeletalMeshComponent * mesh_component, UAnimSequenceBase * animation, const FAnimNotifyEventReference & event_reference )
{
Super::NotifyEnd( mesh_component, animation, event_reference );

if ( auto * ability_input_buffer_component = GetAbilityInputBufferComponent( mesh_component ) )
{
ability_input_buffer_component->StopMonitoring();
}
}

UGBFAbilityInputBufferComponent * UGBFAnimNotifyState_InputBuffer::GetAbilityInputBufferComponent_Implementation( const USkeletalMeshComponent * mesh_component ) const
{
if ( mesh_component == nullptr )
{
return nullptr;
}

const auto * owner = mesh_component->GetOwner();
if ( owner == nullptr )
{
UE_LOG( LogGBF, Error, TEXT( "Ability Input Buffer Notify : No Owner found" ) );
return nullptr;
}

auto * ability_input_buffer_component = owner->FindComponentByClass< UGBFAbilityInputBufferComponent >();

if ( ability_input_buffer_component != nullptr )
{
return ability_input_buffer_component;
}

// Check all parent
auto * parent = owner->GetParentActor();
while ( parent )
{
ability_input_buffer_component = parent->FindComponentByClass< UGBFAbilityInputBufferComponent >();
if ( ability_input_buffer_component != nullptr )
{
return ability_input_buffer_component;
}
parent = parent->GetParentActor();
}

// Check all attached actors
TArray< AActor * > actors;
owner->GetAttachedActors( actors, true, true );
actors.RemoveAll( []( AActor * actor ) {
return !Cast< APawn >( actor );
} );
for ( auto & child : actors )
{
ability_input_buffer_component = child->FindComponentByClass< UGBFAbilityInputBufferComponent >();
if ( ability_input_buffer_component != nullptr )
{
return ability_input_buffer_component;
}
}

UE_LOG( LogGBF, Error, TEXT( "No Ability Input Buffer Component found" ) );
return nullptr;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
#include "Characters/Components/GBFAbilityInputBufferComponent.h"

#include "Characters/Components/GBFHeroComponent.h"
#include "Characters/Components/GBFPawnExtensionComponent.h"
#include "GAS/Components/GBFAbilitySystemComponent.h"
#include "Input/GBFInputComponent.h"

UGBFAbilityInputBufferComponent::UGBFAbilityInputBufferComponent( const FObjectInitializer & object_initializer ) :
Super( object_initializer )
{
TriggerPriority = ETriggerPriority::LastTriggeredInput;
this->PrimaryComponentTick.bStartWithTickEnabled = false;
this->PrimaryComponentTick.bCanEverTick = true;
}

#if !UE_BUILD_SHIPPING
void UGBFAbilityInputBufferComponent::TickComponent( float delta_time, ELevelTick tick_type, FActorComponentTickFunction * this_tick_function )
{
Super::TickComponent( delta_time, tick_type, this_tick_function );
MonitoringTime += delta_time;
if ( !ensureAlwaysMsgf( MonitoringTime <= MaxMonitoringTime, TEXT( "Ability Input Buffer didn't call Stop Monitor %f secs after activation, please call it manually !" ), MaxMonitoringTime ) )
{
StopMonitoring();
}
}
#endif

void UGBFAbilityInputBufferComponent::StartMonitoring( FGameplayTagContainer input_tags_to_check, ETriggerPriority trigger_priority )
{
if ( input_tags_to_check.IsEmpty() )
{
return;
}

Reset();
TriggerPriority = trigger_priority;
InputTagsToCheck = input_tags_to_check;
BindActions();

#if !UE_BUILD_SHIPPING
SetComponentTickEnabled( true );
#endif
}

void UGBFAbilityInputBufferComponent::StopMonitoring()
{
RemoveBinds();
TryToTriggerAbility();
Reset();
}

void UGBFAbilityInputBufferComponent::Reset()
{
TriggeredTags.Reset();
InputTagsToCheck.Reset();
BindHandles.Reset();
MonitoringTime = 0.0f;

#if !UE_BUILD_SHIPPING
SetComponentTickEnabled( false );
#endif
}

void UGBFAbilityInputBufferComponent::BindActions()
{
if ( InputTagsToCheck.IsEmpty() )
{
return;
}

auto * pawn = GetPawn< APawn >();
if ( pawn == nullptr )
{
return;
}

auto * input_component = Cast< UEnhancedInputComponent >( pawn->InputComponent );
if ( input_component == nullptr )
{
return;
}

const auto * hero_component = UGBFHeroComponent::FindHeroComponent( pawn );
if ( hero_component == nullptr )
{
return;
}

for ( auto & input_config : hero_component->GetBoundActionsByInputconfig() )
{
for ( auto & tag : InputTagsToCheck )
{
if ( const auto * input_action = input_config.Key->FindAbilityInputActionForTag( tag ) )
{
BindHandles.Add( input_component->BindAction( input_action, ETriggerEvent::Triggered, this, &ThisClass::AbilityInputTagPressed, tag ).GetHandle() );
}
}
}
}

void UGBFAbilityInputBufferComponent::RemoveBinds()
{
auto * pawn = GetPawn< APawn >();
if ( pawn == nullptr )
{
return;
}

if ( auto * input_component = Cast< UEnhancedInputComponent >( pawn->InputComponent ) )
{
for ( auto & handle : BindHandles )
{
input_component->RemoveBindingByHandle( handle );
}
}
}

void UGBFAbilityInputBufferComponent::AbilityInputTagPressed( FGameplayTag input_tag )
{
TriggeredTags.Add( input_tag );
}

bool UGBFAbilityInputBufferComponent::TryToTriggerAbility()
{
if ( TriggeredTags.IsEmpty() )
{
return false;
}

auto * pawn = GetPawn< APawn >();
if ( pawn == nullptr )
{
return false;
}

const auto * pawn_ext_comp = UGBFPawnExtensionComponent::FindPawnExtensionComponent( pawn );
if ( pawn_ext_comp == nullptr )
{
return false;
}

if ( auto * asc = pawn_ext_comp->GetGBFAbilitySystemComponent() )
{
for ( auto & tagged_ability_tag : InputTagsToCheck )
{
auto * tagged_ability = asc->FindAbilityByInputTag( tagged_ability_tag );
asc->CancelAbility( tagged_ability );
}

// Try to activate ability in priority order
FGameplayTag tag = TryToGetInputTagWithPriority();

while ( tag.IsValid() )
{
if ( auto * ability = asc->FindAbilityByInputTag( tag ) )
{
asc->CancelAbility( ability );
if ( asc->TryActivateAbilityByClass( ability->GetClass() ) )
{
return true;
}
}

tag = TryToGetInputTagWithPriority();
}
}
return false;
}

FGameplayTag UGBFAbilityInputBufferComponent::TryToGetInputTagWithPriority()
{
if ( TriggeredTags.IsEmpty() )
{
return FGameplayTag::EmptyTag;
}

switch ( TriggerPriority )
{
case ETriggerPriority::LastTriggeredInput:
return GetLastTriggeredInput();

case ETriggerPriority::MostTriggeredInput:
return GetMostTriggeredInput();

default:
return FGameplayTag::EmptyTag;
}
}

FGameplayTag UGBFAbilityInputBufferComponent::GetLastTriggeredInput()
{
FGameplayTag first_tag = TriggeredTags[ 0 ];
TriggeredTags.Remove( first_tag );
return first_tag;
}

FGameplayTag UGBFAbilityInputBufferComponent::GetMostTriggeredInput()
{
TSortedMap< int, FGameplayTag > triggered_tag_map;

// Remove all to get count easily
for ( auto & tag_to_remove : InputTagsToCheck )
{
int count = TriggeredTags.Remove( tag_to_remove );
triggered_tag_map.Add( count, tag_to_remove );
}

// Get most triggered input
TArray< int > triggered_tag_keys;
triggered_tag_map.GetKeys( triggered_tag_keys );
int max = triggered_tag_keys[ triggered_tag_map.GetMaxIndex() ];

FGameplayTag most_triggered_tag = triggered_tag_map.FindAndRemoveChecked( max );

triggered_tag_map.Remove( 0 );
for ( auto & input : triggered_tag_map )
{
TriggeredTags.Add( input.Value );
}

return most_triggered_tag;
}
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,11 @@ void UGBFHeroComponent::ClearAbilityCameraMode( const FGameplayAbilitySpecHandle
}
}

const TMap< const UGBFInputConfig *, TArray< uint32 > > & UGBFHeroComponent::GetBoundActionsByInputconfig() const
{
return BoundActionsByInputConfig;
}

void UGBFHeroComponent::OnRegister()
{
Super::OnRegister();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,24 @@ FGameplayAbilitySpecHandle UGBFAbilitySystemComponent::FindAbilitySpecHandleForC
return FGameplayAbilitySpecHandle();
}

UGameplayAbility * UGBFAbilitySystemComponent::FindAbilityByInputTag( const FGameplayTag input_tag ) const
{
if ( !input_tag.IsValid() )
{
return nullptr;
}

for ( const auto & ability_spec : ActivatableAbilities.Items )
{
if ( ability_spec.Ability && ability_spec.DynamicAbilityTags.HasTagExact( input_tag ) )
{
return ability_spec.Ability;
}
}

return nullptr;
}

void UGBFAbilitySystemComponent::OurCancelAllAbilities()
{
static const auto GameplayTagContainer = FGameplayTagContainer::CreateFromArray(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include "Characters/Components/GBFAbilityInputBufferComponent.h"

#include <Animation/AnimNotifies/AnimNotifyState.h>
#include <CoreMinimal.h>

#include "GBFAnimNotifyState_InputBuffer.generated.h"

class UGBFAbilityInputBufferComponent;

UCLASS( DisplayName = "Ability Input Buffer Window" )
class GAMEBASEFRAMEWORK_API UGBFAnimNotifyState_InputBuffer : public UAnimNotifyState
{
GENERATED_BODY()

public:
void NotifyBegin( USkeletalMeshComponent * mesh_component, UAnimSequenceBase * animation, float total_duration, const FAnimNotifyEventReference & event_reference ) override;
void NotifyEnd( USkeletalMeshComponent * mesh_component, UAnimSequenceBase * animation, const FAnimNotifyEventReference & event_reference ) override;

protected:
UFUNCTION( BlueprintNativeEvent )
UGBFAbilityInputBufferComponent * GetAbilityInputBufferComponent( const USkeletalMeshComponent * mesh_component ) const;

private:
UPROPERTY( EditAnywhere )
ETriggerPriority TriggerPriority;

UPROPERTY( EditAnywhere, Meta = ( Categories = "Input" ) )
FGameplayTagContainer InputTagsToCheck;
};
Loading
Loading