Broadcast from one Actor class to others?

I am currently trying to find a way to have one actor broadcast a message to N other actors of a different class. I have found the DELEGATE macro but from what I have seen that is only intended for within a single class and does not allow cross-class communication. Does anyone know a way to do this or am I wrong and a DELEGATE would be able to do what I want, just need to figure out how to do so correctly?

The Delegate should be declared on the “Sender” Actor and all the “Receiver” Actors can bind to it no matter what Class they are.
You may not want the other classes to include the “Sender” class though for coupling reasons.

If you don’t mind the “Sender” keeping track of who the “Receivers” are then the “Sender” could keep an array of “Receiver” Object Class References and call an interface function on all of them. The “Receivers” need to implement the Interface of course in order to receive the function call.

Would you be able to give me a code example, when I tried to use the delegate before I was having a ton of issues with getting sender, receiver, and broadcasting working. Also trying to not have an array as the number of receivers is not determined ahead of time.

In this example one Boss instance is expected to exist prior to spawning the Minions.

The Minions find one Boss and Bind to the OnEnraged Delegate when they are spawned.

When Enrage is called on the Boss after the Minions have Bound to it you should see the Log message.

The RageLevel is just an arbitrary integer to show how the Boss can pass a value to the Minions.

UnbindToBoss is called before BindingToBoss to ensure that only one Boss is ever bound at a time.

Boss.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Boss.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FEnragedDelegate, int32, RageLevel);

UCLASS()
class ABoss : public AActor
{
	GENERATED_BODY()

public:
	ABoss();

	UPROPERTY(BlueprintReadWrite, EditAnywhere)
	int32 BossRageLevel;
	
	UFUNCTION(BlueprintCallable)
	void Enrage();
	
	UPROPERTY(BlueprintAssignable)
	FEnragedDelegate OnEnraged;
};

Boss.cpp

#include "Boss.h"

ABoss::ABoss()
{
	BossRageLevel = 1;
}

void ABoss::Enrage()
{
	OnEnraged.Broadcast(BossRageLevel);
}

Minion.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Minion.generated.h"

class ABoss;

UCLASS()
class AMinion : public AActor
{
	GENERATED_BODY()

protected:
	void BeginPlay() override;
	void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

	void BindToBoss();
	void UnbindToBoss();
	UFUNCTION()
	void BossEnragedCallback(int32 RageLevel);

private:
	UPROPERTY()
	ABoss* BossPtr;
};

Minion.cpp

#include "Minion.h"
#include "EngineUtils.h"
#include "Boss.h"

void AMinion::BeginPlay()
{
	Super::BeginPlay();
	BindToBoss();
}

void AMinion::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	UnbindToBoss();
	Super::EndPlay(EndPlayReason);
}

void AMinion::BindToBoss()
{
	UnbindToBoss();
	UWorld* MyWorld = GetWorld();
	if (MyWorld && MyWorld->IsGameWorld())
	{
		for (TActorIterator<ABoss> It(MyWorld); It; ++It)
		{
			ABoss* Boss = *It;
			BossPtr = Boss;
			Boss->OnEnraged.AddDynamic(this, &AMinion::BossEnragedCallback);
			return;
		}
	}
}

void AMinion::UnbindToBoss()
{
	if (BossPtr)
	{
		BossPtr->OnEnraged.RemoveDynamic(this, &AMinion::BossEnragedCallback);
		BossPtr = nullptr;
	}
}

void AMinion::BossEnragedCallback(int32 RageLevel)
{
	UE_LOG(LogTemp, Log, TEXT("Boss Enraged Callback on %s with RageLevel =%d"), *GetHumanReadableName(), RageLevel);
}
4 Likes

Thanks, Ill give it a try again and update when I can.

The Array can be updated dynamically. You would essentially make your own invocation list of objects similar to how a Delegate works.