diff --git a/GameBaseFramework.uplugin b/GameBaseFramework.uplugin index 797fdac6..b73b9ec3 100644 --- a/GameBaseFramework.uplugin +++ b/GameBaseFramework.uplugin @@ -126,6 +126,10 @@ { "Name": "ChaosCaching", "Enabled": true + }, + { + "Name": "ModelViewViewModel", + "Enabled": true } ] } diff --git a/Source/GameBaseFramework/GameBaseFramework.Build.cs b/Source/GameBaseFramework/GameBaseFramework.Build.cs index 7b629621..ab9d58df 100644 --- a/Source/GameBaseFramework/GameBaseFramework.Build.cs +++ b/Source/GameBaseFramework/GameBaseFramework.Build.cs @@ -62,7 +62,8 @@ public GameBaseFramework( ReadOnlyTargetRules Target ) "StateTreeModule", "LevelSequence", "MovieScene", - "ChaosCaching" + "ChaosCaching", + "ModelViewViewModel" } ); } diff --git a/Source/GameBaseFramework/Private/Interaction/Abilities/GBFGameplayAbility_Interact.cpp b/Source/GameBaseFramework/Private/Interaction/Abilities/GBFGameplayAbility_Interact.cpp index ad1ec9f7..b57a29b0 100644 --- a/Source/GameBaseFramework/Private/Interaction/Abilities/GBFGameplayAbility_Interact.cpp +++ b/Source/GameBaseFramework/Private/Interaction/Abilities/GBFGameplayAbility_Interact.cpp @@ -36,6 +36,7 @@ void UGBFGameplayAbility_Interact::ActivateAbility( const FGameplayAbilitySpecHa LookForInteractables(); } + void UGBFGameplayAbility_Interact::EndAbility( const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo * ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled ) { ResetAllInteractions(); @@ -62,6 +63,11 @@ bool UGBFGameplayAbility_Interact::InputMappingContextInfos::IsValid() const return EnhancedSystem.IsValid() && InputMappingContext.IsValid(); } +void UGBFGameplayAbility_Interact::WidgetInfosHandle::Reset() +{ + InteractableComponent = nullptr; +} + void UGBFGameplayAbility_Interact::InteractableTargetContext::Reset() { for ( const auto context : InputMappingContextInfos ) @@ -85,8 +91,8 @@ void UGBFGameplayAbility_Interact::InteractableTargetContext::Reset() } } + WidgetInfosHandle.Reset(); BindActionHandles.Reset(); - WidgetInfosHandles.Reset(); OptionHandles.Reset(); InteractionsId = INDEX_NONE; } @@ -151,7 +157,7 @@ void UGBFGameplayAbility_Interact::UpdateIndicators() } Indicators.Reset(); - const auto add_indicator = [ & ]( const UGBFInteractableComponent * interactable_component, const FGBFInteractionWidgetInfos & widget_infos ) { + const auto add_indicator = [ & ]( UGBFInteractableComponent * interactable_component, const FGBFInteractionWidgetInfos & widget_infos, TArrayView< const OptionHandle > options ) { if ( widget_infos.InteractionWidgetClass == nullptr ) { return; @@ -161,8 +167,23 @@ void UGBFGameplayAbility_Interact::UpdateIndicators() auto * indicator = NewObject< UGBFIndicatorDescriptor >(); indicator->SetDataObject( interactable_target_actor ); indicator->SetSceneComponent( interactable_target_actor->GetRootComponent() ); + indicator->SetComponentSocketName( widget_infos.SocketName ); indicator->SetIndicatorClass( widget_infos.InteractionWidgetClass ); + indicator->SetWorldPositionOffset( widget_infos.InteractionWorldOffset ); indicator->SetScreenSpaceOffset( widget_infos.InteractionWidgetOffset ); + indicator->SetProjectionMode( widget_infos.ProjectionMode ); + indicator->SetBoundingBoxAnchor( widget_infos.BoundingBoxAnchor ); + + TArray< FGBFInteractionOption > interaction_options; + interaction_options.Reserve( options.Num() ); + + for ( const auto & option : options ) + { + interaction_options.Emplace( option.InitialInteractionOption ); + } + + interactable_component->CustomizeIndicator( indicator, interaction_options ); + indicator_manager->AddIndicator( indicator ); Indicators.Add( indicator ); @@ -170,10 +191,7 @@ void UGBFGameplayAbility_Interact::UpdateIndicators() for ( const auto & [ actor, context ] : InteractableTargetContexts ) { - for ( const auto & option_container : context.WidgetInfosHandles ) - { - add_indicator( option_container.InteractableComponent.Get(), option_container.WidgetInfos ); - } + add_indicator( context.WidgetInfosHandle.InteractableComponent.Get(), context.WidgetInfosHandle.WidgetInfos, context.OptionHandles ); } } } @@ -325,7 +343,7 @@ void UGBFGameplayAbility_Interact::RegisterInteraction( const InteractableTarget return; } - context.WidgetInfosHandles.Emplace( interactable_component, option_container.CommonWidgetInfos ); + context.WidgetInfosHandle = { interactable_component, option_container.CommonWidgetInfos }; if ( const auto * pc = Cast< APlayerController >( pawn->GetController() ) ) { @@ -354,10 +372,9 @@ void UGBFGameplayAbility_Interact::RegisterInteraction( const InteractableTarget continue; } - auto & option_handle = context.OptionHandles.AddZeroed_GetRef(); + OptionHandle option_handle; option_handle.InteractableComponent = interactable_component; - - const FGameplayAbilitySpec * interaction_ability_spec = nullptr; + option_handle.InitialInteractionOption = option; switch ( option.AbilityTarget ) { @@ -384,7 +401,7 @@ void UGBFGameplayAbility_Interact::RegisterInteraction( const InteractableTarget } // Find the spec - interaction_ability_spec = option_handle.TargetAbilitySystem->FindAbilitySpecFromClass( option.InteractionAbility ); + const auto * interaction_ability_spec = option_handle.TargetAbilitySystem->FindAbilitySpecFromClass( option.InteractionAbility ); if ( interaction_ability_spec == nullptr ) { @@ -413,6 +430,6 @@ void UGBFGameplayAbility_Interact::RegisterInteraction( const InteractableTarget } } - context.WidgetInfosHandles.Emplace( interactable_component, option.WidgetInfos ); + context.OptionHandles.Emplace( MoveTemp( option_handle ) ); } } diff --git a/Source/GameBaseFramework/Private/Interaction/GBFInteractableComponent.cpp b/Source/GameBaseFramework/Private/Interaction/GBFInteractableComponent.cpp index 5f86fee9..e4f9afe7 100644 --- a/Source/GameBaseFramework/Private/Interaction/GBFInteractableComponent.cpp +++ b/Source/GameBaseFramework/Private/Interaction/GBFInteractableComponent.cpp @@ -40,4 +40,8 @@ void UGBFInteractableComponent::RemoveInteractions() void UGBFInteractableComponent::CustomizeInteractionEventData_Implementation( FGameplayEventData & event_data, FGameplayTag event_tag ) { -} \ No newline at end of file +} + +void UGBFInteractableComponent::CustomizeIndicator_Implementation( UGBFIndicatorDescriptor * indicator_descriptor, const TArray< FGBFInteractionOption > & options ) +{ +} diff --git a/Source/GameBaseFramework/Private/UI/IndicatorSystem/GBFIndicatorDescriptor.cpp b/Source/GameBaseFramework/Private/UI/IndicatorSystem/GBFIndicatorDescriptor.cpp index 1e991df6..6fe5bf5f 100644 --- a/Source/GameBaseFramework/Private/UI/IndicatorSystem/GBFIndicatorDescriptor.cpp +++ b/Source/GameBaseFramework/Private/UI/IndicatorSystem/GBFIndicatorDescriptor.cpp @@ -12,7 +12,7 @@ bool FGBFIndicatorProjection::Project( const UGBFIndicatorDescriptor & indicator TOptional< FVector > world_location; if ( indicator_descriptor.GetComponentSocketName() != NAME_None ) { - world_location = component->GetSocketTransform( indicator_descriptor.GetComponentSocketName() ).GetLocation(); + world_location = component->GetSocketLocation( indicator_descriptor.GetComponentSocketName() ); } else { @@ -20,9 +20,8 @@ bool FGBFIndicatorProjection::Project( const UGBFIndicatorDescriptor & indicator } const auto project_world_location = world_location.GetValue() + indicator_descriptor.GetWorldPositionOffset(); - const auto projection_mode = indicator_descriptor.GetProjectionMode(); - switch ( projection_mode ) + switch ( const auto projection_mode = indicator_descriptor.GetProjectionMode() ) { case EGBFActorCanvasProjectionMode::ComponentPoint: { diff --git a/Source/GameBaseFramework/Public/Interaction/Abilities/GBFGameplayAbility_Interact.h b/Source/GameBaseFramework/Public/Interaction/Abilities/GBFGameplayAbility_Interact.h index c46150db..b61abd1f 100644 --- a/Source/GameBaseFramework/Public/Interaction/Abilities/GBFGameplayAbility_Interact.h +++ b/Source/GameBaseFramework/Public/Interaction/Abilities/GBFGameplayAbility_Interact.h @@ -73,18 +73,11 @@ class GAMEBASEFRAMEWORK_API UGBFGameplayAbility_Interact : public UGBFGameplayAb { OptionHandle() = default; - OptionHandle( const TWeakObjectPtr< UGBFInteractableComponent > & interactable_component, const TWeakObjectPtr< UAbilitySystemComponent > & target_ability_system, const FGameplayAbilitySpecHandle & interaction_ability_handle, const TWeakObjectPtr< UGBFInteractionEventCustomization > & event_customization ) : - InteractableComponent( interactable_component ), - TargetAbilitySystem( target_ability_system ), - InteractionAbilityHandle( interaction_ability_handle ), - EventCustomization( event_customization ) - { - } - TWeakObjectPtr< UGBFInteractableComponent > InteractableComponent; TWeakObjectPtr< UAbilitySystemComponent > TargetAbilitySystem; FGameplayAbilitySpecHandle InteractionAbilityHandle; TWeakObjectPtr< UGBFInteractionEventCustomization > EventCustomization; + FGBFInteractionOption InitialInteractionOption; }; struct WidgetInfosHandle @@ -97,6 +90,8 @@ class GAMEBASEFRAMEWORK_API UGBFGameplayAbility_Interact : public UGBFGameplayAb { } + void Reset(); + TWeakObjectPtr< UGBFInteractableComponent > InteractableComponent; FGBFInteractionWidgetInfos WidgetInfos; }; @@ -106,7 +101,7 @@ class GAMEBASEFRAMEWORK_API UGBFGameplayAbility_Interact : public UGBFGameplayAb void Reset(); int InteractionsId; - TArray< WidgetInfosHandle > WidgetInfosHandles; + WidgetInfosHandle WidgetInfosHandle; TArray< OptionHandle > OptionHandles; TArray< InputMappingContextInfos > InputMappingContextInfos; TArray< InputBindingInfos > BindActionHandles; diff --git a/Source/GameBaseFramework/Public/Interaction/GBFInteractableComponent.h b/Source/GameBaseFramework/Public/Interaction/GBFInteractableComponent.h index eba42461..fa2c5292 100644 --- a/Source/GameBaseFramework/Public/Interaction/GBFInteractableComponent.h +++ b/Source/GameBaseFramework/Public/Interaction/GBFInteractableComponent.h @@ -32,6 +32,9 @@ class GAMEBASEFRAMEWORK_API UGBFInteractableComponent : public UActorComponent UFUNCTION( BlueprintNativeEvent, meta = ( ForceAsFunction ) ) void CustomizeInteractionEventData( UPARAM( ref ) FGameplayEventData & event_data, FGameplayTag event_tag ); + UFUNCTION( BlueprintNativeEvent, meta = ( ForceAsFunction ) ) + void CustomizeIndicator( UGBFIndicatorDescriptor * indicator_descriptor, const TArray< FGBFInteractionOption > & options ); + private: UPROPERTY( EditAnywhere, BlueprintReadOnly, meta = ( AllowPrivateAccess = true ) ) FGBFInteractionOptionContainer InteractionOptionContainer; diff --git a/Source/GameBaseFramework/Public/Interaction/GBFInteractionOption.h b/Source/GameBaseFramework/Public/Interaction/GBFInteractionOption.h index f6fecc2b..b4f2e085 100644 --- a/Source/GameBaseFramework/Public/Interaction/GBFInteractionOption.h +++ b/Source/GameBaseFramework/Public/Interaction/GBFInteractionOption.h @@ -1,5 +1,7 @@ #pragma once +#include "UI/IndicatorSystem/GBFIndicatorDescriptor.h" + #include #include "GBFInteractionOption.generated.h" @@ -21,9 +23,21 @@ struct FGBFInteractionWidgetInfos UPROPERTY( EditAnywhere, BlueprintReadWrite ) TSoftClassPtr< UUserWidget > InteractionWidgetClass; + UPROPERTY( EditAnywhere, BlueprintReadWrite ) + FVector InteractionWorldOffset = FVector::ZeroVector; + UPROPERTY( EditAnywhere, BlueprintReadWrite ) FVector2D InteractionWidgetOffset = FVector2D::ZeroVector; + UPROPERTY( EditAnywhere, BlueprintReadWrite ) + EGBFActorCanvasProjectionMode ProjectionMode = EGBFActorCanvasProjectionMode::ActorBoundingBox; + + UPROPERTY( EditAnywhere, BlueprintReadOnly, meta = ( EditCondition = "ProjectionMode != EGBFActorCanvasProjectionMode::ComponentPoint" ) ) + FVector BoundingBoxAnchor = { 0.5f, 0.5f, 1.0f }; + + UPROPERTY( EditDefaultsOnly, BlueprintReadWrite ) + FName SocketName; + bool operator==( const FGBFInteractionWidgetInfos & other ) const; }; @@ -80,18 +94,15 @@ struct FGBFInteractionOption UPROPERTY( EditAnywhere, Instanced ) TObjectPtr< UGBFInteractionEventCustomization > EventCustomization; - UPROPERTY( EditAnywhere ) + UPROPERTY( EditAnywhere, BlueprintReadOnly ) FGameplayTagRequirements InteractableTargetTagRequirements; - UPROPERTY( EditAnywhere ) + UPROPERTY( EditAnywhere, BlueprintReadOnly ) FGameplayTagRequirements InstigatorTagRequirements; UPROPERTY( EditAnywhere, BlueprintReadOnly ) TObjectPtr< const UInputAction > InputAction = nullptr; - UPROPERTY( EditAnywhere, BlueprintReadOnly ) - FGBFInteractionWidgetInfos WidgetInfos; - bool operator==( const FGBFInteractionOption & other ) const; bool operator!=( const FGBFInteractionOption & other ) const; }; @@ -100,8 +111,7 @@ FORCEINLINE bool FGBFInteractionOption::operator==( const FGBFInteractionOption { return InteractionAbility == other.InteractionAbility && Text.IdenticalTo( other.Text ) && - SubText.IdenticalTo( other.SubText ) && - WidgetInfos == other.WidgetInfos; + SubText.IdenticalTo( other.SubText ); } FORCEINLINE bool FGBFInteractionOption::operator!=( const FGBFInteractionOption & other ) const diff --git a/Source/GameBaseFramework/Public/UI/IndicatorSystem/GBFIndicatorDescriptor.h b/Source/GameBaseFramework/Public/UI/IndicatorSystem/GBFIndicatorDescriptor.h index a3151bcf..c665fccf 100644 --- a/Source/GameBaseFramework/Public/UI/IndicatorSystem/GBFIndicatorDescriptor.h +++ b/Source/GameBaseFramework/Public/UI/IndicatorSystem/GBFIndicatorDescriptor.h @@ -5,6 +5,7 @@ #include "GBFIndicatorDescriptor.generated.h" +class UMVVMViewModelBase; class SWidget; class UGBFIndicatorDescriptor; class UGBFIndicatorManagerComponent; @@ -37,26 +38,38 @@ class GAMEBASEFRAMEWORK_API UGBFIndicatorDescriptor : public UObject GENERATED_BODY() public: - UGBFIndicatorDescriptor() - {} + UGBFIndicatorDescriptor() = default; -public: UFUNCTION( BlueprintCallable ) UObject * GetDataObject() const { return DataObject; } + UFUNCTION( BlueprintCallable ) void SetDataObject( UObject * data_object ) { DataObject = data_object; } + UFUNCTION( BlueprintCallable ) + UMVVMViewModelBase * GetViewModel() const + { + return ViewModel; + } + + UFUNCTION( BlueprintCallable ) + void SetViewModel( UMVVMViewModelBase * view_model ) + { + ViewModel = view_model; + } + UFUNCTION( BlueprintCallable ) USceneComponent * GetSceneComponent() const { return Component; } + UFUNCTION( BlueprintCallable ) void SetSceneComponent( USceneComponent * component ) { @@ -68,6 +81,7 @@ class GAMEBASEFRAMEWORK_API UGBFIndicatorDescriptor : public UObject { return ComponentSocketName; } + UFUNCTION( BlueprintCallable ) void SetComponentSocketName( const FName socket_name ) { @@ -79,22 +93,19 @@ class GAMEBASEFRAMEWORK_API UGBFIndicatorDescriptor : public UObject { return IndicatorWidgetClass; } + UFUNCTION( BlueprintCallable ) void SetIndicatorClass( const TSoftClassPtr< UUserWidget > indicator_widget_class ) { IndicatorWidgetClass = indicator_widget_class; } -public: - // TODO Organize this better. - TWeakObjectPtr< UUserWidget > IndicatorWidget; - -public: UFUNCTION( BlueprintCallable ) void SetAutoRemoveWhenIndicatorComponentIsNull( const bool can_automatically_remove ) { bAutoRemoveWhenIndicatorComponentIsNull = can_automatically_remove; } + UFUNCTION( BlueprintCallable ) bool GetAutoRemoveWhenIndicatorComponentIsNull() const { @@ -106,10 +117,6 @@ class GAMEBASEFRAMEWORK_API UGBFIndicatorDescriptor : public UObject return bAutoRemoveWhenIndicatorComponentIsNull && !IsValid( GetSceneComponent() ); } -public: - // Layout Properties - //======================= - UFUNCTION( BlueprintCallable ) bool GetIsVisible() const { @@ -127,78 +134,79 @@ class GAMEBASEFRAMEWORK_API UGBFIndicatorDescriptor : public UObject { return ProjectionMode; } + UFUNCTION( BlueprintCallable ) void SetProjectionMode( const EGBFActorCanvasProjectionMode projection_mode ) { ProjectionMode = projection_mode; } - // Horizontal alignment to the point in space to place the indicator at. UFUNCTION( BlueprintCallable ) EHorizontalAlignment GetHAlign() const { return HAlignment; } + UFUNCTION( BlueprintCallable ) void SetHAlign( const EHorizontalAlignment h_alignment ) { HAlignment = h_alignment; } - // Vertical alignment to the point in space to place the indicator at. UFUNCTION( BlueprintCallable ) EVerticalAlignment GetVAlign() const { return VAlignment; } + UFUNCTION( BlueprintCallable ) void SetVAlign( const EVerticalAlignment v_alignment ) { VAlignment = v_alignment; } - // Clamp the indicator to the edge of the screen? UFUNCTION( BlueprintCallable ) bool GetClampToScreen() const { return bClampToScreen; } + UFUNCTION( BlueprintCallable ) void SetClampToScreen( const bool value ) { bClampToScreen = value; } - // Show the arrow if clamping to the edge of the screen? UFUNCTION( BlueprintCallable ) bool GetShowClampToScreenArrow() const { return bShowClampToScreenArrow; } + UFUNCTION( BlueprintCallable ) void SetShowClampToScreenArrow( const bool value ) { bShowClampToScreenArrow = value; } - // The position offset for the indicator in world space. UFUNCTION( BlueprintCallable ) FVector GetWorldPositionOffset() const { return WorldPositionOffset; } + UFUNCTION( BlueprintCallable ) void SetWorldPositionOffset( const FVector offset ) { WorldPositionOffset = offset; } - // The position offset for the indicator in screen space. UFUNCTION( BlueprintCallable ) FVector2D GetScreenSpaceOffset() const { return ScreenSpaceOffset; } + UFUNCTION( BlueprintCallable ) void SetScreenSpaceOffset( const FVector2D offset ) { @@ -210,40 +218,38 @@ class GAMEBASEFRAMEWORK_API UGBFIndicatorDescriptor : public UObject { return BoundingBoxAnchor; } + UFUNCTION( BlueprintCallable ) void SetBoundingBoxAnchor( const FVector bounding_box_anchor ) { BoundingBoxAnchor = bounding_box_anchor; } -public: - // Sorting Properties - //======================= - - // Allows sorting the indicators (after they are sorted by depth), to allow some group of indicators - // to always be in front of others. UFUNCTION( BlueprintCallable ) int32 GetPriority() const { return Priority; } + UFUNCTION( BlueprintCallable ) void SetPriority( const int32 priority ) { Priority = priority; } -public: - UGBFIndicatorManagerComponent * GetIndicatorManagerComponent() + UGBFIndicatorManagerComponent * GetIndicatorManagerComponent() const { return ManagerPtr.Get(); } + void SetIndicatorManagerComponent( UGBFIndicatorManagerComponent * manager ); UFUNCTION( BlueprintCallable ) void UnregisterIndicator(); private: + friend class SGBFActorCanvas; + UPROPERTY() bool bVisible = true; UPROPERTY() @@ -272,12 +278,12 @@ class GAMEBASEFRAMEWORK_API UGBFIndicatorDescriptor : public UObject UPROPERTY() FVector WorldPositionOffset = FVector( 0, 0, 0 ); -private: - friend class SGBFActorCanvas; - UPROPERTY() TObjectPtr< UObject > DataObject; + UPROPERTY() + TObjectPtr< UMVVMViewModelBase > ViewModel; + UPROPERTY() TObjectPtr< USceneComponent > Component; @@ -290,6 +296,7 @@ class GAMEBASEFRAMEWORK_API UGBFIndicatorDescriptor : public UObject UPROPERTY() TWeakObjectPtr< UGBFIndicatorManagerComponent > ManagerPtr; + TWeakObjectPtr< UUserWidget > IndicatorWidget; TWeakPtr< SWidget > Content; TWeakPtr< SWidget > CanvasHost; }; diff --git a/Source/GameBaseFramework/Public/UI/IndicatorSystem/GBFIndicatorLibrary.h b/Source/GameBaseFramework/Public/UI/IndicatorSystem/GBFIndicatorLibrary.h index fdca11a7..dc93892c 100644 --- a/Source/GameBaseFramework/Public/UI/IndicatorSystem/GBFIndicatorLibrary.h +++ b/Source/GameBaseFramework/Public/UI/IndicatorSystem/GBFIndicatorLibrary.h @@ -5,9 +5,6 @@ #include "GBFIndicatorLibrary.generated.h" class UGBFIndicatorManagerComponent; -class AController; -class UObject; -struct FFrame; UCLASS() class GAMEBASEFRAMEWORK_API UGBFIndicatorLibrary : public UBlueprintFunctionLibrary @@ -19,4 +16,4 @@ class GAMEBASEFRAMEWORK_API UGBFIndicatorLibrary : public UBlueprintFunctionLibr UFUNCTION( BlueprintCallable, Category = Indicator ) static UGBFIndicatorManagerComponent * GetIndicatorManagerComponent( AController * controller ); -}; +}; \ No newline at end of file