From a854eefda742770b3fdac423310c4d3e60b4bf06 Mon Sep 17 00:00:00 2001 From: Michael Delva Date: Tue, 4 Feb 2025 09:50:17 +0100 Subject: [PATCH] Detect when an actor that can interact with the component enters the radius, or leaves it --- .../Abilities/GBFGameplayAbility_Interact.cpp | 35 +++++++++++++++- .../Interaction/GBFInteractableComponent.cpp | 22 ++++++++++ .../Interaction/GBFInteractableComponent.h | 42 +++++++++++++++++++ .../GBFIndicatorManagerComponent.h | 4 +- 4 files changed, 100 insertions(+), 3 deletions(-) diff --git a/Source/GameBaseFramework/Private/Interaction/Abilities/GBFGameplayAbility_Interact.cpp b/Source/GameBaseFramework/Private/Interaction/Abilities/GBFGameplayAbility_Interact.cpp index b57a29b0..590ca8b9 100644 --- a/Source/GameBaseFramework/Private/Interaction/Abilities/GBFGameplayAbility_Interact.cpp +++ b/Source/GameBaseFramework/Private/Interaction/Abilities/GBFGameplayAbility_Interact.cpp @@ -100,10 +100,43 @@ void UGBFGameplayAbility_Interact::InteractableTargetContext::Reset() void UGBFGameplayAbility_Interact::UpdateInteractableOptions( const TArray< UGBFInteractableComponent * > & interactable_components ) { TArray< InteractableTargetInfos > target_infos; - GatherTargetInfos( target_infos, interactable_components ); + + typedef TMap< TWeakObjectPtr< AActor >, TWeakObjectPtr< UGBFInteractableComponent > > TActorToComponentMap; + + const auto fill_actor_to_component_map = [ & ]( TActorToComponentMap & map ) { + map.Reserve( InteractableTargetContexts.Num() ); + + for ( const auto & [ actor, context ] : InteractableTargetContexts ) + { + map.Emplace( actor, context.WidgetInfosHandle.InteractableComponent ); + } + }; + + TActorToComponentMap previous_active_targets; + fill_actor_to_component_map( previous_active_targets ); + ResetUnusedInteractions( target_infos ); RegisterInteractions( target_infos ); + + TActorToComponentMap new_active_targets; + fill_actor_to_component_map( new_active_targets ); + + for ( const auto & [ actor, component ] : previous_active_targets ) + { + if ( !new_active_targets.Contains( actor ) ) + { + component->OnInteractableActorLeftRadius( GetAvatarActorFromActorInfo() ); + } + } + + for ( const auto & [ actor, component ] : new_active_targets ) + { + if ( !previous_active_targets.Contains( actor ) ) + { + component->OnInteractableActorEnteredRadius( GetAvatarActorFromActorInfo() ); + } + } } void UGBFGameplayAbility_Interact::OnPressCallBack( OptionHandle interaction_option ) diff --git a/Source/GameBaseFramework/Private/Interaction/GBFInteractableComponent.cpp b/Source/GameBaseFramework/Private/Interaction/GBFInteractableComponent.cpp index e4f9afe7..acd4790e 100644 --- a/Source/GameBaseFramework/Private/Interaction/GBFInteractableComponent.cpp +++ b/Source/GameBaseFramework/Private/Interaction/GBFInteractableComponent.cpp @@ -38,6 +38,28 @@ void UGBFInteractableComponent::RemoveInteractions() InteractionOptionContainer.ResetOptions(); } +void UGBFInteractableComponent::OnInteractableActorEnteredRadius( AActor * actor ) +{ + ActorsInInteractionRadius.AddUnique( actor ); + OnInteractableActorEnteredRadiusDelegate.Broadcast( actor ); + K2_OnInteractableActorEnteredRadius( actor ); +} + +void UGBFInteractableComponent::OnInteractableActorLeftRadius( AActor * actor ) +{ + ActorsInInteractionRadius.Remove( actor ); + OnInteractableActorLeftRadiusDelegate.Broadcast( actor ); + K2_OnInteractableActorLeftRadius( actor ); +} + +void UGBFInteractableComponent::K2_OnInteractableActorEnteredRadius_Implementation( AActor * actor ) +{ +} + +void UGBFInteractableComponent::K2_OnInteractableActorLeftRadius_Implementation( AActor * actor ) +{ +} + void UGBFInteractableComponent::CustomizeInteractionEventData_Implementation( FGameplayEventData & event_data, FGameplayTag event_tag ) { } diff --git a/Source/GameBaseFramework/Public/Interaction/GBFInteractableComponent.h b/Source/GameBaseFramework/Public/Interaction/GBFInteractableComponent.h index fa2c5292..72f76f89 100644 --- a/Source/GameBaseFramework/Public/Interaction/GBFInteractableComponent.h +++ b/Source/GameBaseFramework/Public/Interaction/GBFInteractableComponent.h @@ -17,8 +17,13 @@ class GAMEBASEFRAMEWORK_API UGBFInteractableComponent : public UActorComponent GENERATED_BODY() public: + DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam( FGBFOnInteractableInteractionRadiusStateChangedDelegate, AActor *, Actor ); + UGBFInteractableComponent(); + FGBFOnInteractableInteractionRadiusStateChangedDelegate & OnInteractableActorEnteredRadius(); + FGBFOnInteractableInteractionRadiusStateChangedDelegate & OnInteractableLeftRadius(); + const FGBFInteractionOptionContainer & GetInteractableOptions() const; bool IsEnabled() const; void SetEnabled( bool enabled ); @@ -35,12 +40,34 @@ class GAMEBASEFRAMEWORK_API UGBFInteractableComponent : public UActorComponent UFUNCTION( BlueprintNativeEvent, meta = ( ForceAsFunction ) ) void CustomizeIndicator( UGBFIndicatorDescriptor * indicator_descriptor, const TArray< FGBFInteractionOption > & options ); + void OnInteractableActorEnteredRadius( AActor * actor ); + void OnInteractableActorLeftRadius( AActor * actor ); + + UFUNCTION( BlueprintPure ) + bool HasInteractableActorsInRadius() const; + +protected: + UFUNCTION( BlueprintNativeEvent, DisplayName = "OnInteractableActorEnteredRadius", meta = ( ForceAsFunction ) ) + void K2_OnInteractableActorEnteredRadius( AActor * actor ); + + UFUNCTION( BlueprintNativeEvent, DisplayName = "OnInteractableActorLeftRadius", meta = ( ForceAsFunction ) ) + void K2_OnInteractableActorLeftRadius( AActor * actor ); + private: UPROPERTY( EditAnywhere, BlueprintReadOnly, meta = ( AllowPrivateAccess = true ) ) FGBFInteractionOptionContainer InteractionOptionContainer; UPROPERTY( EditAnywhere, BlueprintReadWrite, meta = ( AllowPrivateAccess = true ) ) uint8 bIsEnabled : 1; + + UPROPERTY( BlueprintAssignable, meta = ( AllowPrivateAccess = true ) ) + FGBFOnInteractableInteractionRadiusStateChangedDelegate OnInteractableActorEnteredRadiusDelegate; + + UPROPERTY( BlueprintAssignable, meta = ( AllowPrivateAccess = true ) ) + FGBFOnInteractableInteractionRadiusStateChangedDelegate OnInteractableActorLeftRadiusDelegate; + + UPROPERTY() + TArray< TObjectPtr< AActor > > ActorsInInteractionRadius; }; FORCEINLINE const FGBFInteractionOptionContainer & UGBFInteractableComponent::GetInteractableOptions() const @@ -56,4 +83,19 @@ FORCEINLINE bool UGBFInteractableComponent::IsEnabled() const FORCEINLINE void UGBFInteractableComponent::SetEnabled( bool enabled ) { bIsEnabled = enabled; +} + +FORCEINLINE UGBFInteractableComponent::FGBFOnInteractableInteractionRadiusStateChangedDelegate & UGBFInteractableComponent::OnInteractableActorEnteredRadius() +{ + return OnInteractableActorEnteredRadiusDelegate; +} + +FORCEINLINE UGBFInteractableComponent::FGBFOnInteractableInteractionRadiusStateChangedDelegate & UGBFInteractableComponent::OnInteractableLeftRadius() +{ + return OnInteractableActorLeftRadiusDelegate; +} + +FORCEINLINE bool UGBFInteractableComponent::HasInteractableActorsInRadius() const +{ + return !ActorsInInteractionRadius.IsEmpty(); } \ No newline at end of file diff --git a/Source/GameBaseFramework/Public/UI/IndicatorSystem/GBFIndicatorManagerComponent.h b/Source/GameBaseFramework/Public/UI/IndicatorSystem/GBFIndicatorManagerComponent.h index ef270a23..c77bee46 100644 --- a/Source/GameBaseFramework/Public/UI/IndicatorSystem/GBFIndicatorManagerComponent.h +++ b/Source/GameBaseFramework/Public/UI/IndicatorSystem/GBFIndicatorManagerComponent.h @@ -25,8 +25,8 @@ class GAMEBASEFRAMEWORK_API UGBFIndicatorManagerComponent : public UControllerCo UFUNCTION( BlueprintCallable, Category = Indicator ) void RemoveIndicator( UGBFIndicatorDescriptor * indicator_descriptor ); - DECLARE_EVENT_OneParam( UGBFIndicatorManagerComponent, FIndicatorEvent, UGBFIndicatorDescriptor * descriptor ) - FIndicatorEvent OnIndicatorAdded; + DECLARE_EVENT_OneParam( UGBFIndicatorManagerComponent, FIndicatorEvent, UGBFIndicatorDescriptor * descriptor ); + FIndicatorEvent OnIndicatorAdded; FIndicatorEvent OnIndicatorRemoved; const TArray< UGBFIndicatorDescriptor * > & GetIndicators() const;