Unreal Engine: How to declare a custom event in C++

This tutorial will show you how to declare a custom event in C++ and expose it to Blueprints.

Disclaimer

I'm new to Unreal Engine. So while learning new stuff, I came to the point where I needed to declare a custom event in C++ and be able to use it in Blueprints.

I've spent some time researching, and there is a lot of confusion about it. So I decided to write this small article.

Why do you need custom events?

Declaring custom events with C++ in Unreal Engine allows you to create specific functions that can be called or triggered in your game code. You can use these events to perform various actions, such as modifying variables, spawning objects, or communicating between different classes/blueprints.

Let's consider a simple example. Let's say you have an ActorComponent called USHealthAttributeComponent with a variable representing your character's Health. You may want to create a function that listens for changes in the Health variable and:

  • React to these changes and update the health bar in the game UI.
  • Make your character non-interactive and play death animation when the Health variable reaches zero.

Where confusions come from

Official documentation on tells you to use DECLARE_EVENT macro to declare a custom event.

So my first attempt was to declare a custom event like this:

UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class GAME_API USHealthAttributeComponent : public UActorComponent
{
    ...
public:
  /**
    * Event called when Health is changed.
    * It takes four parameters:
    * - AActor* - pointer to the Instigator (the one who caused the health change, can be nullptr)
    * - UPawnAttributeComponent* - pointer to the component that called the event
    * - float - New health value
    * - float - Old health value
    */
  DECLARE_EVENT_FourParams(USHealthAttributeComponent, FOnHealthChanged,
    AActor* /* Instigator */,
    UPawnAttributeComponent* /* AttributeComponent */,
    float /* NewHealthValue */,
    float /* NewHealth */
  )
  FOnHealthChanged OnHealthChanged;
}

I spent some time trying to figure out why I can't see this event and can't call it from Blueprints. I tried to apply UPROPERTY macro:

// `BlueprintAssignable` exposes the property for assigning in Blueprints.
UPROPERTY(BlueprintAssignable)

But you can't use UPROPERTY macro with DECLARE_EVENT macro. You'll get a compilation error.

So I've started to look for other ways to declare custom events and checked the forums. I've found many people asking the same question, and all answers pointed to the DECLARE_DYNAMIC_MULTICAST_DELEGATE macro.

I wasn't familiar with all sorts of delegate macros in Unreal Engine, so I didn't pay much attention to what kind of the delegate should I use and tried DECLARE_MULTICAST_DELEGATE:

DECLARE_MULTICAST_DELEGATE_FourParams(FOnHealthChanged, AActor*, USAttributeComponent*, float, float);

UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class GAME_API USHealthAttributeComponent : public UActorComponent
{
    ...
public:
  UPROPERTY(BlueprintAssignable)
  FOnHealthChanged OnHealthChanged;
}

And it gives me a compilation error. So it's time to go deeper.

Delegates and Events

What is a delegate? It's just a function pointer that you can assign to a variable and call later. In Unreal Engine, you can define several types of delegates. I'll describe the most common ones.

Let's start with understanding the serialization concept and how it relates to delegates.

Serialization

What does it mean to be serializable? It means you can convert an object to bytes, store it in a file, and restore it later.

For delegates, it means that you can convert the event graph with all delegate connections to bytes, store it in project files, and then restore it when reopening the project.

Non-dynamic and dynamic delegates

First, you need to know that there are two types of delegates: non-dynamic and dynamic.

The most common difference between them is that you can not use non-dynamic delegates in Blueprints because they are non-serializable. On the contrary, it makes non-dynamic delegates faster.

Multicast and single-cast delegates

Another essential concept it's a way of broadcasting: to single or multiple targets. So there are two types of delegates: multicast and single-cast.

  • For multicast delegates, you can assign as many receivers as you want.
  • For single-cast delegates, you can assign only one receiver; every time you assign a new one, the previous one will be replaced.

Events

Events are a special type of delegates, bound to a specific OwningType, within which only they can be called. Events also can be dynamic, non-dynamic, multicast, or single-cast.

The most significant disadvantage of the events - they are not supporting the possibility of passing arguments to receivers.They are just firing notifications that something happened without any specific data.

Also, If you'll take a look at the source code of DECLARE_EVENT macro, you'll find a note

NOTE: This behavior is not enforced and this type should be considered deprecated for new delegates, use normal multicast instead

How to declare a custom event?

We can finally declare a custom event in C++ with all this information.

We want to:

  1. Give several receivers a chance to get the information that Health has changed.
  2. Pass the information about the change, what caused it, the new health value, and what the old one was.
  3. Be able to use this event in Blueprints.

So we need a dynamic multicast delegate with four parameters. Let's use DECLARE_DYNAMIC_MULTICAST_DELEGATE macro:

DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(
  FOnHealthChanged, // Name of the structure that will be generated
  // Parameters of the delegate (Type, Name):
  AActor*, Instigator, // Who caused the health change
  USHealthAttributeComponent*, ChangedComponent, // Component that called the event
  float, NewValue, // New health value
  float, OldValue // Old health value
);

UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class GAME_API USHealthAttributeComponent : public UActorComponent
{
...
public:
  UPROPERTY(BlueprintAssignable)
  FOnHealthChanged OnHealthChanged;
}

That's it. Now we can use our custom event in Blueprints. In order to trigger it, we need to call Broadcast method:

void USHealthAttributeComponent::SetHealth(float NewHealth, AActor* Instigator = nullptr)
{
  if (NewHealth != Health)
  {
    const float OldHealth = Health;
    Health = NewHealth;
    OnHealthChanged.Broadcast(Instigator, this, NewHealth, OldHealth);
  }
}

More information

If you want to learn more about delegates, I recommend reading Ben UI's article.

All rights reserved.