Dispatch/bind (delegate) like interfaces?

Hi! I want to know if possible to bind a delegate (dispatch) from an object without casting it. Like interfaces.

For example:

I have 5 different player controller classes and an actor that works in different environments.

I want to make a dispatch call from any of the player controllers to trigger the event in the actor,
but I dont want to cast the player controller class for the binding.

I want to decouple the code from player controller classes.

Actualy player controllers and an actor is just for the example purpose. I just want to bind a dispatch event from an actor into another without know its class. The only method coming to my mind is just making a base class for all the player controllers and put the dispatchers there so the actor just always casts to the base class where the dispatchers are. But I dont like this because I keep relying into casting (not decoupling class)

1 Like

I would love to know if this exists, but I don’t think it does… :thinking:

1 Like

you could use an actor component or even an object to hold your events, then your owner can call events on the component and other actors can get the component by interface and bind to that

1 Like

But each actor component is a separate instance, no?

It would be nice to be able to bind lots of actors to one central object.

It can be done with C++

The Lyra Team Agent Interface is doing something similar with the OnTeamChangedDelegate, and you can probably take it one step further and expose it to blueprint. But I don’t think it’s possible at all in BP.

You would just need to declare a delegate
DECLARE_DYNAMIC_DELEGATE(FMyDelegate);

And then in your interface create a function like so:

UFUNCTION(BlueprintCallable)
virtual void MyFunction(const FMyDelegate& MyDelegate);

Then in your implementing classes you could have a field for that delegate:

FMyDelegate MyObjectsDelegate;

And implement the function:

void MyFunction(const FMyDelegate& MyDelegate) override
{
   MyObjectsDelegate = MyDelegate;
};

And then somewhere else in your object you’d do:

void MyDelegateBroadcastingFunction()
{
    MyObjectsDelegate.ExecuteIfBound();
};

This has one quirk, which is that it can’t be bound to multiple functions at once, so you can have only one listener

But I think you can combine it with a multicast delegate, and bind the delegate from your function argument to the multicast

DECLARE_DYNAMIC_DELEGATE(FMyDelegate);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FMyDelegateMulticast);

...
UFUNCTION(BlueprintAssignable)
FMyDelegateMulticast MyMulticastDelegateProperty;

void MyFunction(const FMyDelegate& MyDelegate) override
{
    MyMulticastDelegateProperty.AddUnique(MyDelegate);
}

I suppose you can also have the delegate be part of the interface (similar to the LyraTeamAgentInterface):

virtual FMyDelegateMulticast* GetMyMulticastDelegate() { return nullptr; }

FMyDelegateMulticast& GetMyMulticastDelegateChecked()
{
    FMyDelegateMulticast* MyDelegate = GetMyMulticastDelegate();
    check(MyDelegate);
    return *MyDelegate;
}

And then combine everything together:

// This can now be your interface function, probably should not be virtual
void MyFunction(const FMyDelegate& MyDelegate)
{
    GetMyMulticastDelegateChecked().AddUnique(MyDelegate);
}

And in your implementing class you add a FMyMulticastDelegate Property, override the GetMyMulticastDelegate() function to return the property, and that should be all

Disclaimer, I wrote this code in this reply box, so it’s not fully tested, but I have done this exact thing before and it should work

1 Like

Man, looks like a huge hassle :joy:

1 Like

It’s not too bad, it’s basically 2 delegates, 1 function to return the delegate, and 1 function to bind to the delegate. But yeah it cannot be implemented in a blueprint unfortunately

2 Likes

no there is only one ActorComponent which all 5 player controllers reference, unless you want different events of course.

I use 3 in my game, Gameplay events which i attach to the Gamemode and all actors can bind to, player events which is usually just for local player events like input, targeting.
and target events which i put on my actors to broadcast hits, death etc

you can take it further too, my event dispatchers just pass through a GameplayTag to say what event happened, then any ‘payload’ needed i get via interface, this way i can reuse the same signature and often only have a small number of dispatchers

2 Likes

alternatively but slower and less performant is to just use interfaces completely, ie to bind to an event you just send an object (target) via interface to the event owner which adds it to an array. then when you call the event you loop over the array and call an event via interface on all ‘listening’ objects.

three advantages to this is you can get a list of all ‘listening’ objects for debugging, you can return values too although im not sure how useful this is yet and finally you know when the loop has completed if order matters for something

2 Likes

So I’m missing something here :slight_smile: how do I add the same ( one ) AC to more than one actor?

you mean the same instance?

using the OPs example
the AC instance lives on the player controller, which only one type would exist. the actors can then GetPlayerController->GetComponentByClass->BindToEvent

this would work for all 5 of his player controllers

2 Likes

Ok, that’s nice :smiley:

I agree using interface for auto reference self objects and keep all into an array and then using a reversing call function is the best approach for now :slight_smile: (both ends implementing same interface but one function for each direction)