Download

Delegates are confusing me...

What I want to do is relatively simple, but I’m struggling to follow the documentation here. All I want to do is make one class of Object tell lots of other object simultaneously that it’s just been created. Essentially, I want the HUD to create a widget that references this object, without having to do lots of casting each time. I thought I had this done correctly, but apparently not since this doesn’t compile.

First of all, this class doesn’t recognise it’s own delegate type… Apparently it doesn’t know what FGameObjectCreated delegate is…

BZGame_GameObjectComponent.h




DECLARE_MULTICAST_DELEGATE_OneParam(FGameObjectCreated, UBZGame_GameObjectComponent* TheObject);

UCLASS(ClassGroup = "Game Object", BlueprintType, hidecategories = (Object, LOD, Physics, Lighting, TextureStreaming, Activation, "Components|Activation", Collision), editinlinenew, meta = (DisplayName = "Game Object Component", BlueprintSpawnableComponent))
class BZGAME_API UBZGame_GameObjectComponent : public UActorComponent
{
	GENERATED_BODY()

public:
	virtual void BeginPlay() override;

	UPROPERTY(BlueprintAssignable)
	FGameObjectCreated ObjectCreationDelegate;
};


I’m fairly certain that this is how I ‘Trigger’ the Delgate:

BZGame_GameObjectComponent.cpp



void UBZGame_GameObjectComponent::BeginPlay()
{
        Super::BeginPlay();

	/* Tell anything that cares, we have a new GameObject in play. */
	ObjectCreationDelegate.Broadcast(this);
}


But the problem is, I have no idea how to ‘Receive’ the delegate in other classes. For example, in my UserWidget class I’ve done this, but it doesn’t look right to me… :confused:

BZGame_InGameWidget.cpp



UBZGame_InGameWidget::UBZGame_InGameWidget(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
	FGameObjectCreated.AddDynamic(this, &UBZGame_GOCWidget::CreateGOCWidget);
}

void UBZGame_InGameWidget::CreateGOCWidget(UBZGame_GameObjectComponent* RegisteredComponent)
{
	if (RootPanel && GOCWidgetClass)
	{
		UBZGame_GOCWidget* NewWidget = CreateWidget<UBZGame_GOCWidget>(GetOwningPlayer(), GOCWidgetClass);
		if (NewWidget)
		{
			NewWidget->GameObject = RegisteredComponent;
			NewWidget->AddToViewport(0);
			GOCWidgetArray.Add(NewWidget);
		}
	}
}


So yeah… where am I going wrong here?

Looks fine to me except the part where you try to add a function to the delegate: You bind it to the delegate type (FGameObjectCreated), but you must bind it to the actual delegate instance (ObjectCreationDelegate) in your UBZGame_GameObjectComponent object. You can’t bind to a type.

Hope that helps. Otherwise have a look here: https://forums.unrealengine.com/showthread.php?30345-Tutorial-Creating-and-Using-Delegates-C-and-Accessing-them-in-Blueprints

Okay, so I bind the delegate to the other class, but in the class where I declare the delegate, not in the class that’s meant to receive the delegate?

I think you need an instance of your object to bind an event from it , maybe be you should use an interface for this

Shouldn’t have too since Collision bind events are setup in the Actors Constructor (which is where I’m now doing this). I want to avoid direct links or pointers for this where possible since it’s causing me all manner of organizational problems.



ObjectCreationDelegate.AddDynamic(this, &UBZGame_InGameWidget::CreateGOCWidget);


Which gives me this crazy output from the log:



E:\Users\James\Documents\Unreal Projects\BZGame\Source\BZGame\Private\Components\BZGame_GameObjectComponent.cpp(39): error C2664: 'void TBaseDynamicMulticastDelegate<FWeakObjectPtr,void,UBZGame_GameObjectComponent *>::__Internal_AddDynamic<UBZGame_GameObjectComponent>(UserClass *,void (__cdecl UBZGame_GameObjectComponent::* )(UBZGame_GameObjectComponent *),const TCHAR *)' : cannot convert argument 2 from 'void (__cdecl UBZGame_InGameWidget::* )(UBZGame_GameObjectComponent *)' to 'void (__cdecl UBZGame_GameObjectComponent::* )(UBZGame_GameObjectComponent *)'


ahh , you forgot a comma (I hate this kind of errors)


DECLARE_MULTICAST_DELEGATE_OneParam(FGameObjectCreated, UBZGame_GameObjectComponent* **,** TheObject);

Ah sorry, I did add that one back in actually, still getting this error.

Perhaps I actually want an event rather than a Delegate… the problem is I have no access to specific instances of objects, I just want the GameObjectComponent to say “IM HERE”, and if things exist and are listening for it, they’ll do something.

Like zaha said, you will have to bind to the delegate instance, but that won’t exist until the UBZGame_GameObjectComponent instance has been created! Can you make the delegate a static property?

Also, I think you need to bind your handler using BindUObject instead of AddDynamic (which is for dynamic delegates).

One other thing, you might need to add a forward declaration to the UBZGame_GameObjectComponent reference where you declare your delegate:
DECLARE_MULTICAST_DELEGATE_OneParam(FGameObjectCreated, class UBZGame_GameObjectComponent);*

Make sure you have a static delegate variable!


class MyClass
{
	DECLARE_DELEGATE_TwoParams(FOnMyStuffHappening, UBlueprint*, TArray<AActor*>);
	static FOnMyStuffHappening OnMyStuffHappening;
};



In another class:


UMyOtherClass::UMyOtherClass(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
	MyClass::OnMyStuffHappening.BindUObject(this, &UMyOtherClass::OnMyStuffHappening);
}

void UMyOtherClass::OnMyStuffHappening(UBlueprint* a_blueprintClass, TArray<AActor*> a_actors)
{
	// do stuff when delegate is executed.
}

EDIT: Damnit, Alderbit! Stealin’ my posts.

Static Delegate would work I suppose… I just want to avoid any kind of instance-based binding, which I’m sure must be possible somehow. I want two classes that have no idea about each other to be able to talk to each other, kind of like shouting across a playground, with no pointer-chain in between.

Thanks Hatedread, that looks like it’ll work perfectly!

The reason your delegate type isn’t getting recognized is that only dynamic delegates can be UPROPERTYs. If you need it to be BlueprintAssignable, you need to declare your delegate type with DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam.

As HateDread says, if you want this delegate to work globally without being bound to any specific instance, then you will need to make it a static member. Unfortunately, in that case it won’t be possible for it also to be a UPROPERTY…

An alternative would be to have a separate manager class for these objects, which would be a singleton (only one instance ever exists). Then the delegate would be a UPROPERTY on the manager class. Any class could bind to the delegate via a reference to the manager (regardless of whether any instances of the game object existed yet or not), and the game objects would likewise trigger the delegate broadcast via a manager reference in their BeginPlay method.

Ah okay thanks guys, had to learn how to use them at some point… Lucky for me this is all code so I don’t need it to be a UPROPERTY. Since it’s static, it’ll still be okay for this delegate to be broadcast several times in one frame right?

I do actually have a manager for the GameObject components already, which is a singleton (though not in PIE, for obvious reasons in MP) - I guess when I register the component there, it could Broadcast the event. Really, the only reason I’m doing this is to save me having to do a Controller Iterator, then Cast to my custom controller, then cast to custom hud, then retrieve a widget from it and call functions. Having the widget ‘Listen’ for these events would be far easier and less convoluted.

What’s the difference then between a Static and Dynamic Delegate? Since I want this to be always bound, can I make it a static, static delegate and save some overhead?

No restrictions on when or how often the delegate is broadcast. It doesn’t have any concept of a ‘frame’ anyway.

I’m not totally clear on what you’re trying to achieve, but if you already have a manager with which objects are being registered, it seems an ideal place to both store the delegate (member of the manager class) and trigger it from (within your game object registration function).

A static class member has no bearing on performance. It just means that there exists only one copy of that variable in your entire process, as opposed to one embedded in every instance of your class as is the case for normal members. You access a static member with MyClass::MyStaticVariable, so you don’t need to have a reference to any instance to do so. Essentially it’s the same as a global variable, only it’s ‘namespaced’ within your class.

A normal UE4 delegate (as opposed to a DYNAMIC_ one) is a bit more efficient. Dynamic delegates are restricted to being bound only to a UFUNCTION on a UObject, and you can’t pass payload data to them. However they have the advantage that they can be UPROPERTYs, so they can be used in blueprints and also the bindings get serialized.

So in your case, you should probably go with a normal (not DYNAMIC_) delegate. Whether it should be a static variable in C++ terms is just a question of where you put it - if it’s in the manager singleton class, then it should just be a normal member, since there will only be one instance of the manager anyway; if it’s in the game object class, it will need to be static since it should be shared amongst all game object instances.

Awesome, Thankyou all - should be able to get this working now. Shall post results…

What I’d generally do in this case, is have a message manager with a delegate based publisher/subscriber pattern. Probably store that messagemanager in the game instance with methods to publish/subscribe specific delegates (perhaps identified by some hash value based on a string). The delegate itself would then just be the core of the pattern for each message (messages would essentially be stored in a map of message name hashes and the associated delegate within the message manager).

The idea being that the delegate is the intermediary between the publisher and the subscriber. So instead of class A having a delegate that class B binds to. You have a class C that holds delegates that can be published by A and subscribed to by B. So class C might be called “MessageManager” or some such, with methods to create a new message type (string that gets hashed into a value for lookup later), this allows you to use the hash to retrieve the delegate again. Class A would register the message type, then once it wants to call it, it retrieves the delegate from the MessageManager class and calls its broadcast. On the subscriber end, class B would simply request the same message (using the same hash) and subscribe to the delegate for receipt.

The point being that A would know about the MessageManager class, but not about B. B would know about the message manager class, but not about A.

Hi there,

delegates cause heavy headaches to me. I have the following setup in my project:

WoEPhotonListener.h


DECLARE_DYNAMIC_DELEGATE_OneParam(FOnOperationResponse, FPhotonOperationResponse, FPOperationResponse);
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnEvent, FPhotonEventData, FPEventData);
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnStatusChanged, int32, FPStatus);
DECLARE_DYNAMIC_DELEGATE_TwoParams(FOnDebugReturn, int32, FPDebugLevel, FString, FPDebugMessage);

class WORLDOFELEMENTS_API WoEPhotonListener : public PhotonListener
{ .....
    FOnOperationResponse ForwardPhotonOperationResponse;
    FOnEvent ForwardPhotonEvent;
    FOnStatusChanged ForwardPhotonStatusChanged;
    FOnDebugReturn ForwardPhotonDebugReturn;

WoEPhotonListener.cpp (Example method)


void WoEPhotonListener::onStatusChanged(int statusCode)
{
    UE_LOG(WoEGeneral, Warning, TEXT("Listener onStatusChanged was called."));
    
    if (ForwardPhotonStatusChanged.ExecuteIfBound(statusCode))
    {
        UE_LOG(WoEGeneral, Warning, TEXT("ForwardPhotonStatusChanged executed."));
    }
    else
    {
        UE_LOG(WoEGeneral, Error, TEXT("ForwardPhotonStatusChanged not executed."));
    }
}

In my WoEPlayerController.cpp I have this in place:


void AWoEPlayerController::PostInitializeComponents()
{
    UE_LOG(WoEGeneral, Warning, TEXT("AWoEPlayerController::PostInitializeComponents called."));

    UWorld* World = GetWorld();

    GI = Cast<UWoEGameInstance>(World->GetGameInstance());

    PListener = GI->PhotonEngine->GetPhotonListener();
    PListener.ForwardPhotonOperationResponse.BindDynamic(this, &AWoEPlayerController::HandleOnOperationResponse);
    PListener.ForwardPhotonEvent.BindDynamic(this, &AWoEPlayerController::HandleOnEvent);
    PListener.ForwardPhotonStatusChanged.BindDynamic(this, &AWoEPlayerController::HandleOnStatusChanged);
    PListener.ForwardPhotonDebugReturn.BindDynamic(this, &AWoEPlayerController::HandleOnDebugReturn);

    if (PListener.ForwardPhotonStatusChanged.IsBound())
    {
        UE_LOG(WoEGeneral, Warning, TEXT("ForwardPhotonStatusChanged bound!"));
    }

    FTimerHandle TimerHandle;
    GetWorldTimerManager().SetTimer(TimerHandle, this, &AWoEPlayerController::CallService, 1.0f, true);

    Super::PostInitializeComponents();
}

Everything compiles without errors or warnings. Pressing play in the editor gives the following log:


[0063.11][310]LogPlayLevel: PIE: Created PIE world by copying editor world from /Temp/Untitled_1.Untitled_1 to /Temp/UEDPIE_0_Untitled_1.Untitled_1 (0.005255s)
[0063.13][310]WoEGeneral:Warning: WoEPhotonEngine Constructor called.
[0063.13][310]WoEGeneral:Warning: WoEPhotonListner Constructor called.
[0063.13][310]WoEGeneral:Warning: WoEPhotonListner created.
[0063.13][310]WoEGeneral:Warning: PhotonPeer created.
[0063.13][310]LogInit: XAudio2 using 'Speaker/HP (Realtek High Definition Audio)' : 2 channels at 48 kHz using 32 bits per sample (channel mask 0x3)
[0063.14][310]LogInit: FAudioDevice initialized.
[0063.19][310]LogWorld: Game class is 'WoELoginGameMode'
[0063.19][310]LogWorld: Bringing World /Temp/UEDPIE_0_Untitled_1.Untitled_1 up for play (max tick rate 60) at 2015.09.18-12.48.33
[0063.19][310]LogActor:Warning: GameSession /Temp/UEDPIE_0_Untitled_1.Untitled_1:PersistentLevel.GameSession_0 has natively added scene component(s), but none of them were set as the actor's RootComponent - picking one arbitrarily
[0063.19][310]LogActor:Warning: GameNetworkManager /Temp/UEDPIE_0_Untitled_1.Untitled_1:PersistentLevel.GameNetworkManager_0 has natively added scene component(s), but none of them were set as the actor's RootComponent - picking one arbitrarily
[0063.19][310]LogWorld: Bringing up level for play took: 0.001802
[0063.19][310]WoEGeneral:Warning: WoEPhotonListner Constructor called.
[0063.19][310]WoEGeneral:Warning: AWoEPlayerController::PostInitializeComponents called.
[0063.19][310]WoEGeneral:Warning: ForwardPhotonStatusChanged bound!
[0063.19][310]PIE: Info Play in editor start time for /Temp/UEDPIE_0_Untitled_1 0.377
[0075.02][782]WoEGeneral:Warning: Listener onStatusChanged was called.
[0075.02][782]WoEGeneral:Error: ForwardPhotonStatusChanged not executed.
[0075.03][782]WoEGeneral:Warning: Listener onStatusChanged was called.
[0075.03][782]WoEGeneral:Error: ForwardPhotonStatusChanged not executed.

What am I missing? Why does it bind in WoEPlayerController but in the declaring listener it states “not bound”? When I use Execute() instead of ExecuteIfBound() I get the following error:


[2015.09.18-10.41.05:521][900]WoEGeneral:Warning: Listener onStatusChanged was called.
[2015.09.18-10.41.05:539][900]LogWindows:Error: Windows GetLastError: Der Vorgang wurde erfolgreich beendet. (0)
[2015.09.18-10.41.12:245][900]LogCrashTracker: 
[2015.09.18-10.41.12:307][900]LogCrashTracker: 
[2015.09.18-10.41.12:307][900]LogWindows: === Critical error: ===
Assertion failed: Object.IsValid() != false [File:C:\Temp\Epic Games\4.9\Engine\Source\Runtime\Core\Public\UObject\ScriptDelegates.h] [Line: 220] 
ProcessDelegate() called with no object bound to delegate!
[2015.09.18-10.41.12:439][900]LogExit: Executing StaticShutdownAfterError

It looks to me like you’re making a copy of this PhotonListener object here. If so, you’ll be copying the delegates, and then you end up binding to the copy. The original delegate that was copied would remain unbound.

Thank you very much - that did the trick. Sometimes I do not see the wood for the trees :eek:

Got this working, thanks folks! The only downside is that because of the order of Player Pawn vs HUD spawning, I have to do a quick ObjectIterator OnCosntruct of the widget. This isn’t too much of an issue since there will usually only be a handful of GOC’s in the world at the start of the game, and this will only happen once per-game per-player. The major advantage of doing this in a manager is that anything in my game can grab it from the static GameInstance and bind if it wants too.

Here’s the final code! I have a GameObject Manager which essentially a Quadtree. Whenever a Component registers with the world, it notifies the tree to add it. Relatively easy injection to then make this fire a Delegate too. Since the Quadtree is static and exists locally for each client, it works fine in MP and PIE too. What’s the fastest way I can modify the code below to make this a static delegate and save some perf?

Also, since UBZGame_GOCManager is already a static / singleton per-world, declaring a variable inside it as static doesn’t make any odds does it? It could / should be a regular var?

UBZGame_GOCManager.h



/* Events for Creation of GameObjects */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FGameObjectCreated, UBZGame_GameObjectComponent*, TheObject);

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


UBZGame_GOCManager.cpp



FGameObjectCreated UBZGame_GOCManager::ObjectCreationDelegate;

void UBZGame_GOCManager::RegisterObjectInTree(UBZGame_GameObjectComponent* NewTreeObject)
{
	if (GameObjectTree)
	{
		GameObjectTree->AddObject(NewTreeObject);
	}

	ObjectCreationDelegate.Broadcast(NewTreeObject);
}


UBZGame_GameObjectComponent.cpp



void UBZGame_GameObjectComponent::OnRegister()
{
	Super::OnRegister();

	if (UBZGame_GameInstance::GetGOCManager(this))
	{
		UBZGame_GameInstance::GetGOCManager(this)->RegisterObjectInTree(this);
	}
}


UBZGame_InGameWidget.cpp



void UBZGame_InGameWidget::NativeConstruct()
{
	Super::NativeConstruct();

	if (GetWorld())
	{
		UBZGame_GameInstance::GetGOCManager(this)->ObjectCreationDelegate.AddDynamic(this, &UBZGame_InGameWidget::OnNewGameObject);
	}

	// TODO - Fix this hack for creating a Widget for the Players Object, which is created before this widget!
	for (TObjectIterator<UBZGame_GameObjectComponent> Itr; Itr; ++Itr)
	{
		if (Itr && Itr->GetWorld() == this->GetWorld())
		{
			OnNewGameObject(*Itr);
		}
	}
}


Just change DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam to DECLARE_MULTICAST_DELEGATE_OneParam, and AddDynamic to AddRaw.

And yes, for a singleton class, it doesn’t make sense for the members to be static.