Archivo de la etiqueta: Delegates

Introducción a los Delegates en Unreal Engine 4

En el desarrollo de videojuegos algo que necesitamos muy a menudo es notificar a los distintos elementos del juego cuando ocurre algún evento, por ejemplo, cuando un personaje muere, cuando las fuerzas enemigas comienzan a atacar nuestra base, cuando alcanzamos un objetivo determinado del juego . . . en fin, en muchísimos casos. Es precisamente en estas situaciones donde nos vienen a ser de gran ayuda los Delegates.

NOTA: Este tutorial ha sido desarrollado con Unreal Engine 4.6.1, si estás trabajando con otra versión puede que encuentres algunas diferencias, ya que el Engine está en constante actualización. De ser así, déjame tus comentarios al final del post y buscamos juntos la solución.

Imaginemos un caso real. Supongamos que tenemos en nuestro juego al personaje protagónico y a un grupo de distintos personajes secundarios, ya sean enemigos, aliados o lo que sea, y queremos ejecutar algún comportamiento en cada uno de estos otros personajes cuando el PlayerCharacter muere.

Una vía de hacer esto es obtener la referencia del Player Character en el “Event Tick“ de cada uno de esos personajes, y preguntar por alguna variable que tengamos creada en el Character que defina su estado en el juego.

Esta solución funciona, es verdad . . . pero es terriblemente mala !!, porque cada uno de los actores tienen que estar ejecutando un método constantemente para poder saber el estado del Player, con el consiguiente gasto de recurso que esto implica, sobre todo si estamos hablando de un juego relativamente grande, donde varios actores estén ejecutando esto en paralelo. Esto sería mucho más desastroso si el proceso no fuera solo preguntar por el valor de alguna variable, sino realizar algún calculo o algo por el estilo, aquí el gasto de recursos sería aún mayor.

Pues es precisamente esta una de las situaciones en donde los Delegates vienen a salvarnos la vida. Un Delegate nos dan la posibilidad de “bindiar“ a él uno o más métodos de otras clases. De esta forma podemos en cualquier momento “llamar“ este Delegate y esto nos permitirá ejecutar en cada una de esas instancias que “bindiaron” métodos a él, esos métodos. O sea, que esto nos permite ejecutar un método de otra clase en el momento que queramos, sin ni tan siquiera tener acceso directo a ese método. Genial no !!??

Pues si volvemos a nuestro ejemplo, para solucionar este mismo problema podemos crear un Delegate en la clase del personaje protagónico. Al iniciar el juego (o en cualquier otro punto que queramos), le decimos a los otros Actores que registren un método de ellos que se va a llamar inmediatamente en el momento en el que este Delegate sea lanzado. Hecho esto, en el momento en el que el Player muere, ”lanzamos“ ese Delegate, y automáticamente será llamado en todas las instancias de las clases que “bindiaron” métodos a él, ese método. Método que tendría una implementación particular en cada una de las clases. Por ejemplo, en el enemigo se puede ejecutar alguna animación de alegría, y en el aliado, el método puede ejecutar alguna animación de tristeza, o puede atacar directamente al enemigo . . . o cualquier otra cosa mucho más original y menos absurda :)

Mucho mejor esta solución, verdad ?

Para entenderlo mejor y ver en la práctica como usarlo, vamos a preparar un pequeñito ejemplo. Veremos primero como trabajar con Delegates desde C++ para entenderlo todo a bajo nivel, después veremos la implementación desde los Blueprints, y queda por tu parte seleccionar la vía que más te guste cuando los vayas a implementar en tu juego 😉

Para este simple ejemplo he creado un proyecto nuevo en UE4 a partir de la plantilla Top-Down en C++ y le he agregado 2 nuevas clases de Characters: RedCharacter y GreenCharacter. Después de esto he creado dos Blueprints a partir de estas clases, RedCharacterBlueprint y GreenCharacterBlueprint. Para el Skeletal Mesh de ambos he usado el mismo que usa MyCharacter al igual que con el Blueprint Animation.

RedCharacter y GreeCharacter podemos verlos como dos personajes cualesquiera que puede tener nuestro juego.

Para diferenciarlos un poco vamos a crear dos Materiales muy simples, que sean de un color completo. Los Materiales en Unreal son un tema bastante complejo y están más en el campo de los diseñadores, así que no nos detendremos en ellos ahora mismo. Para crear un material da clic derecho en el Content Browser dentro de la carpeta donde lo quieras crear, selecciona Material y dale un nombre, en este caso yo lo voy a llamar RedMaterial. Dale doble clic para abrir el Material Editor que es el Editor de Materiales que viene con el Unreal Editor.

Crea un nodo de tipo “Constant 3Vector“ y en el panel de detalles de este nodo da clic en la propiedad Constant y selecciona el color rojo en el Picker Color que se te mostrará. Por último conecta este nodo al Base Color del nodo RedMaterial. Con esto hemos creado un material súper simple que al aplicarlo en el Mesh del RedCharacter hará que este se vea rojo en su totalidad, así lo podremos distinguir fácilmente.

Captura del RedMaterial en el Material Editor

Captura del RedMaterial en el Material Editor

 

Por último, abre el RedCharacterBlueprint en el modo Components, selecciona el Mesh y en la sección Rendering verás la propiedad Materials que es un array. Agrega un elemento dando clic en el símbolo de + y en el ítem que se agrega, despliega el combo y selecciona el RedMaterial que acabamos de crear. Verás como inmediatamente el personaje toma color rojo.

Captura del RedCharacterBlueprint en el modo Components después de settear el RedMaterial como Material del Mesh

Captura del RedCharacterBlueprint en el modo Components después de settear el RedMaterial como Material del Mesh

 

Ahora repite todo el proceso para configurar el GreenCharacterBlueprint, crea asígnale a este un material de color verde.

Por último, agrega el nivel cerca del PlayerStart una instancia del RedCharacter y una instancia del GreenCharacter y elimina los obstáculos que trae el nivel de esta plantilla top-down. Con eso ya tendremos nuestra escena lista.

Captura del nivel preparado después de eliminar los obstáculos y agregar las instancias del RedCharacterBlueprint y GreenCharacterBlueprint

Captura del nivel preparado después de eliminar los obstáculos y agregar las instancias del RedCharacterBlueprint y GreenCharacterBlueprint

 

Como comentamos, lo que queramos es implementar un mecanismo que nos permita notificar a los otros dos personajes (Green y Red) cuando ocurre un evento determinado en el juego, en este caso, cuando nuestro personaje muere, y para esto usaremos los Delegates.

Implementando un Delegate desde C++ en Unreal Engine

En Unreal Engine 4 podemos declarar dos tipos de Delegates, Single-cast y Multi-cast. Los Single-cast nos van a permitir notificar solo a un objeto determinado de nuestro juego, por ejemplo, si quisiéramos que solo el RedCharacter se entere de la muerte de nuestro personaje. Por otra parte, los Multi-cast nos permiten notificar a todas las clases que queramos. En la mayoría de los casos usaremos Multi-cast, ya que estos también podemos usarlo para notificar a solo una clase, así que vamos directamente a ver los multi-cast, y comentamos en cada punto la diferencia a los Single-cast.

Como dijimos al inicio, el objetivo de los Delegates es permitirnos ejecutar un método determinado de otro objeto cuando ocurre algún evento del juego, incluso sin tener acceso directo al método de ese objeto, pero como sabes, los métodos pueden tener distintas estructuras. Un método puede no retornar ningún parámetro ni recibir parámetros. Puede también no retornar nada, pero recibir un parámetro o dos o los que sean y así tenemos muchas variantes. Pues para declarar los Delegates en C++ el compilador de Unreal nos brinda algunos macros específicos según el tipo de método que vayamos a ejecutar al lanzar el delegate.

Function signature Declaration macro
void Function() DECLARE_MULTICAST_DELEGATE( DelegateName )
void Function( ) DECLARE_MULTICAST_DELEGATE_OneParam( DelegateName, Param1Type )
void Function( , ) DECLARE_MULTICAST_DELEGATE_TwoParams( DelegateName, Param1Type, Param2Type )
void Function( , , … ) DECLARE_MULTICAST_DELEGATE_Params( DelegateName, Param1Type, Param2Type, … )
Function() DECLARE_MULTICAST_DELEGATE_RetVal( RetValType, DelegateName )
Function( ) DECLARE_MULTICAST_DELEGATE_RetVal_OneParam( RetValType, DelegateName, Param1Type )
Function( , ) DECLARE_MULTICAST_DELEGATE_RetVal_TwoParams( RetValType, DelegateName, Param1Type, Param2Type )
Function( , , … ) DECLARE_MULTICAST_DELEGATE_RetVal_Params( RetValType, DelegateName, Param1Type, Param2Type, … )

Para nuestro ejemplo usaremos el caso más simple que es ejecutar un método sin valor de retorno ni parámetros, pero al final la lógica es la misma para cada caso.

En el caso de la declaración de los Delegates Single-cast, la lógica es la misma, solo que se usa una variación del macro sin la palabra MULTICAST, por ejemplo para el primer caso sería DECLARE_DELEGATE

Lo primero que vamos a hacer es declarar este delegate. Abre el .h de la clase del PlayerCharacter y antes de la declaración de la clase agrega la declaración del Delegate de la siguiente forma:

DECLARE_MULTICAST_DELEGATE( FMulticastDelegateSample );

En este caso lo que acabamos de declarar es un multi-cast delegate al que se le pueden “bindiar“ métodos que no retornan nada ni reciben parámetros. El próximo paso es agregar una instancia de este delegate en la clase que lanzaría el evento. En este caso es nuestro personaje protagónico así que agrega como variable de clase del personaje, lo siguiente:

FMulticastDelegateSample MulticastDelegate;

Ahora solo nos queda ejecutar este delegate en el momento que queramos. En este caso vamos a crear un método Die en el Player, este método se llamará cuando el personaje muera y en ese momento se lazará el delegate. Para adornar un poco el momento de la muerte, reproduciremos una animación dentro de este método Die así que agrega también una instancia de tipo UAnimationAsset para cargar el Asset de animación desde el Editor, como hemos hecho en tutoriales anteriores.

Agrega lo siguiente en el .h

/** Asset de la animacion de muerte */
UPROPERTY(EditDefaultsOnly, Category = Animations, meta = (AllowPrivateAccess = "true"))
UAnimationAsset *DeathAnimation;

UFUNCTION(BlueprintCallable, Category=Default)
void Die();

Ahora pasa al .cpp y agrega la implementación del método

void AUE4SampleCharacter::Die()
{
    //Reproduce la animación de muerte
    if(DeathAnimation)
    {
        GetMesh()->PlayAnimation(DeathAnimation, false);    
    }
    
    //Lanza el Delegate para notificar en todos los objetos que bindiaron método a este delegate que el PlayerCharacter acaba de morir
    MulticastDelegate.Broadcast();
}

Como puedes ver, es súper simple lanzar el delegate. Suficiente con llamar al método Broadcast(). Una vez que se llama este método se ejecutará cada uno de los métodos que fueron bindiados a él en los objetos correspondientes.

Dos cosas a tener en cuenta con el Broadcast() es que lo podemos llamar incluso si al delegate no se le ha bindiado ningún método, como es en este preciso momento, y otra cosa importante es que en caso que tengamos bindiado más de un método, el orden de ejecución de estos no es garantizado, así que tenlo en cuenta y no implementes ninguna lógica que pueda depender de este orden.

Para el caso de los Single-cast, para llamarlos tenemos los métodos Execute(), ExecuteIfBound() y IsBound().

Con esto que hemos hecho hasta ahora ya tenemos creado y ejecutamos el delegate en el momento que queremos, pero no hemos bindiado a él nada, por lo que al ejecutar el Broadcast() simplemente no pasará nada. Así que vamos a implementar el método que queremos llamar en el GreenCharacter y bindiarlo al delegate.

Bindiando métodos al multi-cast delegate desde C++

Modifica la clase GreenCharacter para que te quede de la siguiente forma:

//----------------------------
// GreenCharacter.h
//----------------------------

#pragma once

#include "GameFramework/Character.h"
#include "GreenCharacter.generated.h"

UCLASS()
class UE4SAMPLE_API AGreenCharacter : public ACharacter
{
	GENERATED_BODY()

    /** Asset de la animacion de muerte */
    UPROPERTY(EditDefaultsOnly, Category = Animations, meta = (AllowPrivateAccess = "true"))
    UAnimationAsset *DeathAnimation;

    /** Método que se llama en este clase, mediante el MulticastDelegate del PlayerCharacter cuando este muere */
    void OnPlayerCharacterDie();

    void Die();
    
    virtual void BeginPlay() override;

};

//----------------------------
// GreenCharacter.cpp
//----------------------------

#include "UE4Sample.h"
#include "GreenCharacter.h"
#include "UE4SampleCharacter.h"
#include "Engine.h" //Para poder usar el GetWorldTimerManager().SetTimer

void AGreenCharacter::BeginPlay()
{
    Super::BeginPlay();
    
    //Obtiene la referencia del PlayerCharacter
    AUE4SampleCharacter* PlayerCharacter = Cast<AUE4SampleCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
    
    //"Bindea" el método OnPlayerCharacterDie de esta clase al delegate que se lanza cuando el PlayerCharacter muere.
    PlayerCharacter->MulticastDelegate.AddUObject(this, &AGreenCharacter::OnPlayerCharacterDie);
}

/** Método que se llama en este clase, mediante el MulticastDelegate del PlayerCharacter cuando este muere */
void AGreenCharacter::OnPlayerCharacterDie()
{
    //Delay de 1 segundo antes de llamar al método Die de esta clase
    GetWorldTimerManager().SetTimer(this, &AGreenCharacter::Die, 1.0, false);
}

void AGreenCharacter::Die()
{
    //Reproduce una animación de muerte
    if(DeathAnimation)
    {
        GetMesh()->PlayAnimation(DeathAnimation, false);
    }
}

El método OnPlayerCharacterDie, que es el método que vamos a bindiar al delegate del PlayerCharacter, simplemente ejecuta un delay y al segundo llama al método Die, que al igual que el método Die del PlayerCharacter, lo que hace es reproducir una animación de muerte. Cuando el PlayerCharacter muera y se notifique a este objeto, el GreenCharacter esperará un segundo (para asimilar la noticia de que su compañero murió :( . . . ) y también morirá . . . Un poco trágica la historia ahora que lo pienso, así que en tu juego usa los delegates para cosas más alegres :)

OnPlayerCharaterDie será el método que se llamará automáticamente cuando el Delegate que creamos en el Character se dispare, pero para esto tenemos que bindiar el método al delegate y esto vamos a hacerlo en el BeginPlay.

Cuando necesitamos bindiar el método de alguna clase a un delegate determinado, una buena idea es hacerlo en el BeginPlay de esos actores, para ya no tener que preocuparnos de eso en el transcurso del juego. Sobrescribe el BeginPlay de esta clase para que te quede de la siguiente forma:

void AGreenCharacter::BeginPlay()
{
    Super::BeginPlay();
    
    //Obtiene la referencia del PlayerCharacter
    AUE4SampleCharacter* PlayerCharacter = Cast<AUE4SampleCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
    
    //"Bindea" el método OnPlayerCharacterDie de esta clase al delegate que se lanza cuando el PlayerCharacter muere.
    PlayerCharacter->MulticastDelegate.AddUObject(this, &AGreenCharacter::OnPlayerCharacterDie);
}

En el BeginPlay de este character simplemente obtenemos la referencia al Character accedemos a la instancia del delegate y mediante el método AddUObject bindiamos el método que queremos ejecutar. El primer parámetro que recibe el AddUObject es la instancia de la clase que contiene el método que vamos a ejecutar, en este caso esta misma clase, por eso el uso de “this” y el segundo es el método que se va a ejecutar. Si te fijas es muy parecido a lo que hacemos al bindiar los métodos a las entrada del usuario. En ese caso lo que hacemos es justamente esto mismo, bindiar un método a un delegate que se llama cuando el Engine detecta la entrada del usuario.

En este punto vale aclarar que por lo general vamos a bindiar a un delegate un método de un instancia UObject, pero el framework también nos brinda los métodos Add(), AddStatic(), AddRaw() y AddSP() para bindiar al delegate otro tipo de funciones, como por ejemplo punteros a funciones globales. Para una referencia más completa de cada uno de los casos puedes darte una vuelta por: docs.unrealengine.com

Algo a tener en cuenta es que de la misma forma que agregamos métodos al delegate, podemos eliminarlos. Esto nos puede ser útil si a partir de determinada condición ya no queremos que se notifique mas a un objeto de determinado evento. Para esto podemos usar los métodos del delegate: Remove() y RemoveAll()

Para bindiar métodos en el caso de los Single-cast delegates tenemos los mismos métodos pero sustituyendo la palabra Add por Bind. Por ejemplo, para los multi-cast tenemos AddUObject y para los single cast tenemos BindUObject. Para eliminar tenemos el método UnBind()

Listo !! . . . esto es todo lo que necesitamos. Guarda, compila y abre el editor.

Para facilitar la muerte de nuestro personaje (el evento que queremos notificar en todos los otros personajes) vamos a forzarla a que suceda cuando se toque la barra espaciadora, no es muy real pero nos permitirá centrarnos en el asunto que de verdad nos interesa en este tutorial. Abre el Project Settings y en la sección Input agrega una entrada de tipo Action, ponle de nombre KillPlayerCharacter y que se active con la barra espaciadora

Ahora abre el Blueprint MyCharacter. Primero en el modo Default carga para la propiedad DeathAnimation una animación de muerte cualquiera que puedes obtener haciendo un Retarget de cualquiera de las animaciones que vienen en el AnimStarterPack.

Pasa al modo Graph y agrega lo siguiente para que cuando se presione la barra espaciadora se llame al método Die que implementamos en el PlayerCharacter.

Captura del Event Graph del PlayerCharacter. Ejecuta el método Die cuando se detecta el InputAction KillPlayerCharacter (Barra Espaciadora)

Captura del Event Graph del PlayerCharacter. Ejecuta el método Die cuando se detecta el InputAction KillPlayerCharacter (Barra Espaciadora)

 

Hecho eso, compila, guarda y dale Play al juego. Presiona la barra espaciadora para forzar la muerte de nuestro Character y verás que un segundo después de la muerte del personaje protagónico, el GreenCharacter también cae al suelo. Lo que nos demuestra que se llamó el método correspondiente en esa instancia en el momento preciso. Genial verdad !!??

 

Captura de dos momentos del juego. El primer cuadro, al iniciar el juego. El segundo cuadro, al tocar la barra espaciadora el PlayerCharacter muere, lanza el Delegate de que murió, es notificado el GreenCharacter mediante el método OnPlayerCharacterDie, inicia un Timer por 1 segundo y después llama al método Die, que hace que el GreenCharacter muera también.

Captura de dos momentos del juego. El primer cuadro, al iniciar el juego. El segundo cuadro, al tocar la barra espaciadora el PlayerCharacter muere, lanza el Delegate de que murió, es notificado el GreenCharacter mediante el método OnPlayerCharacterDie, inicia un Timer por 1 segundo y después llama al método Die, que hace que el GreenCharacter muera también.

 

Bindiando más de un método de distintos objetos a un mismo Delegate

Como dijimos al inicio, la ventaja que tenemos con los delegate Multi-cast es que no estamos limitados a bindiar un solo método, sino que podemos bindiar todos los métodos que queramos de distintas clases. Por ejemplo, supongamos que también queremos que cuando muera nuestro personaje “se entere“ el RedCharacter y que ejecute otra lógica.

Pues para esto no tenemos que hacer nada en espacial. Simplemente implementar el método que queramos en el RedCharacter y bindiarlo al delegate del PlayerCharacter en el BeginPlay, como mismo hicimos con el GreenCharacter:

//----------------------------
// RedCharacter.h
//----------------------------

#pragma once

#include "GameFramework/Character.h"
#include "RedCharacter.generated.h"

UCLASS()
class UE4SAMPLE_API ARedCharacter : public ACharacter
{
	GENERATED_BODY()
	
    /** Material a usar en el Mesh de este Character cuando el PlayerCharacter muere */
    UPROPERTY(EditDefaultsOnly, Category = Materials, meta = (AllowPrivateAccess = "true"))
    UMaterial *PlayerCharacterDeadMaterial;

    /** Método que se llama mediante el MulticastDelegate del PlayerCharacter cuando este muere */
    void OnPlayerCharacterDie();

    virtual void BeginPlay() override;
};

//----------------------------
// RedCharacter.cpp
//----------------------------

#include "UE4Sample.h"
#include "RedCharacter.h"
#include "UE4SampleCharacter.h"

void ARedCharacter::BeginPlay()
{
    Super::BeginPlay();
    
    //Obtiene la referencia del PlayerCharacter
    AUE4SampleCharacter* PlayerCharacter = Cast<AUE4SampleCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
    
    //"Bindea" el método OnPlayerCharacterDie de esta clase al delegate que se lanza cuando el PlayerCharacter muere.
    PlayerCharacter->MulticastDelegate.AddUObject(this, &ARedCharacter::OnPlayerCharacterDie);
}

/** Método que se llama mediante el MulticastDelegate del PlayerCharacter cuando este muere */
void ARedCharacter::OnPlayerCharacterDie()
{
    if(PlayerCharacterDeadMaterial)
    {
        //Cambia "al vuelo" el primer Material del Mesh de este personaje
        GetMesh()->SetMaterial(0, PlayerCharacterDeadMaterial);
    }
}

Este método lo que hará será cambiar el Material del Mesh del RedCharacter al que carguemos desde el Editor en la propiedad PlayerCharacterDeadMaterial. Para el ejemplo yo preparé un Material de color negro, de esta forma cuando el RedCharacter sea notificado de la muerte del PlayerCharacter este se pondrá negro al instante.

Guarda, compila y lanza el juego. Toca la barra espaciadora y verás como el RedCharacter cambia a color negro al instante y el GreenCharacter, al pasar un segundo, cae al suelto.

 

Captura del juego después que muere el PlayerCharacter. Son notificados ambos personajes. GreenCharacter muere al segundo y RedCharacter cambia su color a negro.

Captura del juego después que muere el PlayerCharacter. Son notificados ambos personajes. GreenCharacter muere al segundo y RedCharacter cambia su color a negro.

 

Muy bien, con este ejemplo hemos podido ver el enorme potencial de los delegates y la forma de usarlos desde C++, pero como una de las potencialidades más grande que tiene el Unreal Engine es su mecanismo de VisualScripting mediante los blueprints, vamos a ver como implementar este mismo tipo de mecanismos desde los blueprints.

Introducción a los Event Dispatchers en Unreal Engine 4.

Como sabes, gracias a “la magia“ que ha logrado el equipo de Epic con los Blueprints para abstraernos de toda la complejidad que puede tener para muchos el asunto de C++, todo esto lo podemos hacer súper fácil y rápido también desde Blueprints, aquí los nombres varían un poco pero el concepto es el mismo.

En los blueprints podemos crear Event Dispatchers. Los Event Dispatchers serán básicamente los delegates, estos Event Dispatchers solamente los podemos ejecutar o bindiar a ellos eventos de otros blueprints.

Para ver un ejemplo práctico, vamos a plantearnos algo simpático. Vamos a suponer que nuestro personaje puede aumentar su fuerza a un nivel extremo y en ese caso se vuelve negro, en el punto en el que nuestro personaje llega a este nivel, el RedCharacter que sería su enemigo, se hecha a correr :).

Agrega otra Action al Project Settings/Input y llámalo Activate Super Power (o como prefieras).

En el Event Graph del Character fíjate que en el panel My Blueprint, desde el que podemos agregar variables y funciones también tenemos la opción para crear un Event Dispatcher. Da clic aquí para crear uno y llámalo SuperPowerActivated.

ue4_tuto9_img04.0

Una vez creado, desde el Panel Detalles, en la sección Inputs, podemos agregar parámetros de entrada según el método que queramos ejecutar al llamar a este Event Dispatcher, en nuestro caso no necesitamos ningún parámetro.

Ahora, modifica el Event Graph de la siguiente forma:

ue4_tuto9_img04.1

Esta es la lógica que se ejecutará cuando aumentemos de poder. A modo de demostración simplemente cambiamos nuestro color a negro.

Es en este preciso momento es cuando queremos notificar que aumentamos nuestro poder, para esto arrastra desde el panel My Blueprint hasta el Event Graph, el Event Dispatcher que creamos anteriormente. Al soltarlo te mostrará un menú contextual con las distintas opciones relacionadas con ese Event Dispatchers, selecciona de ahí la opción Call.

Captura del EventGraph del MyCharacter después de agregar el llamado del Event Dispatcher una vez que el personaje incrementa su poder (cuando se detecta la entrada de nombre ActivateSuperPower)

Captura del EventGraph del MyCharacter después de agregar el llamado del Event Dispatcher una vez que el personaje incrementa su poder (cuando se detecta la entrada de nombre ActivateSuperPower)

 

Listo, este es el “sinónimo“ del Broadcast de C++. Con esto todos los métodos que se hayan bindiado a este Event Dispatchers se llamarán. Pero, como mismo vimos en C++, si no bindiamos ningún evento a este Event Dispatcher no pasará nada, así que vamos a ello.

Abre el RedCharacterBlueprint crea un Custom Event de nombre OnPlayerCharacterIncreasePower e implementa en él el siguiente algoritmo.

A partir del Actor Location obtenemos un vector 1000 unidades detrás del personaje y el aplicamos un AI Move To que hará que este personaje salga corriendo hacia ese punto.

A partir del Actor Location obtenemos un vector 1000 unidades detrás del personaje y el aplicamos un AI Move To que hará que este personaje salga corriendo hacia ese punto.

 

Listo ya tenemos el evento que queremos ejecutar cuando el PlayerCharacter aumente su poder, ahora solo nos queda bindiarlo al Event Dispatcher del PlayerCharacter.

Como mismo hicimos en C++, vamos a implementar en el Begin Play el binding. Necesitamos acceder al PlayerCharacter que es donde tenemos el Event Dispatcher, una vez que tengamos el PlayerCharacter casteado a MyCharacter podemos seleccionar Bind Event To . . . que nos permitirá bindiar un evento a cualquiera de los Event Dispatchers que existan en el PlayerCharacter. Fíjate que podemos Bindiar un evento al SuperPowerActivated que creamos, pero también podemos bindiar evento a muchos otros momentos importantes, como el OnDestroyed, el OnTakeAnyDamage y muchísimos otros eventos que nos brinda el Engine ya por defecto, así que ya sabes, si en algún momento necesitas lanzar un evento cuando un Actor es destruido, por ejemplo, puedes bindiar tu evento al OnDestroyed. Esto es algo que se usa mucho para incrementar puntos, o hacer respawn de otros personajes, o si es el OnDestroyed del PlayerCharacter, para dar el Game Over.

Retomando nuestro ejemplo, el EventGraph del RedCharacterBlueprint nos quedaría así:

Captura del EventGraph del RedCharacterBlueprint después de implementar el Event Begin Play, donde se obtiene la referencia al MyCharacter y se bindea el evento OnPlayerCharacterIncreasePower al Event Dispatchers que se llama cuando el PlayerCharacter aumenta de poder.

Captura del EventGraph del RedCharacterBlueprint después de implementar el Event Begin Play, donde se obtiene la referencia al MyCharacter y se bindea el evento OnPlayerCharacterIncreasePower al Event Dispatchers que se llama cuando el PlayerCharacter aumenta de poder.

 

Listo ¡!! . . . guarda los cambios y dale Play al juego. Toca la tecla para activar el super poder, veras como nuestro personaje se vuelve negro, y el RedCharacter sale corriendo como todo un cobarde :)

ue4_tuto9_img08

Conclusión

Este tutorial ha sido un poco más corto que los de costumbre pero espero que haya servido para darte un acercamiento a como trabajar con Delegates en Unreal Engine 4 y que puedas explotar a partir de ahora esta fenomenal vía que tenemos de notificar a los distintos Actors de nuestro juego cuando un evento determinado ocurre.

Para profundizar en los Delegates desde C++ puedes darte una vuelta por la documentación oficial

. . . Y esto es todo por hoy. Seguiremos viendo nuevas cosas de Unreal Engine 4 en próximos tutoriales así que no te vayas muy lejos. También puedes seguirme en Twitter (@nan2cc) y así te dejo saber cuando tengamos un nuevo tutorial. Mientras, me encantaría escuchar tus comentarios o temáticas que quisieras que tocara en próximos tutoriales. Hasta la próxima, bye 😉