diff --git a/CHANGELOG.md b/CHANGELOG.md index eecda5e..cbccd72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.2.1] - 2025-11-26 + +### Updated + +- `UModularPawnOrchestratorComponent` transitions to the state `ModularGameplayTag_InitState_GameplayReady` when the components of both the pawn and the player controller are in the state `ModularGameplayTag_InitState_DataInitialized` +- `UModularPawnOrchestratorComponent` sends the event `NAME_PawnOrchestratorReady` when it transitions to the state `ModularGameplayTag_InitState_GameplayReady` ## [1.2.0] - 2025-11-20 diff --git a/ModularGameplayActors.uplugin b/ModularGameplayActors.uplugin index 175db2d..a78f390 100644 --- a/ModularGameplayActors.uplugin +++ b/ModularGameplayActors.uplugin @@ -1,7 +1,7 @@ { "FileVersion": 3, "Version": 1, - "VersionName": "1.2.0", + "VersionName": "1.2.1", "FriendlyName": "Modular Gameplay Actors", "Description": "Base classes designed to be used with the Modular Gameplay plugin.", "Category": "Gameplay", diff --git a/README.md b/README.md index dad425e..cc479a2 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![Unreal Engine](https://img.shields.io/badge/Unreal%20Engine-5.3%2B-blue.svg)](https://unrealengine.com/) -[![Version](https://img.shields.io/badge/version-1.2.0-green.svg)](https://github.com/yourusername/yourplugin/releases) +[![Version](https://img.shields.io/badge/version-1.2.1-green.svg)](https://github.com/yourusername/yourplugin/releases) ## Overview diff --git a/Source/ModularGameplayActors/Private/ModularGameplayActorsFunctionLibrary.cpp b/Source/ModularGameplayActors/Private/ModularGameplayActorsFunctionLibrary.cpp new file mode 100644 index 0000000..5766120 --- /dev/null +++ b/Source/ModularGameplayActors/Private/ModularGameplayActorsFunctionLibrary.cpp @@ -0,0 +1,21 @@ +#include "ModularGameplayActorsFunctionLibrary.h" + +#include "Components/GameFrameworkComponentManager.h" +#include "Components/GameFrameworkInitStateInterface.h" + +void UModularGameplayActorsFunctionLibrary::CheckDefaultInitializationForImplementersOnActor(AActor* Actor, FName ExcludingFeature /*= NAME_None*/) +{ + if (UGameFrameworkComponentManager* Manager = UGameFrameworkComponentManager::GetForActor(Actor)) + { + TArray Implementers; + Manager->GetAllFeatureImplementers(Implementers, Actor, FGameplayTag(), ExcludingFeature); + + for (UObject* Implementer : Implementers) + { + if (IGameFrameworkInitStateInterface* ImplementerInterface = Cast(Implementer)) + { + ImplementerInterface->CheckDefaultInitialization(); + } + } + } +} \ No newline at end of file diff --git a/Source/ModularGameplayActors/Private/ModularPawnOrchestratorComponent.cpp b/Source/ModularGameplayActors/Private/ModularPawnOrchestratorComponent.cpp index a86d57e..ce6724b 100644 --- a/Source/ModularGameplayActors/Private/ModularPawnOrchestratorComponent.cpp +++ b/Source/ModularGameplayActors/Private/ModularPawnOrchestratorComponent.cpp @@ -1,111 +1,118 @@ #include "ModularPawnOrchestratorComponent.h" -#include "ModularGameplayTags.h" #include "Components/GameFrameworkComponentManager.h" #include "GameFramework/Controller.h" +#include "ModularGameplayTags.h" #include "Net/UnrealNetwork.h" -const FName UModularPawnOrchestratorComponent::NAME_ActorFeatureName( "ModularPawnOrchestratorComponent" ); +const FName UModularPawnOrchestratorComponent::NAME_ActorFeatureName("ModularPawnOrchestratorComponent"); +const FName UModularPawnOrchestratorComponent::NAME_PawnOrchestratorReady("PawnOrchestratorReady"); -UModularPawnOrchestratorComponent::UModularPawnOrchestratorComponent( const FObjectInitializer & ObjectInitializer ) : - Super( ObjectInitializer ) +UModularPawnOrchestratorComponent::UModularPawnOrchestratorComponent(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) { - PrimaryComponentTick.bStartWithTickEnabled = false; - PrimaryComponentTick.bCanEverTick = false; + PrimaryComponentTick.bStartWithTickEnabled = false; + PrimaryComponentTick.bCanEverTick = false; - SetIsReplicatedByDefault( true ); + SetIsReplicatedByDefault(true); } -void UModularPawnOrchestratorComponent::OnPawnReadyToInitialize_UnRegister( const FSimpleMulticastDelegate::FDelegate & Delegate ) +void UModularPawnOrchestratorComponent::OnPawnReadyToInitialize_UnRegister(const FSimpleMulticastDelegate::FDelegate& Delegate) { - OnPawnReadyToInitialize.Remove( Delegate.GetHandle() ); + OnPawnReadyToInitialize.Remove(Delegate.GetHandle()); } FName UModularPawnOrchestratorComponent::GetFeatureName() const { - return NAME_ActorFeatureName; + return NAME_ActorFeatureName; } -bool UModularPawnOrchestratorComponent::CanChangeInitState( UGameFrameworkComponentManager * Manager, FGameplayTag CurrentState, FGameplayTag DesiredState ) const +bool UModularPawnOrchestratorComponent::CanChangeInitState(UGameFrameworkComponentManager* Manager, FGameplayTag CurrentState, FGameplayTag DesiredState) const { - check( Manager != nullptr ); - - auto * Pawn = GetPawn< APawn >(); - - if (!CurrentState.IsValid() && DesiredState == ModularGameplayTag_InitState_Spawned) - { - return Pawn != nullptr; - } - if (CurrentState == ModularGameplayTag_InitState_Spawned && DesiredState == ModularGameplayTag_InitState_DataAvailable) - { - const auto bHasAuthority = Pawn->HasAuthority(); - const auto bIsLocallyControlled = Pawn->IsLocallyControlled(); - - if (bHasAuthority || bIsLocallyControlled) - { - // Check for being possessed by a controller. - if (GetController< AController >() == nullptr) - { - return false; - } - } - - return true; - } - if (CurrentState == ModularGameplayTag_InitState_DataAvailable && DesiredState == ModularGameplayTag_InitState_DataInitialized) - { - // Transition to initialize if all features have their data available - return Manager->HaveAllFeaturesReachedInitState( Pawn, ModularGameplayTag_InitState_DataAvailable ); - } - if (CurrentState == ModularGameplayTag_InitState_DataInitialized && DesiredState == ModularGameplayTag_InitState_GameplayReady) - { - return true; - } - - return false; + check(Manager != nullptr); + + auto* Pawn = GetPawn(); + + if (!CurrentState.IsValid() && DesiredState == ModularGameplayTag_InitState_Spawned) + { + return Pawn != nullptr; + } + if (CurrentState == ModularGameplayTag_InitState_Spawned && DesiredState == ModularGameplayTag_InitState_DataAvailable) + { + const auto bHasAuthority = Pawn->HasAuthority(); + const auto bIsLocallyControlled = Pawn->IsLocallyControlled(); + + if (bHasAuthority || bIsLocallyControlled) + { + // Check for being possessed by a controller. + if (GetController() == nullptr) + { + return false; + } + } + + return true; + } + if (CurrentState == ModularGameplayTag_InitState_DataAvailable && DesiredState == ModularGameplayTag_InitState_DataInitialized) + { + return Manager->HaveAllFeaturesReachedInitState(Pawn, ModularGameplayTag_InitState_DataAvailable); + } + if (CurrentState == ModularGameplayTag_InitState_DataInitialized && DesiredState == ModularGameplayTag_InitState_GameplayReady) + { + // Check on both the pawn and its player controller because we want everything to be initialized, including inputs (which are handlded by the player controller0 + return Manager->HaveAllFeaturesReachedInitState(Pawn, ModularGameplayTag_InitState_DataInitialized) && + Manager->HaveAllFeaturesReachedInitState(GetController(), ModularGameplayTag_InitState_DataInitialized); + } + + return false; } -void UModularPawnOrchestratorComponent::HandleChangeInitState( UGameFrameworkComponentManager * Manager, FGameplayTag CurrentState, FGameplayTag DesiredState ) +void UModularPawnOrchestratorComponent::HandleChangeInitState(UGameFrameworkComponentManager* Manager, FGameplayTag CurrentState, FGameplayTag DesiredState) { - // This is currently all handled by other components listening to this state change + if (CurrentState == ModularGameplayTag_InitState_DataInitialized && DesiredState == ModularGameplayTag_InitState_GameplayReady) + { + // Send the information that the pawn (and its controller) are fully initialized. + // This is required for example by the ModularPawnGameplayMode system which needs not only the pawn, but also the ability system and inputs to be fully initialized + UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(GetPawn(), NAME_PawnOrchestratorReady); + } } -void UModularPawnOrchestratorComponent::OnActorInitStateChanged( const FActorInitStateChangedParams & Params ) +void UModularPawnOrchestratorComponent::OnActorInitStateChanged(const FActorInitStateChangedParams& Params) { - // If another feature is now in DataAvailable, see if we should transition to DataInitialized - if (Params.FeatureName != NAME_ActorFeatureName) - { - if (Params.FeatureState == ModularGameplayTag_InitState_DataAvailable) - { - CheckDefaultInitialization(); - } - } + // If another feature is now in DataAvailable, see if we should transition to DataInitialized + if (Params.FeatureName != NAME_ActorFeatureName) + { + if (Params.FeatureState == ModularGameplayTag_InitState_DataAvailable) + { + CheckDefaultInitialization(); + } + } } -UModularPawnOrchestratorComponent * UModularPawnOrchestratorComponent::FindPawnOrchestratorComponent( const AActor * Actor ) +UModularPawnOrchestratorComponent* UModularPawnOrchestratorComponent::FindPawnOrchestratorComponent(const AActor* Actor) { - return Actor - ? Actor->FindComponentByClass< UModularPawnOrchestratorComponent >() - : nullptr; + return Actor + ? Actor->FindComponentByClass() + : nullptr; } void UModularPawnOrchestratorComponent::OnRegister() { - Super::OnRegister(); + Super::OnRegister(); - const auto * Pawn = GetPawn< APawn >(); - if (!ensureAlwaysMsgf( ( Pawn != nullptr ), TEXT( "ModularPawnOrchestratorComponent on [%s] can only be added to Pawn actors." ), *GetNameSafe( GetOwner() ) )) - { - return; - } + const auto* Pawn = GetPawn(); + if (!ensureAlwaysMsgf((Pawn != nullptr), TEXT("ModularPawnOrchestratorComponent on [%s] can only be added to Pawn actors."), *GetNameSafe(GetOwner()))) + { + return; + } - TArray< UActorComponent * > PawnExtensionComponents; - Pawn->GetComponents( StaticClass(), PawnExtensionComponents ); - ensureAlwaysMsgf( ( PawnExtensionComponents.Num() == 1 ), TEXT( "Only one ModularPawnOrchestratorComponent should exist on [%s]." ), *GetNameSafe( GetOwner() ) ); + TArray PawnExtensionComponents; + Pawn->GetComponents(StaticClass(), PawnExtensionComponents); + ensureAlwaysMsgf((PawnExtensionComponents.Num() == 1), TEXT("Only one ModularPawnOrchestratorComponent should exist on [%s]."), *GetNameSafe(GetOwner())); } void UModularPawnOrchestratorComponent::BindToRequiredOnActorInitStateChanged() { - // Listen for changes to all features - BindOnActorInitStateChanged( NAME_None, FGameplayTag(), false ); + // Listen for changes to all features + BindOnActorInitStateChanged(NAME_None, FGameplayTag(), false); } \ No newline at end of file diff --git a/Source/ModularGameplayActors/Public/ModularGameplayActorsFunctionLibrary.h b/Source/ModularGameplayActors/Public/ModularGameplayActorsFunctionLibrary.h new file mode 100644 index 0000000..7320957 --- /dev/null +++ b/Source/ModularGameplayActors/Public/ModularGameplayActorsFunctionLibrary.h @@ -0,0 +1,19 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" + +#include "ModularGameplayActorsFunctionLibrary.generated.h" + +/** + * Helper functions + */ +UCLASS() +class MODULARGAMEPLAYACTORS_API UModularGameplayActorsFunctionLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + /** This will call CheckDefaultInitialization on all other feature implementers using this interface on a specific actor, useful to update the state of any dependencies */ + static void CheckDefaultInitializationForImplementersOnActor(AActor* Actor, FName ExcludingFeature = NAME_None); +}; diff --git a/Source/ModularGameplayActors/Public/ModularPawnOrchestratorComponent.h b/Source/ModularGameplayActors/Public/ModularPawnOrchestratorComponent.h index a478803..e950dbc 100644 --- a/Source/ModularGameplayActors/Public/ModularPawnOrchestratorComponent.h +++ b/Source/ModularGameplayActors/Public/ModularPawnOrchestratorComponent.h @@ -27,6 +27,7 @@ class MODULARGAMEPLAYACTORS_API UModularPawnOrchestratorComponent : public UModu /** The name of this overall feature, this one depends on the other named component features */ static const FName NAME_ActorFeatureName; + static const FName NAME_PawnOrchestratorReady; protected: void OnRegister() override;