Skip to content

Delegates

Ruslan Mustakov edited this page May 16, 2016 · 1 revision

Delegates

UE4 delegates allow you to call functions bound to them in a generic and type-safe way. The concept is similar to C# delegates.

Delegates are often used to implement events in a game - an actor can notify others when something of interest has happened without having to know who exactly is subscribed to the event.

nimue4 provides udelegate macro to declare delegate types:

type DelegateKind* = enum
  dkSimple,
  dkMulticast,
  dkDynamic,
  dkDynamicMulticast,
  dkSimpleRetVal,
  dkMulticastRetVal,
  dkDynamicRetVal,
  dkDynamicMulticastRetVal

macro udelegate*(name: expr, kind: DelegateKind, params: varargs[expr]): stmt

Examples of declaring different kinds of delegates:

# Simple delegate with no parameters and no return value
udelegate(FSomethingHappened, dkSimple)

# Dynamic multicast delegate with 3 parameters
udelegate(FCharacterMovementUpdated, dkDynamicMulticast,
          deltaSeconds: cfloat, oldLocation: FVector, oldVelocity: FVector)

# Dynamic multicast delegate with 3 parameters and boolean return value
# For RetVal delegates the third parameter denotes return value type
udelegate(FOnProcessEvent, dkDynamicMulticastRetVal, bool,
          actor: ptr AActor, f: ptr UFunction, obj: ptr UObject)

# Simple delegate with no parameters and bool return value
udelegate(FCanUnpause, dkSimpleRetVal, bool)

After a delegate is declared, you can use it as an ordinary type:

# Gunner.nim
import ue4

udelegate(FNoAmmo, dkDynamicMulticast)

uclass AGunner of AActor:
  var ammo* {.editorReadOnly, bpReadOnly, category: "Gunner".}: int32
  var onNoAmmo* {.bpDelegate.}: FNoAmmo

  method beginPlay() {.override, callSuper.} =
    ammo = 42
    onNoAmmo.addDynamic(this, noAmmoHandler)

  method tick(deltaSeconds: float32) {.override, callSuper.} =
    if ammo > 0:
      dec ammo
      if ammo == 0:
        onNoAmmo.broadcast()

  proc noAmmoHandler() =
    ueLog("Oh no, the ammo is wasted!")

Procedures supported by all delegates are:

proc clear[T](delegate: T)
proc isBound[T](delegate: T): bool

Depending on their type, delegates support the following procedures and templates:

# Execution for non-multicast delegates:
proc execute(delegate: FYourDelegate, #[delegate params]#): #[delegate retval]#
proc executeIfBound(delegate: FYourDelegate, #[delegate params]#): #[delegate retval]#

# Binding for non-multicast dynamic delegates:
template bindDynamic(delegate: typed, obj: ptr, methodName: untyped)

# Binding for simple non-dynamic delegates:
template bind(delegate: typed, obj: ptr, methodName: untyped)
template bindUObject(delegate: typed, obj: ptr, methodName: untyped)

# Execution for multicast delegates:
proc broadcast(delegate: FYourDelegate, #[delegate params]#)

# Binding for non-dynamic multicast delegates:
template add(delegate: typed, obj: ptr, methodName: untyped)
template addUObject(delegate: typed, obj: ptr, methodName: untyped)

# Binding for dynamic multicast delegates:
template addDynamic(delegate: typed, obj: ptr, methodName: untyped)

# Unbinding for dynamic delegates:
template removeDynamic(delegate: typed, obj: ptr, methodName: untyped)

# Unbinding for multicast delegates:
proc removeAll[T](delegate: T, obj: ptr UObject)
Clone this wiki locally