Skip to content

Instantly share code, notes, and snippets.

@muit
Last active November 14, 2022 19:47
Show Gist options
  • Save muit/48590ce6a1bf6e6c32c382436416e55e to your computer and use it in GitHub Desktop.
Save muit/48590ce6a1bf6e6c32c382436416e55e to your computer and use it in GitHub Desktop.
4.22 Subsystems vs Managers
#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();
}
#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);
}
};
// 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