Last active
November 14, 2022 19:47
-
-
Save muit/48590ce6a1bf6e6c32c382436416e55e to your computer and use it in GitHub Desktop.
4.22 Subsystems vs Managers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "MyManager.h" | |
#include <Engine/Engine.h> | |
#include <GameDelegates.h> | |
TMap<TWeakObjectPtr<UGameInstance>, TWeakObjectPtr<UMyManager>> UMyManager::GlobalManagers {}; | |
void UMyManager::DoInitialize() | |
{ | |
// Don't garbage collect | |
AddToRoot(); | |
FGameDelegates::Get().GetExitCommandDelegate().AddUObject(this, &UMyManager::DoDeinitialize); | |
bInitialized = true; | |
Initialize(); | |
} | |
void UMyManager::DoDeinitialize() | |
{ | |
if (bInitialized) | |
{ | |
bInitialized = false; | |
Deinitialize(); | |
FGameDelegates::Get().GetExitCommandDelegate().RemoveAll(this); | |
RemoveFromRoot(); | |
MarkPendingKill(); | |
// Doesn't matter if GameInstance became null or not. | |
// The weak ptr will be cleaned anyway. | |
GlobalManagers.Remove(GetGameInstance()); | |
} | |
} | |
UWorld* UMyManager::GetWorld() const | |
{ | |
// If we are a CDO, we must return nullptr instead of calling Outer->GetWorld() to fool UObject::ImplementsGetWorld. | |
if (HasAllFlags(RF_ClassDefaultObject) || !GetOuter()) | |
{ | |
return nullptr; | |
} | |
// Our outer is the GameInstance | |
return GetOuter()->GetWorld(); | |
} | |
UMyManager* UMyManager::Get(const UObject* ContextObject) | |
{ | |
UWorld* World = GEngine->GetWorldFromContextObject(ContextObject, EGetWorldErrorMode::LogAndReturnNull); | |
if (!World) | |
{ | |
return nullptr; | |
} | |
UGameInstance* GI = World->GetGameInstance(); | |
if (!ensureMsgf(GI, TEXT("World should always have a Game Instance"))) | |
{ | |
return nullptr; | |
} | |
TWeakObjectPtr<UMyManager>& Manager = GlobalManagers.FindOrAdd(GI); | |
if (!Manager.IsValid()) | |
{ | |
Manager = NewObject<UMyManager>(GI); | |
Manager->DoInitialize(); | |
} | |
return Manager.Get(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <CoreMinimal.h> | |
#include <Engine/GameInstance.h> | |
#include <Tickable.h> | |
#include "MySubsystem.generated.h" | |
/** | |
* Our (pseudo code) manager. | |
* Is initialized the first time Get() is called. | |
* FTickableGameObject is optional. | |
* | |
* To get the subsystem: | |
* UMyManager* MySubsystem = UMyManager::Get(Context); | |
*/ | |
UCLASS() | |
class UMyManager : public UObject, public FTickableGameObject | |
{ | |
GENERATED_BODY() | |
/************************************************************************/ | |
/* PROPERTIES */ | |
/************************************************************************/ | |
UPROPERTY(Transient) | |
bool bInitialized = false; | |
/************************************************************************/ | |
/* METHODS */ | |
/************************************************************************/ | |
void DoInitialize(); | |
void DoDeinitialize(); | |
protected: | |
virtual void Initialize() {} | |
virtual void Deinitialize() {} | |
UFUNCTION(BlueprintPure, Category = MyManager) | |
FORCEINLINE UGameInstance* GetGameInstance() const | |
{ | |
return Cast<UGameInstance>(GetOuter()); | |
} | |
//~ Begin Tickable Object Interface | |
virtual void Tick(float DeltaTime) override {} | |
virtual bool IsTickable() const override | |
{ | |
return bInitialized;//Same as: !IsDefaultSubobject() && !IsPendingKill(); | |
} | |
virtual TStatId GetStatId() const override | |
{ | |
RETURN_QUICK_DECLARE_CYCLE_STAT(UMySubsystem, STATGROUP_Tickables); | |
} | |
//~ End Tickable Object Interface | |
//~ Begin UObject Interface | |
virtual void BeginDestroy() override | |
{ | |
DoDeinitialize();// <- Ensure it is deinitialized | |
Super::BeginDestroy(); | |
} | |
virtual UWorld* GetWorld() const override; | |
//~ End UObject Interface | |
/** STATIC */ | |
private: | |
static TMap<TWeakObjectPtr<UGameInstance>, TWeakObjectPtr<UMyManager>> GlobalManagers; | |
public: | |
static UMyManager* Get(const UObject* ContextObject); | |
// Different blueprint function to keep C++ api short but Blueprints understandable | |
UFUNCTION(BlueprintPure, Category = SaveExtension, meta = (WorldContext = "ContextObject")) | |
static FORCEINLINE UMyManager* GetMyManager(const UObject * ContextObject) | |
{ | |
return Get(ContextObject); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2015-2019 Piperift. All Rights Reserved. | |
#pragma once | |
#include <CoreMinimal.h> | |
#include <Engine/Engine.h> | |
#include <Engine/GameInstance.h> | |
#include <Subsystems/GameInstanceSubsystem.h> | |
#include <Tickable.h> | |
#include "MySubsystem.generated.h" | |
/** | |
* Our subsystem. | |
* Its automatically initialized (unless its 'Abstract', which can be used for inheritance). | |
* FTickableGameObject is optional. | |
* | |
* To get the subsystem: | |
* UMySubsystem* MySubsystem = UMySubsystem::Get(Context); | |
*/ | |
UCLASS() | |
class UMySubsystem : public UGameInstanceSubsystem, public FTickableGameObject | |
{ | |
GENERATED_BODY() | |
/************************************************************************/ | |
/* METHODS */ | |
/************************************************************************/ | |
protected: | |
//~ Begin USubsystem | |
virtual void Initialize(FSubsystemCollectionBase& Collection) override {} | |
virtual void Deinitialize() override {} | |
//~ End USubsystem | |
//~ Begin Tickable Object Interface | |
virtual void Tick(float DeltaTime) override {} | |
virtual bool IsTickable() const override | |
{ | |
return !IsDefaultSubobject() && !IsPendingKill(); | |
} | |
virtual TStatId GetStatId() const override | |
{ | |
RETURN_QUICK_DECLARE_CYCLE_STAT(UMySubsystem, STATGROUP_Tickables); | |
} | |
//~ End Tickable Object Interface | |
//~ Begin UObject Interface | |
virtual UWorld* GetWorld() const override; | |
//~ End UObject Interface | |
/** STATIC */ | |
public: | |
static UMySubsystem* Get(const UObject* ContextObject) | |
{ | |
if(UWorld* World = GEngine->GetWorldFromContextObject(ContextObject, EGetWorldErrorMode::LogAndReturnNull)) | |
{ | |
return UGameInstance::GetSubsystem<UMySubsystem>(World->GetGameInstance()); | |
} | |
return nullptr; | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment