Download

How to broadcast delegates from static functions? And how to singletons/managers?

Hello,

I’m trying to create an Enemy Manager module in a plugin, which will be part of my generic game framework but I’m failing to decide in which class design and pattern to choose. Basically, the Enemy Manager is an invisible object, where you have to initially add some string identifiers to it that will be added to an array. Also events/delegates/functions have to be exposed to be accessible in Blueprint.

Sorry if those questions will appear to be silly, but I come from Unity and the approach there is to have a GameObject, i.e. Game Manager where I drag an “EnemyManager” script/component and then I can make the GameObject static (so it will stay alive between levels/scenes - in case I have only one set of enemies) or also I can just have one specific Game Manager GameObject per scene and each scene can have its own set of enemies added to the manager. Notice this Game Manager object is not something visual, it is kept alive invisible in the level, as a framework functionality.

As another example let’s consider an Input Manager. You have only one in your game, but you can define your custom controls and have them saved in an array in the Input Manager. Then it has events that are fired when a key is pressed and you can also call functions from it.

Issues/Questions:

  1. How to create a singleton/static class that is accessible from Blueprint?

  2. How to create a static Delegate/Event? At the moment I have the static function AddEnemy and of course it fails to call the non static event, but at the same time I didn’t find a way to create a static event.

  3. Overall, what would be the best way to design this?

  4. Forgetting the whole singleton thing, how to design the Enemy Manager as it where spawnable one time per map and that has to be exposed to Blueprint, but since it is an invisible part of the game framework it can’t be a Component of an object. What could it be defined as? Should I create instances of UEnemyManager manually in Blueprint and then just call the functions non statically, linking to the Target slot? How can I get a similar Unity approach that I explained?

  5. EDIT 1: I am failing to see beyond the “Unity’s way” (been using that engine full time and daily for the past 3 years!). For instance, on the level blueprint I tried to call Add Enemy on Event Begin Play, but then it won’t allow me to create an instance of EnemyManager. Then it also doesn’t offer me way to listen to OnEnemyAdded anywhere because I don’t have an instance of EnemyManager. So is UBlueprintFunctionLibrary the correct parent class to choose?

Example code that I tried and it failed because I didn’t find a way to declare the delegate static and also because I am feeling I am not getting the “Unreal” way to do it:


// .h
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnEnemyAddedSignature, FName, Identifier);

UCLASS()
class UEnemyManager : public UBlueprintFunctionLibrary
{
	GENERATED_UCLASS_BODY()

	/** Add enemy type to be initialised */
	UFUNCTION(BlueprintCallable, Category = "Enemy Manager")
	static void AddEnemy(FName Identifier);

	/** Called when an enemy is successfully added to the manager */
	UPROPERTY(BlueprintAssignable, Category = "Enemy Manager")
	FOnEnemyAddedSignature OnEnemyAdded;

	static TArray<FName> EnemyIdentifiers;
};



// .cpp
UEnemyManager::UEnemyManager(const FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
{
}

void UEnemyManager::AddEnemy(FName Identifier)
{
	EnemyIdentifiers.Add(Identifier);

	// Of course this won't work because OnEnemyAdded is not static... so how to?
	OnEnemyAdded.Broadcast(Identifier);
}

Did you found any solution to your problem?
I have exactly the same question.
How to make the delegate static…?

I really hope I’m wrong, but AFAIK static delegates are not possible yet. The reason is that the reflection system in Unreal is not managing static UPROPERTIES, so you can’t compile with UPROPERTY() on a static variable.
I got here looking for a solution, also. But, as was not finding a way to do it, I made it with a singleton.
In the same class that I made all my static functions and variables (not UPROPERTIES) I added a static pointer of the class’ type to be it’s singleton. The class has the delegates as regular UPROPERTIES and a static function to return the pointer to it’s singleton. The singleton’s only utility is to broadcast events. And I broadcast them from the static functions like this: Singleton->OnActionResult.Broadcast();

Hope it helps as we have no other workarounds yet. At least I think so.

Static Delegates are possible, they can work fine :slight_smile:


.h
/* Events for Creation of GameObjects */
DECLARE_MULTICAST_DELEGATE_OneParam(FGameObjectCreated, UBZGame_GameObjectComponent*);

/* Object Creation Delegate Event */
static FGameObjectCreated ObjectCreationDelegate;



.cpp
FGameObjectCreated UBZGame_GOCManager::ObjectCreationDelegate;


Of course, you need to be able to access the class that the Delegate is declared in so that you can bind something to it - and therefore need an instance of that class in the game.



UBZGame_GameInstance::GetInstance(this)->ObjectCreationDelegate.Broadcast(NewTreeObject);


As far as Singletons / Managers are concerned - your best bet is to initialize them as part of the UGameInstance. I used ObjectInitializer to create them in the Constructor:



UBZGame_GameInstance::UBZGame_GameInstance(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	GameObjectManager = ObjectInitializer.CreateDefaultSubobject<UBZGame_GOCManager>(this, TEXT("GameObjectManager"));
	WorldFXManager = ObjectInitializer.CreateDefaultSubobject<UBZGame_WorldFXManager>(this, TEXT("WorldFXManager"));
	RadarManager = ObjectInitializer.CreateDefaultSubobject<UBZGame_RadarManager>(this, TEXT("RadarManager"));
}


2 Likes

I am sorry I don´t have time to answer all your questions right now, but I didn´t see anyone answer the singleton question. So here is how you would do that:
Header File
UFUNCTION(BlueprintPure)
static MYCLASS* GetInstance();

Source File
MYCLASS* MYCLASS::GetInstance()
{
static MYCLASS *i = 0; if (!i)
{
i = NewObject<MYCLASS>();
i->AddToRoot();
// Do various other things here that you need to do
}

return i;

}

EDIT

In addition to this if you want access to a bunch of Unreal´s functions, you need a pointer to the World everything is in, now there are hacky ways to do this with an iterator. The cleaner way is to have an init function, which is called from the BluePrint at Begin Play or whenever you need it initialized.

Header
UFUNCTION(BlueprintCallable)
void DoInitialize(UObject* WorldContextObject);

Source
void DoInitialize(UObject* WorldContextObject)
{
if (!GEngine)
{
// Fatal Error - GEngine is null
return;
}

UWorld* World = GEngine-&gt;GetWorldFromContextObject(WorldContextObject);
if (!World)
{
	// Fatal Error - This should NEVER happen - World is null
	return;
}

// You now have a pointer to world and can add timers and other funky stuff
}