Archivo de la etiqueta: C++

Desarrollando un Third Person Multiplayer Shooter en U4 – Parte 1

En este tutorial vamos a preparar la base de nuestro juego. Un personaje con su pistola en tercera persona. Podrá apuntar y disparar proyectiles en la dirección a la que apunta. Esto nos va a permitir, además de preparar la base de nuestro juego, ver varias cosas que nos ofrece el Engine y que no hemos visto en tutoriales anteriores, como los AimOffset, para implementar el movimiento de un personaje al apuntar. El ProjectileMovementComponent, que nos permite afectar en el Tick de un actor su posición a partir de una velocidad y gravedad, simulando el desplazamiento de un proyectil. Y justamente basados en este componente veremos como implementar un sistema de disparo con proyectiles. Así que sin más, manos a la obra !!

Requisitos previos: Para no extender innecesariamente cada tutorial, las cosas que ya hemos visto en anteriores entregas no las volvemos a abordar en detalles. Si recién comienzas tu aventura en el desarrollo de videojuegos con Unreal Engine 4, antes de continuar con este tutorial te recomiendo que le des un vistazo a los anteriores.

NOTA: Este tutorial ha sido desarrollado con Unreal Engine 4.8, 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.

Preparando los recursos para el proyecto.

Antes de comenzar a implementar cualquier cosa necesitamos los recursos de nuestro juego (modelos y animaciones) y si hay un lugar bueno para encontrar recursos para nuestras pruebas, estudios y tutoriales, ese es el Marketplace :).

Vamos a necesitar dos paquetes que hay en el Marketplace, que sí, son de pago :( pero con el precio que tienen para lo que nos brindan, son prácticamente un regalo. Me refiero al Pistol Anim Set Pro y el Military Weapon Silver

Captura de pantalla del Marketplace de los paquetes Pistol Anim Set Pro y Military Weapon Silver

Captura de pantalla del Marketplace de los paquetes Pistol Anim Set Pro y Military Weapon Silver

Con estos recursos a la mano ya podemos comenzar. Vamos iniciar nuestro proyecto a partir de la plantilla Third Person en C++. Los amantes a los blueprints se sentirán un poco mal, pero la realidad es que muchas de las funcionalidades orientadas al multiplayer en Unreal aún no están expuestas a Blueprints, por eso lo más probable es que si tu objetivo es desarrollar un juego multiplayer y quieras usar características avanzadas, vas a necesitar C++. Pero no te preocupes, que veremos también las opciones que tenemos desde blueprint, que la realidad es que para cosas simples sigue siendo una opción.

Configurando las animaciones básicas para el personaje.

Después de crear el proyecto a partir de la plantilla “Third Person en C++“ y agregar los recursos del Pistol Anim Set Pro y Military Weapon Silver Abre el Blueprint del Character y vamos a cambiarle el esqueleto para que use el Epic_Skeleton_Template_Skeleton que viene con el PistolAnimsetPro. En realidad es el mismo esqueleto que viene con Unreal pero todos los assets de animaciones del PistolAnimsetPro están hechos para esta copia del esqueleto. Haciendo esto nos evitamos el retarget de cada una de las animaciones que queramos usar. También con esto ya adelantamos la creación del socket en la mano del personaje para anclar el arma, este esqueleto que viene en el PistolAnimsetPro ya lo tiene creado.

Además de esto agrega los siguientes atributos a la clase del Character:

/** true cuando el personaje está apuntando su arma */
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category=Character)
bool bIsAiming;

/** Animación de disparo con la pistola */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Animation")
UAnimMontage *ShotPistolAnimation;

En el constructor inicializa bIsAiming en true; ya que temporalmente, desde el inicio del juego el personaje estará apuntando y bUseControllerRotationYaw también ponlo en true;

Ahora vuelve al Editor y crea un BlendSpace1D con las animaciones Pistol_Idle, Pistol_WalkFwdLoop y Pistol_RunFwdLoop (la versión InPlace). Después ve al AnimationBlueprint del personaje y modifica el nodo Idle/Run en el StateMachine para que use este BlendSpace que acabas de crear. Si tienes duda de como hacer esto dale un vistazo a los primeros tutoriales donde se explica el proceso detalladamente.

Captura del BlendSpace en modo de edición. Los assets Pistol_WalkFwdLoop y Pistol_RunFwdLoop puedes colocarlos en el valor que prefieras según el MaxWalkSpeed de tu personaje al caminar y correr.

Captura del BlendSpace en modo de edición. Los assets Pistol_WalkFwdLoop y Pistol_RunFwdLoop puedes colocarlos en el valor que prefieras según el MaxWalkSpeed de tu personaje al caminar y correr.

En este punto tenemos la animación de reposo y caminando mientras está apuntando, pero tenemos un pequeño detalle que usaremos como pretexto para ver un Asset de animación que nos brinda Unreal y que no hemos visto en tutoriales anteriores. Hablo del AnimOffset.

Introducción al AimOffset en Unreal Engine 4

Si te detienes un segundo por algún Third Person Shooter que tengas a mano, como el Max Payne 3 por ejemplo, y analizas su comportamiento, verás que el personaje tiene dos estados (centrándonos solo en lo que se refiere a apuntar/usar su arma). Puedes estar apuntando el arma, listo para disparar (generalmente con el clic secundario se pasa a este estado) y con su arma abajo, que la tiene equipada pero no está apuntando. En el primer estado verás que por lo general el personaje rota su Yaw en dirección del Controller pero si apuntas hacia arriba o hacia abajo lo que hace es rotar el torso. Pues bien, es precisamente este segundo movimiento lo que podemos implementar gracias a los AimOffset.

El AnimOffset es un asset de animación que almacena una serie de poses que son “blendeados“ a partir de los parámetros de entrada, que por lo general son la rotación en los ejes Yaw y Pitch. De esta forma hacemos que nuestro character pueda apuntar su arma sin tener que rotar todo el cuerpo, ya sea horizontalmente o verticalmente.

Imagen tomada de https://docs.unrealengine.com/latest/INT/Engine/Animation/AnimHowTo/AimOffset/index.html donde se ve al personaje apuntando su arma en distintas direcciones sin rotar todo el cuerpo, gracias al AnimOffset.

Imagen tomada de https://docs.unrealengine.com/latest/INT/Engine/Animation/AnimHowTo/AimOffset/index.html donde se ve al personaje apuntando su arma en distintas direcciones sin rotar todo el cuerpo, gracias al AnimOffset.

Para simplificar un poco la implementación de nuestro mecanismo de apuntar haremos que en la horizontal el personaje simplemente rote en la dirección del controller, por esto fue que pusimos en true la propiedad del Character Use Controller Rotation Yaw. Más adelante vamos a “afinar” un poco más esto, para evitar que cuando el character esté apuntando en la horizontal rote patinando en el suelo, pero de momento es suficiente. Para apuntar en la vertical si vamos a hacer uso del AimOffset.

Creando un AimOffset

En el Content Browser selecciona Add New/Animation y verás que tenemos dos opciones AimOffset y AimOffset 1D. Tal como tenemos con los BlendSpace, en el AimOffset pasa lo mismo. Podemos crearlo para que tenga una sola entrada, por ejemplo, la rotación solo en un eje (AimOffset 1D), o para que tenga dos entradas. Aunque de momento vamos apuntar en la horizontal con el controller, vamos a crear el AimOffset de dos entradas (Aim Offset) para ver el proceso más complejo y ya dejarlo listo para cuando vayamos, más adelante, a mejorar el apuntar en la horizontal.

Después de crear el Aim Offset, dale un nombre y ábrelo para editarlo. Como verás su modo de edición es prácticamente idéntico a los BlendSpace (de hecho, los AimOffset son una especie de BlendSpace). Como ya ha estas alturas debes entender bien como funcionan los BlendSpace simplemente te dejo como tienes que configurar el AimOffset con las animaciones que nos brinda el PistolAnimsetPro. La característica que tienen los AimOffset a diferencia de los BlendSpace es que los primeros trabajan con animaciones de un solo frame, o sea, con poses, y es el propio Engine el que se encarga de hacer el blend entre los distintos poses según los valores de entrada para que el movimiento se vea fluido.

Captura del AimOffset en modo de edición. Los parámetros de entrada son la rotación que tendrá el controller en los ejes Yaw y Pitch y el rango de valores entre -90 y 90 es para evitar la torsión total del cuerpo.

Captura del AimOffset en modo de edición. Los parámetros de entrada son la rotación que tendrá el controller en los ejes Yaw y Pitch y el rango de valores entre -90 y 90 es para evitar la torsión total del cuerpo.

Primero configura los parámetros de entrada y el rango de valores como se muestra en la imagen, después arrastra los siguientes assets de animación al gráfico del AnimOffset comenzando de arriba hacia abajo y yendo de izquierda a derecha (recuerda usar la versión InPlace)

Pistol_AimOffset_LU
Pistol_AimOffset_CU
Pistol_AimOffset_RU
Pistol_AimOffset_LC
Pistol_AimOffset_CC
Pistol_AimOffset_RC
Pistol_AimOffset_LD
Pistol_AimOffset_CD
Pistol_AimOffset_RD

Abre cada uno de estos assets para que veas que son una animación de un solo frame y que representan el pose máximo en cada una de las direcciones en las que podrá apuntar el character. Hecho esto puedes mover el cursor sobre el gráfico, simulando la entrada de los valores, para que veas en el Preview como se comporta la animación. Salva y cierra el editor del AimOffset.

Gracias al PistolAnimsetPro los Assets necesarios para crear el AnimOffset ya estaban listos, pero te recomiendo que profundices un poco más con la documentación oficial de Unreal en las secciones Aim Offset y Creating an Aim Offset porque los Animation Sequence que se necesitan para cada pose tienen varios detalles importantes de configuración a tener en cuenta para que después estos assets los puedas usar en el AimOffset. También puedes revisar los ejemplos del Content Examples.

Bien, con estos dos assets listos podemos pasar a implementar la lógica en el EventGrapth del AnimationBlueprint para poder darle valor a las variables que servirán como valores de entrada a estos dos assets. Abre el AnimBlueprint del Character y crea las siguientes variables:

Speed (float): Para el BlendSpace del Idle/Run, será la velocidad del personaje.

PlayerAimYaw (float) y PlayerAimPitch (float): Serán la dirección de apuntar en cada eje, estos son los parámetros de entrada para el AimOffset.

Delta (float): Variable temporal para almacenar el DeltaTime y poderlo usar cómodamente en el script.

IsAiming (bool). Variable para saber si el personaje está apuntando o no, la inicializamos con la variable de mismo nombre que agregamos en el Character.

Una vez creada cada una de estas variables, implementa el siguiente algoritmo:

EventGraph del AnimBlueprint del Character para obtener los valores necesarios para pasar como parámetro al BlendSpace del estado Idle/Run y el AimOffset

EventGraph del AnimBlueprint del Character para obtener los valores necesarios para pasar como parámetro al BlendSpace del estado Idle/Run y el AimOffset

En este algoritmo no hacemos nada complejo, la velocidad la obtenemos a partir del Length del vector velocity del Character, como hemos hecho muchas veces ya y los valores para el Aim los obtenemos calculando la diferencia entre la rotación del Controller (la cámara) y la rotación del Character (mediante el nodo Delta (rotator)), suavizamos un poco el valor resultante mediante el nodo RInterp To para evitar saltos bruscos, y por último, limitamos esos valores entre -90 y 90 ya que son los valores extremos de entrada para el AimOffset para evitar la torsión total del cuerpo.

Solo un detalle curioso que seguro notarás y es ese 2.5 que restamos al Pitch y el 10 que sumamos al Yaw de la rotación del Controller. Esto lo hago para “forzar“ un poco la pose inicial del Character. Por defecto la pose inicial es apuntando totalmente hacia delante, pero si te fijas en la mayoría de los shooters el personaje está un poco hacia la izquierda de la pantalla y su pistola queda apuntando hacia el centro, lo que implica que tenga que estar un poco rotada hacia la derecha.
Después que terminemos, prueba quitar esos dos valores para que notes la diferencia en el pose del personaje con respecto a la mira (el centro de la pantalla).

Ahora crea un Montage a partir de la animación Pistol_ShootOnce. Este Montage lo usaremos para reproducirlo en el momento del disparo. Settea desde el editor la propiedad ShotPistolAnimation que creamos en el Character con este Montage. Por último pasa al AnimGraph y modifícalo para que te quede de la siguiente forma.

Captura del AnimGraph del AnimBlueprint del personaje después de agregar el Slot para poder reproducir el Montage del disparo, el Layered blend per bone, para blendear ese montage y solo reproducir esa animación en la parte de arriba del esqueleto y por último, antes de terminar en el Final Animation Pose, tenemos el AimOffset que creamos para lograr el movimiento al apuntar.

Captura del AnimGraph del AnimBlueprint del personaje después de agregar el Slot para poder reproducir el Montage del disparo, el Layered blend per bone, para blendear ese montage y solo reproducir esa animación en la parte de arriba del esqueleto y por último, antes de terminar en el Final Animation Pose, tenemos el AimOffset que creamos para lograr el movimiento al apuntar.

Aquí lo único nuevo que tenemos es que antes de terminar en el Final Animation Pose tenemos el nodo que representa el AimOffset que creamos anteriormente. Esto es lo último necesario para tener nuestro mecanismo de apuntar listo.

Guarda todo, compila y dale Play al juego. El personaje ya inicia en su pose de apuntar, si mueves el mouse en la vertical para girar el personaje rota en esa dirección y si mueves el mouse hacia arriba y hacia abajo verás como entra en juego el AimOffset haciendo que solamente rote el torso para apuntar en esa dirección, tal como haríamos en la vida real. Puedes probar poner en false la propiedad bUseControllerRotationYaw para que veas como se comporta el AimOffset al apuntar en la horizontal.

Captura de pantalla del juego donde se ve el personaje en su pose de apuntar.

Captura de pantalla del juego donde se ve el personaje en su pose de apuntar.

Bien, ya terminamos con el movimiento básico para nuestro personaje. Hay un detalle que seguro notarás, el Third Person Sample no tiene ese punto de mira en el centro de la pantalla. En realidad lo pedimos prestado del First Person Sample :) es muy simple. Crea un blueprint que herede de HUD y agrega lo siguiente (copia la textura del First Person Sample). No olvides modificar el GameMode para usar este Blueprint como HUD.

Simple método del HUD para dibujar una mira en el centro de la pantalla (Tomado del First Person Template)

Simple método del HUD para dibujar una mira en el centro de la pantalla (Tomado del First Person Template)

Solo nos va quedando agregarle un arma a nuestro personaje, verdad ?. Pero antes de implementar el arma vamos a implementar el proyectil que usará esa arma.

Anteriormente implementamos un mecanismo para disparar un arma y detectar la colisión del disparo con el enemigo, pero en ese caso lo hicimos un poco ficticio. Lo que hicimos fue que en el momento del disparo se haga un Line Trace para simular la trayectoria de la bala, y el primer Actor con el que colisione ese Line Trace es el actor que recibe el disparo. Este modo de disparo generalmente se conoce como “Instant Hit“ porque inmediatamente que se hace clic, se hace el trace y se detecta el impacto. Esa solución es totalmente válida y para nuestro caso se ajustaba perfectamente, pero no siempre esa es la solución que estamos buscando para este tipo de situación. En muchos casos necesitamos una implementación que se ajuste más a la realidad. Cuando se dispare el arma salga de esta un proyectil con una velocidad determinada, que sea afectado por la gravedad, que tenga cuerpo etc. Pues para esto tenemos un fenomenal componente que nos brinda Unreal Engine 4: el ProyectileMovementComponent.

Introducción al uso del ProjectileMovementComponent

El ProjectileMovementComponent es un componente que nos permite actualizar la posición del Actor en cada Tick simulando la trayectoria de un proyectil a partir de su velocidad y gravedad. También permite activar si queremos que rebote al impactar. Es muy útil cuando queremos implementar por ejemplo: un lanza granadas, el disparo de una pelota, o el disparo de una bala. Aunque no debemos abusar de su uso, ten en cuenta que sería un actor más en el escenario calculando física, render etc. Por ejemplo, los disparos de una ametralladora no son aconsejados hacerlos por esta vía, mejor usar el Instant Hit, por la cantidad de proyectiles que se tendrían que crear en runtime y todo el calculo de la física. En realidad, una pistola como la que estamos implementando ahora, también por lo general su disparo se implementa como instant hit, pero vamos a hacer la excepción aquí para poder ver como funciona este genial componente. Después, un buen ejercicio es que intentes hacer por tu cuenta la implementación Instant Hit para esta arma, y si tienes alguna duda puedes dejarme un comentario y vemos como solucionarlo 😉

Bien, lo primero que vamos a crear es el Actor que usaremos como proyectil de nuestra arma. Crea una clase en C++ de nombre AProjectile que herede de Actor. Agrégale un componente de colisión, un StaticMesh y un ProjectileMovementComponent. A continuación te dejo como te debe quedar, ve detenidamente por los comentarios, sobre todo los relacionados al ProjectileMovementComponent para que veas como es su inicialización.

//-------------------------------------------
// Projectile.h
//-------------------------------------------

#pragma once

#include "GameFramework/Actor.h"
#include "Projectile.generated.h"

UCLASS()
class THIRDPERSONSHOOTER_API AProjectile : public AActor
{
	GENERATED_BODY()

    /** Componente de colisión */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
    class UBoxComponent* CollisionComponent;

    /** Mesh del proyectil */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
    class UStaticMeshComponent* MeshComponent;

    /** Componente para lograr el movimiento del proyectil */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
    class UProjectileMovementComponent* MovementComponent;

public:
	// Sets default values for this actor's properties
	AProjectile();

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

	// Called every frame
	virtual void Tick( float DeltaSeconds ) override;

    FORCEINLINE class UBoxComponent* GetCollisionComponent() const { return CollisionComponent; }

    FORCEINLINE class UStaticMeshComponent* GetMeshComponent() const { return MeshComponent; }

    FORCEINLINE class UProjectileMovementComponent* GetMovementComponent() const { return MovementComponent; }
};

//-------------------------------------------
// Projectile.cpp
//-------------------------------------------

#include "ThirdPersonShooter.h"
#include "Projectile.h"

AProjectile::AProjectile()
{
 	//Settea este actor para que se llame el método Tick() en cada frame
	PrimaryActorTick.bCanEverTick = true;

    //Crea el componente de colision como el RootComponent de este Actor
    CollisionComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("CollisionComponent"));
    RootComponent = CollisionComponent;

    //Crea el componente para el Mesh del proyectil y lo ancla al RootComponent
    MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
    MeshComponent->AttachTo(RootComponent);

    //Crea el UProjectileMovementComponent para el movimiento del proyectil
    MovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("MovementComponent"));

    //Fíjate que el MovementComponent no se ancla a ningun otro componente, en cambio lo que se hace es definir cual será el componente
    //que se moverá a partir de la configuración de este, en este caso es el rootcomponent el que queremos afectar.
    MovementComponent->UpdatedComponent = CollisionComponent;

    //Velocidad iniciar que tendrá el proyectil al ser agregado a la escena
    //En realidad un proyectil se movería mucho más rápido, pero vamos a quedarnos de momento con este valor para poder ver su recorrido
    MovementComponent->InitialSpeed = 2000.0f;

    //Máxima velocidad que podrá alcanzar
    MovementComponent->MaxSpeed = 2000.0f;

    //Con bRotationFollowsVelocity en true, hacemos que la rotación sea actualizada en cada tick para q empareje con vector de velocidad
    MovementComponent->bRotationFollowsVelocity = true;

    //El valor de gravedad que afectará al proyectil, en este caso no queremos que sea afectado por la gravedad.
    MovementComponent->ProjectileGravityScale = 0.f;
}

// Called when the game starts or when spawned
void AProjectile::BeginPlay()
{
	Super::BeginPlay();

}

// Called every frame
void AProjectile::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );

}

Compila y abre el editor, crea un nuevo Blueprint que herede de la clase AProjectile que acabamos de crear, nómbralo BP_Projectile. Ábrelo para editarlo y en el StaticMeshComponent settea un mesh que será el cuerpo del proyectil, como no tenemos ningún recurso en forma de bala, puedes usar una esfera escalada o cualquier otro mesh semejante. Ajusta las propiedades del Collision Component y la posición del Mesh dentro de este.

Selecciona el ProjectileMovementComponent y dale un vistazo a las propiedades que puedes modificar desde acá, verás que por defecto los valores de velocidad y gravedad ya están configurados con los números que le dimos en C++.

Pudiera darse el caso que quisiéramos que nuestro proyectil se comportara más como una pelota, por ejemplo, como sucede en el Template First Person. En este caso solo tienes que disminuir el valor de la propiedad InitialSpeed y MaxSpeed para que salga disparado a menos velocidad y aumentar el valor de la propiedad GravityScale, para que la gravedad lo haga caer al suelo mucho más rápido. Puedes crear un proyecto basado en la platilla del First Person y revisar los parámetros del blueprint del proyectil en ese proyecto para que veas su comportamiento en el juego.

Finalmente el BP_Proyectile te debe quedar así.

BP_Projectile. En este caso el Mesh del proyectil brilla tanto por el material que tiene aplicado, lo hice así para poderlo ver mejor al ser disparo. En tu caso, te repito, puedes usar cualquier Mesh incluso sin material.

BP_Projectile. En este caso el Mesh del proyectil brilla tanto por el material que tiene aplicado, lo hice así para poderlo ver mejor al ser disparo. En tu caso, te repito, puedes usar cualquier Mesh incluso sin material.

Pues bien, ya con el proyectil listo, solo nos queda el arma.

Implementando la pistola que usará nuestro personaje

Primero vamos a crear la clase base de todas las armas que tendremos en nuestro juego.

//-------------------------------------------
// Weapon.h
//-------------------------------------------

/** Clase base de todas las armas */
UCLASS()
class THIRDPERSONSHOOTER_API AWeapon : public AActor
{
	GENERATED_BODY()

private:

    /** Mesh del arma */
    UPROPERTY(VisibleDefaultsOnly, Category=Mesh)
    USkeletalMeshComponent* Mesh;

protected:

    /** Get del Mesh **/
    FORCEINLINE USkeletalMeshComponent* GetWeaponMesh() const { return Mesh; }

public:

	/** Constructor */
    AWeapon(const FObjectInitializer& ObjectInitializer);

	/** Es llamado por el engine cuando este actor es agregado al juego */
	virtual void BeginPlay() override;

	/** Se llama en cada frame */
	virtual void Tick( float DeltaSeconds ) override;

    /** Retorna el posición del socket MuzzleFlash. La punta del cannos del arma */
    FVector GetMuzzleLocation() const;

    /**
     * Método virtual puro, nunca tendrá implementación en esta clase, se tiene que implementar en las clases base
     * Tendrá la lógica de disparo de cada arma
     */
    virtual void FireWeapon() PURE_VIRTUAL(AShooterWeapon::FireWeapon,);

};

//-------------------------------------------
// Weapon.cpp
//-------------------------------------------

#include "ThirdPersonShooter.h"
#include "Weapon.h"

AWeapon::AWeapon(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

    //Inicialización del Mesh como RootComponent de este actor
    //Fíjate que le desactivamos la colisión a este mesh, de esta forma evitamos que un disparo pueda colisionar con el arma
    //a menos que quieras implementar algún mecanismo en el que si esto pasa, el personaje suelte el arma 😉
    Mesh = ObjectInitializer.CreateDefaultSubobject<USkeletalMeshComponent>(this, TEXT("WeaponMesh"));
    Mesh->SetCollisionObjectType(ECC_WorldDynamic);
    Mesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
    Mesh->SetCollisionResponseToAllChannels(ECR_Ignore);
    RootComponent = Mesh;
}

FVector AWeapon::GetMuzzleLocation() const
{
    USkeletalMeshComponent* UseMesh = GetWeaponMesh();
    return UseMesh->GetSocketLocation(FName(TEXT("MuzzleFlash")));
}

// Called when the game starts or when spawned
void AWeapon::BeginPlay()
{
	Super::BeginPlay();
}

// Called every frame
void AWeapon::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );
}

Como ves, es una clase muy simple que hereda de Actor y de extra solo le agregamos el método GetMuzzleLocation que nos devuelve la posición del socket en la punta del cañón del arma.

Ahora crea otra clase que herede de esta clase AWeapon y vamos a llamarla APistol. Esta clase APistol será la clase para la pistola que usará nuestro personaje. Tendrá un atributo que será una estructura que crearemos para almacenar en ella la información del tipo de proyectil que usará. De esta forma nos quedará muy fácil si en el futuro queremos cambiar el tipo de proyectil que usa el arma. Además de esto, tendrá la implementación del método FireWeapon con toda la lógica necesaria para calcular la dirección en la que se tiene que hacer el Spawn del proyectil para que salga disparado en la dirección exacta a la que estamos apuntando.

//-------------------------------------------
// Pistol.h
//-------------------------------------------

#pragma once

#include "Weapon.h"
#include "Projectile.h"
#include "Pistol.generated.h"

/**
 * Estructura para encapsular la información del projectile que usa cada arma
 * Cada arma tendrá una instancia de esta estructura, que se podrá editar desde el Editor para facilmente configurar la información de su proyectil
 */
USTRUCT()
struct FProjectileData
{
    GENERATED_USTRUCT_BODY()

    /** Clase del proyectil. Necesario para el momento de hacer el Spawn */
    UPROPERTY(EditDefaultsOnly, Category="Defaults")
    TSubclassOf<class AProjectile> ProjectileClass;

    /** Damage que causará este proyectil al impactar */
    UPROPERTY(EditDefaultsOnly, Category="Defaults")
    float Damage;

    /** Constructor por default de la estructura */
    FProjectileData()
    {
        ProjectileClass = NULL;
        Damage = 100;
    }
};

UCLASS()
class THIRDPERSONSHOOTER_API APistol : public AWeapon
{
	GENERATED_BODY()

protected:

    /** Info del proyectil que usará esta arma */
    UPROPERTY(EditDefaultsOnly, Category=Config)
    FProjectileData ProjectileConfig;

public:

    /**
     * Dispara el arma
     * Calcula la posición y rotación en la que debe de hacerse el Spawn del proyectil para que salga en la dirección a la que estamos apuntando
     */
	virtual void FireWeapon() override;
};

//-------------------------------------------
// Pistol.cpp
//-------------------------------------------

#include "ThirdPersonShooter.h"
#include "Pistol.h"

void APistol::FireWeapon()
{
    //TODO: Implementar el disparo
}

Con estas clases creadas, compila, abre el editor y crea un blueprint a partir de esta clase, yo le llamé BP_Pistol. Como SkeletalMesh selecciona la pistola que viene en el Military Weapon Silver y fíjate que gracias al atributo Projectile Config que creamos en C++ como EditDefaultsOnly. Desde acá podemos definir el proyectil que usará esta pistola, junto con el Damage que causará.

BP_Pistol configurado con el Mesh de la pistola que viene en el Military Weapon Silver Pack y para usar como proyectil el BP_Projectile que creamos previamente

BP_Pistol configurado con el Mesh de la pistola que viene en el Military Weapon Silver Pack y para usar como proyectil el BP_Projectile que creamos previamente

Ahora, para comprobar que todo está bien, vamos desde el BeginPlay ha equiparle el arma a nuestro personaje. Abre el blueprint del character, en el BeginPlay usa el nodo Spawn Actor From Class (que nos permite agregar un actor a la escena en tiempo de ejecución) para hacer un Spawn de la pistola y anclarla a la mano del personaje. Fíjate que en este caso es importante settear como el Instigator del arma al Character (self) . . . Ups y casi se me olvida :), debemos agregar en la clase del Character un atributo de tipo AWeapon para tener en todo momento una referencia al arma que está usando. Abre la .h del Character y agrégale el siguiente atributo (Recuerda incluir la .h de AWeapon):

/** Arma actualmente equipada */
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Weapon")
class AWeapon* CurrentWeapon;

Listo !! ahora si podemos equiparle el arma a nuestro Character en el Begin Play

BeginPlay del Character, donde se hace un Spawn del arma, se inicializa el CurrentWeapon del Character y se ancla la pistola a la mano.

BeginPlay del Character, donde se hace un Spawn del arma, se inicializa el CurrentWeapon del Character y se ancla la pistola a la mano.

Muy bien, con esto ya tenemos nuestro personaje caminando por el escenario con la pistola en la mano, solo nos queda implementar el mecanismo para disparar el arma. Ve al Pistol.cpp y sustituye el método FireWeapon que dejamos pendiente por implementar por el siguiente:

void APistol::FireWeapon()
{
    //El Origen del disparo es la punta del cannon de la pistola (la posición del socket "MuzzleFlash")
    FVector Origin = GetMuzzleLocation();

    //Ahora vamos a determinar la rotación inicial que le daremos al proyectil para que salga disparado hacia donde estamos apuntando.
    //Para determinar esta rotación, primero tenemos que hacer un Trace hacia donde está "mirando" la camara
    //para obtener la posición exacta del objeto al que estamos apuntando (el objeto con el que colisione ese Trace)
    //y con esta posición, podemos calcular la rotación que le tenemos que dar al proyectil para que vaya en esa dirección.

    //Primero vamos a obtener el punto de inicio del Trace

    FVector ShootDir = FVector::ZeroVector;
    FVector CamLocModified = FVector::ZeroVector;

    //El Instigator de un actor es el responsable del Damage causado por este actor. Por lo general se settea al hacer el Spawn del actor
    //A partir del Instigator de la pistola, que será nuestro Character, obtenemos el Controller para poder obtener el punto de mira de la camara
    APlayerController* const PlayerController = Instigator ? Cast<APlayerController>(Instigator->Controller) : NULL;

    if (PlayerController)
    {
        FVector CamLoc;
        FRotator CamRot;

        //GetPlayerViewPoint del PlayerController retorna en CamLoc la posición de la cámara y en CamRot la rotación
        PlayerController->GetPlayerViewPoint(CamLoc, CamRot);
        ShootDir = CamRot.Vector();

        //Ahora vamos a desplazar el vector CamLoc para que quede a partir del character y en la dirección a la que estamos mirando
        //para poder hacer un Trace en dirección a donde estamos mirando sin que nuestro Character bloquee ese Trace por estar en el medio

        FVector Diff = ((Instigator->GetActorLocation()) - CamLoc);

        float DotProductResult = FVector::DotProduct(Diff, ShootDir);

        CamLocModified = CamLoc + (ShootDir * DotProductResult);
    }

    //Ahora tenemos que hacer un LineTrace desde la posición que acabamos de calcular, en dirección a donde estamos apuntando,
    //para predecir el punto de impacto que tendrá el proyectil y poder calcular la rotación que le tenemos que dar para que vaya en esa dirección.

    const float WeaponRange = 10000.0f; // Un valor grande que básicamente sería el alcance del proyectil

    const FVector EndTrace = CamLocModified + ShootDir * WeaponRange;

    FCollisionQueryParams TraceParams;
    FHitResult Impact(ForceInit);
    GetWorld()->LineTraceSingleByChannel(Impact, CamLocModified, EndTrace, ECC_GameTraceChannel1, TraceParams);

    //Finalmente el ajuste de rotación del proyectil para que al hacerle el spawn vaya en esa dirección
    //El vector resultante de la resta entre el punto de impacto y el origen, normalizado, nos da ese vector de dirección que buscamos
    if (Impact.bBlockingHit)
    {
        ShootDir = (Impact.ImpactPoint - Origin).GetSafeNormal();
    }

    //Listo !! ... ya tenemos los dos vectores que necesitamos, origen y rotación, ahora solo queda hacer el Spawn del proyectil

    //Creamos el FTransform para el Spawn del proyectil
    FTransform SpawnTransform(ShootDir.Rotation(), Origin);

    //Iniciamos el Spawn. Fíjate que la Class que le pasamos es lo que setteamos en ProjectileConfig.ProjectileClass
    //Esta clase la vamos a cargar desde el Editor en las propiedades del Blueprint de la pistola y será el BP_Projectile que creamos a partir de la clase AProjectile
    AProjectile* Projectile = Cast<AProjectile>(UGameplayStatics::BeginSpawningActorFromClass(this, ProjectileConfig.ProjectileClass, SpawnTransform));
    if (Projectile)
    {
        UGameplayStatics::FinishSpawningActor(Projectile, SpawnTransform);
    }
}

Es muy importante que vayas detenidamente por los comentarios del método porque para poder garantizar que al hacer el spawn del proyectil, este vaya exactamente en dirección a donde estamos apuntando, necesitamos de un poco de matemáticas y Traces :).

Lo que hacemos en el método es determinar la rotación inicial que le daremos al proyectil para que salga disparado hacia donde estamos apuntando. Para determinar esta rotación, primero tenemos que hacer un Trace hacia donde está “mirando” la cámara para obtener la posición exacta del objeto al que estamos apuntando (el objeto con el que colisione ese Trace) y con esta posición, podemos calcular la rotación que le tenemos que dar al proyectil para que vaya en esa dirección. Una vez que tenemos la posición y la rotación del proyectil solo tenemos que hacerle un Spawn en el nivel y del resto se encarga el ProjectileMovementComponent 😉

La línea Roja es una línea recta desde la punta de la pistola, como vez esa no puede ser la dirección que siga el proyectil. La línea azul es el Trace que hacemos para determinar la posición del objeto al que estamos mirando y por último, la línea verde representa la trayectoria que seguirá el proyectil después de ajustar su rotación inicial.

La línea Roja es una línea recta desde la punta de la pistola, como vez esa no puede ser la dirección que siga el proyectil. La línea azul es el Trace que hacemos para determinar la posición del objeto al que estamos mirando y por último, la línea verde representa la trayectoria que seguirá el proyectil después de ajustar su rotación inicial.

Por último, abre la clase del Character y agrega el método que disparará el arma que tengamos equipada. Recuerda llamar este método desde algún InputAction.

void AUnrealMannequin::FireWeapon()
{
	if(CurrentWeapon)
    {
        //Play a la animación de disparo
        PlayAnimMontage(ShotPistolAnimation);

        //Fire del arma
        CurrentWeapon->FireWeapon();
    }
}

Listo !! Compila y dale Play al juego. Dispara el arma y verás que en el momento en el que tocamos la tecla de Fire, además de la animación del personaje, sale disparado el proyectil desde la punta del cañón y exactamente hacia la dirección en la que estamos apuntando, justo como si se tratara de un proyectil real 😉

Captura del juego en ejecución donde se ve después de hacer el disparo, el proyectil “spawneado “ en la escena desde la punta del cañón y moviéndose en dirección a donde estamos apuntando.

Captura del juego en ejecución donde se ve después de hacer el disparo, el proyectil “spawneado “ en la escena desde la punta del cañón y moviéndose en dirección a donde estamos apuntando.

Terminado por hoy . . .

Vamos a dejar este tutorial hasta aquí. Verdad que aún no hemos visto nada de multiplayer, que es el plato fuerte de la serie, pero antes necesitábamos de todo este mecanismo inicial. En el próximo tutorial comenzaremos ya con el multiplayer. Este mismo sistema de disparar el arma vamos a hacerlo funcionar en multiplayer, así que no te vayas muy lejos . . . mientras, como siempre, me encantaría escuchar tus comentarios 😉

Making a 3D side-scroller game in UE4

Introduction

Let’s start summing up what we did in the previous tutorial. We started our series with a general introduction to Unreal Engine 4 through a project which explores some of the basic concepts of the engine. We saw the framework’s class hierarchy, the visual scripting, the character animation settings, and we defined a fixed camera as a temporary solution in our game, among other things. If you haven’t already read the previous tutorial, I strongly suggest you do before continuing with this one.

In this tutorial, we will create the base to our 3D side-scroller game’s style. Currently my team and I are working on an automatic Runner, 2D side-scroller for IOS and Android. You can follow us on our Facebook page to keep up with our work in progress and to know when it will release. If you like this kind of game, I can assure you that you will love ours ;).

As I already said, in this tutorial we will configure our game camera to achieve a side-scroller view. We will also add some coins to our scene and use simple collision mechanism that will allow the character to collect the coins. We will “teach” our character to run and jump :). We will also see some variable and function macros for the integration between the C++ code and the Editor… and so much more. Are you ready?!… Well let’s get started!!

Setting the level from the editor

Let’s start by making a small change to the current level through the editor to match the style of game that we want. The base to a side-scroller game is to have the camera parallel to the main character and to have the camera a certain distance away from the character on the y axis. The character moves only in two directions – upward and downward when he jumps, and left/right when he walks.

Open in the Editor the UE4Demo project that we used in the last tutorial and delete the visible objects that we won’t use (the chairs, the table, the statue, etc.), leaving only the floor object. After that, modify this object using the transformation and escalate tools situated in the upper-right corner of the viewport. Then create copies of the object, modify them indistinctly and spread them through the level. Make sure to leave the ‘Actor Play Start’ on one of the platforms, to avoid that the character free fall into abyss. This is how I did it (use your imagination to achieve a better result than mine :).

Modified level in the Editor to match our game style

Modified level in the Editor to match our game style

 

As you will notice, when the game runs, it shows a red alert which says: LIGHTING NEEDS TO BE REBUILT. This alert has to do with the fact that we change the level geometry and the engine needs to rebuild the light so the illumination and shadows fit the new geometry. You can rebuild the lights selecting the Toolbar/Build/Build Lighting Only option. Run again and you will notice that everything goes back to normal.

For now we have a very basic level but enough to implement and test all the things that we have planned to do.

Configuring the camera for a side-scroller game.

At this point, it’s important to clarify that UE4 has a default template to build this style of game. In fact, we will use practically the same elements that this template uses but the idea of this tutorial is to do it from scratch to understand the basic concepts of the game style.

First we will change the game camera. In the last tutorial, we configured a simple fixed camera that served the purpose of introducing us both into the visual scripting (Blueprint Editor) and the C++ code. We will no longer use this camera so go ahead and delete the current implementation of the camera both in code and in the Blueprint Editor. If you still have the code implementation, comment out the lines inside the Begin Play method. In the Blueprint you may delete all the nodes or, if you want to keep the references, delete the connection leaving the BeginPlay node. By deleting this connection, we break the camera algorithm because the execution doesn’t continue when the BeginPlay event is executed but we still hold the rest of the connections and references.

Let’s configure a new camera’s style, this time through code in our Character’s class. Always take into account that you can also do it through the Editor. Remember that we have a Blueprint class that inherits our HeroCharacter class. A good way to practice will be for you to adventure by yourself once we do it by code to do the same in the Editor by modifying the HeroCharacterBlueprint.

Open the HeroCharacter class and add the next declarations after the GENERATED_UCLASS_BODY() macro:


/** Spring arm to fix the camera to the Character to match the side-scroller style */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
TSubobjectPtr<USpringArmComponent> SpringArm;
    
/** Game camera, is attached to the arm’s socket to achieve the side-scroller’s camera style */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
TSubobjectPtr<UCameraComponent> SideViewCamera;

We add two class variables to our HeroCharacter, but I’m sure that the thing that caught your attention was the UPROPERTY macro included for each variable. This way we define whether or not we want to use these attributes in the Editor and what options we have to work with them. As we saw in the previous tutorial, one of the coolest things about UE4 is the seamless integration between the code and the editor and this macro is one of the powerful utilities to achieve it. Next, let’s look at some of this macro’s parameters:

VisibleAnywhere: Defines if this property will be visible in the Editor’s property panel.

BlueprintReadOnly: Defines if this property could be read from VisualScript in the Blueprint, but it can’t be modified.

Category: Allow to specify a category name under the properties will be listed in the Editor.

You can find a detailed reference of all of the configurable parameters to the UPROPERTY macro in the Runtime/CoreUObject/Public/UObject/ObjectBase.h inside the UP namespace. If you click over a category while pressing the cmd key in MACOS, or click one while holding ctrl in Windows, you will be taken to the specific reference for that macro.
After we implemented the constructor where we initialized these variables, go to the Editor to check the result of defining the attributes with this macro.

These two class attributes just created in HeroCharacter are of a data type we haven’t used before. The first one is USpringArmComponent that we named SpringArm. Notice that we used the parametrized TSubobjectPtr class to define it. This way we can use the CreateDefaultSubobject method of the PCIP object received in the constructor to create an instance of any data type, which we will in a second. The USpringArmComponent allows us to fix a component to its parent in a fixed distance. We will use this component to fix the game’s camera with a certain distance from the character.

We will also create the class attribute SideViewCamera of UCameraComponent data type. The class name is quite descriptive. This attribute is our game camera :).

Next we will initialize this variable in the class constructor. Open the HeroCharacter.cpp and add the following code block:


//Initializing the USpringArmComponent attribute
SpringArm = PCIP.CreateDefaultSubobject<USpringArmComponent>(this, TEXT("CameraBoom"));
   
//Adding the springArm to the Character's RootComponent (the collision capsule)
SpringArm->AttachTo(RootComponent);
   
//bAbsoluteRotation allows us to define if this support will rotate with the player or will stay fixed.
//In our case we don't want it to rotate with the character.
SpringArm->bAbsoluteRotation = true;
   
//The distance between the arm and its target. This value defines the distance between the character and the camera.
//Try out different values to see the outcome.
SpringArm->TargetArmLength = 500.f;
   
//Socket Offset.
//Socket is an anchor point to other components.
//For instance, in the character case we can define the socket in the character's hand
//this way we can add another component(for example a gun).
//But in our case we will add a camera to our SpringArm
SpringArm->SocketOffset = FVector(0.f,0.f,75.f);
   
//The relative rotation of the arm regard to its parent.
//We want the camera rotated 180 degrees in the Y axis in order to be situated parallel to the character, at the same level.
//This way we achieve the classic side-scroller camera's style.
SpringArm->RelativeRotation = FRotator(0.f, 180.f, 0.f);
   
// Creating the UCameraComponent instance.
SideViewCamera = PCIP.CreateDefaultSubobject<UCameraComponent>(this, TEXT("SideViewCamera"));
   
//The AttachTo method allow us to add an object to another in a given socket. It receives two parameters,
//the first one, the object where we will be anchored (the springArm) and the second the socket's name where we will be anchored.
//USpringArmComponent's SocketName returns the name of the components socket.
SideViewCamera->AttachTo(SpringArm, USpringArmComponent::SocketName);

Pay attention to each line comments so you can understand what each stands for. Overall, we create and configure the USpringArmComponent’s object and then we add it to the Character. Next, we create and configure the UCameraComponent’s object (the game camera) and add it to the USpringArmComponent to fix it to a given distance from the Character. Notice that we create the instances with the received reference in the constructor class using PCIP.CreateDefaultSubobject.

Ready, build and play the game. Now you have the game view in the side-scroller style :). Try to move in the level just created to see how the camera follows your every moment, always at a fixed distance.

New game view configuration to match the side-scroller camera's style

New game view configuration to match the side-scroller camera’s style

 

Don’t close the editor. Let’s focus in HeroCharacterBlueprint. In the last tutorial, we created this blueprint in the Character folder. Open it and activate the Components mode (upper-right corner). Notice that now in the Character’s Components panel we have a new object: the SpringArm and it contains a child the SideViewCamera. Check out all the properties defined by code. You can also check all the default properties values, change the values, and check out the different outcomes.

To test the parameters effect of the UPROPERTY macro in practice, go back to the code and delete the VisibleAnywhere parameter set in the property and build again. When you open the HeroCharacterBlueprint in the editor, in spite of seeing the components, we can’t see the properties values in the details panel when we select them.

Modifying the game controls for a side-scroller’s game.

So far we have set the camera’s style but I’m sure you noticed that the game controls don’t fit our game’s style, given that the character can move both in X and Y axis which is very unusual in a side-scroller game. Therefore we’ll change the game controls a bit to adjust them to the side-scroller style. We will also add two new actions and controllers to our character: run and jump.

I assume that after the last tutorial you have a couple of ideas of how to add these actions. In the Editor, select Edit/Project Settings/Input and leave only the MoveRight entry. Now let’s add another input type the ActionBinding. These entries, unlike the AxisBinding entries, are used to execute specific actions e.g. to jump or open a door. Create a new entry of this data type and name it Jump. Select the space bar as the key control. If you look closely you will notice that we can also define that the action gets executed when two keys are pressed simultaneously. In this case, will only use the space bar to jump.

New configuration of the game's controls. Adding jump control.

New configuration of the game’s controls. Adding jump control.

 

Now let’s code. First of all, we no longer need the MoveForward method of the HeroCharacter class. Delete the method statement in the .h and the implementation in the .cpp. Also, in the SetupPlayerInputComponent method, delete the MoveForward BindAxis. Lastly, we have to modify the MoveRight implementation given that at the moment when the entry gets called we rotate the character position, now we want that the character to move forward and backward respectively. Modify the MoveForward as the code below:


/**
*  Gets called when the MoveForward entry is detected (When the user press the A or D keys).
*  @param Value Value is equal to 1 when the D is pressed and -1 when A is.
*/
void AHeroCharacter::MoveRight(float Value)
{
if ( (Controller != NULL) && (Value != 0.0f) )
{
        // Adds a new movement to the right or left according to the Value value.
        AddMovementInput(FVector(0.f,-1.f,0.f), Value);
}
}

Now this method is much simpler and I hope it’s easier to understand what it does. Anyway, I will explain step by step what we just did. We apply a movement vector that affects only the y axis of the vector’s value. So when the user presses the D key, the character will move forward (to the right) and when the A key is pressed, the character will move backward, meaning to the left side.

Now we have to implement the method that will get called to execute the jump action when the user presses the space bar. It’s very simple. Just add the next line to the SetupPlayerInputComponent method:

InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);

Notice one thing, the method that gets called is the ACharacter::Jump. Jump is a method implemented in our base class, not in our own class HeroCharacter. This method already has all that we need to make the character jump. It’s really amazing how much help the framework provides us with :).

Go ahead build and play the game. Press the A and D keys to see how the character moves correctly on the scene, and how when you press the W and S keys, nothing happens. Hit the space bar and you will see how our character now also jumps :) … but something is still missing. Yes the character is jumping but visually is playing the walk animation. Next thing we need to do is add the jump animation.

Setting the jump animations for the character

Since the last tutorial, we have the main character resources that supposedly our design team have us, among them we have three FBX animations to jump: the Jump_End.FBX, Jump_Loop.FBX, Jump_Start.FBX and the Run.FBX file. Import them in the Animation folder. You can inspect them in the Persona Editor.

I’m sure that the fact that we have three different animations for the jump caught your attention, and you ask yourself, “Why??”. I will give you the answer right away but first let me share a personal experience with you involving this aspect: When we start the development of our current project, you can take a look in here :)). We struggle with this problem.
In our game, the character jumps at different heights. For instance, it can do a small jump to avoid a box or a long jump from a cliff and stays longer in the air. The problem is that in the long jump case we play the jump animation when the action is started with a duration and might end while the character is still in the air falling in the last frame of the jump animation until it touches the ground where the transition will continue normally. That’s the reason why in many cases, it’s necessary to divide the jump animation in three pieces, we have the start jumping action then we have a loop animation that is played while the character is falling and finally the animation when the character touches the ground. This is how we achieve the whole jump animation cycle independently of the height.

Let’s configure it in the character animation blueprint. As we saw in the last tutorial, we use a state machine for the animation system that allows us to separate the different states that our character supports and set an animation for each state. Currently the state machine of our character is very simple, containing only the Idle state and the Walking state. Let’s add the jumping states.

Inside the HeroAnimBlueprint/AnimGraph, enter the state machine, drag the cursor from the Idle/Walk node to create a new node, and name it JumpStart. Repeat the procedure twice using the last created node to create the JumpLoop node and finally the JumpEnd. At the end, connect the JumpEnd state to Idle/Walk to close the cycle. It should look like this:

New Character state machine, with the jumping states added

New Character state machine, with the jumping states added

 

We have just defined three new states to our character and the order in which they would be reached. In other words, the character could be in rest or running and can change to the JumpStart state (jump starts). From the JumpStart state, he can reach the JumpLoop (loop cycle) and then the JumpEnd state. Lastly, he could go to the Idle/Walk state. Notice the directional arrows between the states. These are the connections that define the origin and destination states. If you jump you will notice the four states and the blending between them.

Each state has an icon over the transition arrow. This icon represents the condition that triggers the transition between the states. We have to define this condition using the visual scripting in the Blueprint Editor. The first case is when the character is in the Idle/Walk state and starts the jump action. The condition to pass to this state will be BOOL variable that will store if the character is in the air or not. When the character changes the state such that it is in the air, we will change to the JumpStart state.

Double click in the transition icon to enter in its edition mode. By default we have a Result node with the “Can Enter Transition” description. This node expects a bool parameter that the state machine will use to decide if the state transition takes place. Create and add a new variable like we did in the last tutorial but this time use bool as data type. Name it IsInAir or any name that you prefer but it must be something meaningful. Add it to the blueprint in GET mode and finally connect this node output port to the input port of the Result node. So if the IsInAir variable has a true value, the transition will execute and the character will pass to the JumpStart state.

VisualScript of the transition between the Idle/Walk and JumpStart

VisualScript of the transition between the Idle/Walk and JumpStart

 

Save and exit the edition mode of the transition between the Idle/Walk and JumpStart and enter the edition mode of the JumpStart node to define which animation will be played in this case. Drag the JumpStart animation from the resources folder into the blueprint and connect it to the final node. The JumpStart and JumpEnd animations will be played only once which is different to what we did in the last tutorial where the Idle and walk animations are played in a loop. The idea is to start playing the JumpStart then pass to the JumpLoop that as its name suggest plays in a loop and finally play the JumpEnd animation only once. So select the JumpStart animation and in the property panel, uncheck the Loop option. Do the same for the JumpEnd animation.

VisualScript of JumpStart node

VisualScript of JumpStart node

 

Now we have to define the condition to pass from the JumpStart to the JumpLoop animation. For this task we will use a new node. We need to be able to detect when the JumpStart animation is about to end to know when to start playing the loop animation. Double click in the transition icon between JumpStart and JumpLoop. Once again we meet the Result node that we’re about to use but first we need to do an algorithm to define when the animation is near to end.

Let’s add a new node of the Time Remaining (Ratio) type to the Jump_Start Asset. This node informs us of how much time remains before the animation ends. Let’s add another node < float type, we will use it to know if an A parameter is smaller than a B one. Connect the output port of the TimeRemaining node to the upper input port of the comparison. For the second input port, we will use a manual value, in this case 0.1. Now,when the TimeRemaining is less than 0.1 (the animation is about to end), we transition to the JumpLoop state. In order to complete the transition setup, we still have to connect the comparison node output to the Result node. It should look like this: [caption id="" align="alignnone" width="630"]VisualScript of the transition between JumpStart and JumpLoop animationsVisualScript of the transition between JumpStart and JumpLoop animations[/caption]

 

Summing up, we periodically check if the StartJump animation is about to end by checking if the remaining time is less than 0.1. When this condition is fulfilled, the transition is executed.

Save and exit the transition’s edition mode and enter the state JumpLoop edition mode. Add the JumpLoop animation and connect it to the Result. Unlike with the JumpStart animation, with the JumpLoop animation, we need to play it in a loop because this is the one that plays while the character is falling, so check that the loop option is checked.

JumpLoop's node VisualScript

JumpLoop’s node VisualScript.

 

Let’s configure the last transition between the JumpLoop and JumpEnd nodes. JumpEnd, as its name suggests, is the animation played when the jump ends. The condition to transition to this state is that the character is close to the floor (it’s no longer in the air). That’s exactly what the isInAir variable represents. Double click on the transition icon between JumpLoop and JumpEnd and add the isInAir variable as GET. The problem is that we need to know when the character isn’t in the air so we need to use the isInAir negation, so we will add a NOT node type to the blueprint. This node type returns the input’s negation. Connect the NOT node to the isInAir node and this to the Result. Now, as soon as the character hits the floor, the character will transition to the JumpEnd state.

VisualScript of transition between JumpLoop and JumpEnd

VisualScript of transition between JumpLoop and JumpEnd.

 

Exit the transition’s edition mode and enter in the JumpEnd edition mode. Drag and connect to the Final Pose the JumpEnd animation and uncheck the loop option.

JumpEnd's node VisualScript

JumpEnd’s node VisualScript

 

Finally, we have to define the condition for when to transition back to the Idle/Walk state coming from the JumpEnd state. This animation represents the character already on the floor but still recovering from the fall. We need to wait until the JumpEnd animation is about to end to change to the initial state (Idle/Walk). Basically, we will repeat the steps we use in the transition between the JumpStart and JumpLoop. I hope you are able to do it by yourself… 😉 it should look like this:

VisualScript of transition between JumpEnd and Idle/Walk

VisualScript of transition between JumpEnd and Idle/Walk

 

Now we have completed the character state machine, but a small detail is still missing. We need to set the IsInAir variable value like we did earlier with the Speed variable.

Close the AnimGraph Editor and open the EventGraph. Add a GetMovementComponent node and connect the TryGetPawnOwner node that we created in our last tutorial to the input port of the GetMovementComponent node. Add another node and name it IsFalling and connect its output port of GetMovementComponent to the new node’s input port. Add the IsInAir variable in SET mode and connect the IsFalling output to the IsInAir input. Finally, connect the blank output port of the SET Speed to the SET IsInAir input for the algorithm continuity.

HeroAnimBlueprint's EventGraph modify it to set the IsInAir variable value when the character is in the air

HeroAnimBlueprint’s EventGraph modified to set the IsInAir variable value when the character is in the air

 

If you have read the previous tutorial, I’m sure you won’t have any problem understanding what we just did. We got the character’s MovementComponent, which contains an IsFalling method that returns a bool value denoting if the character is in the air or not. This IsFalling method is the one we will be using to set our IsInAir variable. Now we have the state machine for our character. Build the AnimationBlueprint and run the game. Press the space bar … now our hero jumps too :).

Character jumping with proper animation

Character jumping with proper animation

 

Implementing character’s running mechanism!!

Now our character knows how to walk, rest and jump, but a classic feature in side-scroller games is the ability to run to get more momentum and jump higher and further. Who doesn’t remember in one of Super Mario latest level the huge hole that can only be jumped with a great momentum, do you :)… So the next thing we will do is to add this skill to our character. Let’s implement it so that when the character is walking and the Shift key is pressed, the character will run.

First of all, open the editor and in the controls sections, add a new ActionBinding entry, name it Run and select the LeftShift key. Close the editor and open the HeroCharacter.cpp inside the SetupPlayerInputComponent method and add the following two lines:


//The Run entry is detected, when the Shift key is pressed we set that the ToggleRunState method should be call.
InputComponent->BindAction("Run", IE_Pressed, this, &AHeroCharacter::ToggleRunState);
   
//The Run entry is detected, when the Shift key is released we set that the ToggleRunState method should be call.
InputComponent->BindAction("Run", IE_Released, this, &AHeroCharacter::ToggleRunState);

Notice a tiny detail in this code. We are setting the BindAction for the Run entry twice and passing the same ToggleRunState method (that we will soon implement). The difference between the calls is that the second parameter specifies when the method gets called. The first case, IE_Pressed is fulfilled when the Shift key is pressed and IE_Release when it’s released. What we want to achieve is that when the key is pressed, the character runs and when the key is released, the character stops running and continues walking. Very similar to the Super Mario logic for the big jump!! :).

Now let’s implement the ToggleRunState method. Add the method’s declaration to the .h file:


/**
* Gets call when the engine detects the Run entry
* Change to the character run state.
*/
void ToggleRunState();

Go to the .cpp and add the following lines to the implementation:


/**
* Gets call when the engine detects the Run entry
* Change to the character run state.
*/
void AHeroCharacter::ToggleRunState()
{
//If the CharacterMovement's MaxWalkSpeed attribute is 400.f we increase it to 900.f to achieve that the character will  move faster.
//Otherwise we set it to 400 again so the character move in the walk speed range.
    if(CharacterMovement->MaxWalkSpeed == 400.0f)
        CharacterMovement->MaxWalkSpeed = 900.0f;
    else
        CharacterMovement->MaxWalkSpeed = 400.0f;
}

By default, the movement speed value is 400. When the shift key is pressed, we change the MaxWalkSpeed to 900, which causes the character to move faster. When the shift key is then released, the method is called again and we set the value back to 400, decreasing the movement speed.

If you want to test your knowledge, you can implement the run mechanism in the HeroCharacter’s Blueprint. If you accept thus challenge, remember first to delete the C++ code. It should looks like this:

Blueprint version of the run mechanism

Blueprint version of the run mechanism

 

I personally prefer to keep all the character’s logic in code, but this exercise may serve as practice to increase your skills in the Blueprint Editor, something that’s new for much of us and I’m sure caught your attention :). I also recommend to expose the max and min values of the MaxWalkSpeed so it can be modifiable from the Editor and to avoid going to the code to change its values. Remember, in order to make variable values modifiable via the editor, you have to use the UPROPERTY macro, but this time I won’t show you how I did it so you must check how we did it previously and use your imagination. I’m sure you can do it 😉

Build and play the game. Let’s try it out. While you’re walking, press the shift key and you will see how the character moves much faster. We’re not quite finished yet because the character still uses the walk animation while running so it looks weird.

Adding the run animation to the character

Let’s add another animation to our character – the run animation. We will use the Idle/Walk state created previously to add the run animation. We will also use the same node to blend between the animations. An amazing feature of this type of node is that we can add more than two animations. The idea is to modify it to set three control points: the first one at the start of the graph will use Idle animation, the second in the middle of the graph will use the Walk animation, and the third at the end of the graph for the Run animation.

Import Run.FBX from your resources, open the IdleWalkBlendSpace1D created in the last tutorial, change the X Axis Range property to 900 (the movement value of the character when it’s running), and click the Apply Parameter Changes. Now add the Idle animation at the start, the Walk animation in the middle, and the Run animation at the end. Make sure the Enable Preview BlendSpace option is checked and move the cursor over the graph to see the blend between the animations according to the speed value. Pretty cool and easy, ehhh???

New IdleWalkBlendSpace1D configuration for the Idle/Walk/Run state

New IdleWalkBlendSpace1D configuration for the Idle/Walk/Run state

 

Save, play the game and try to run. Now our character walks and runs perfectly.

Running character pressing the D+Shift keys

Running character pressing the D+Shift keys

 

Adding coins in the scene for the character collect.

We already have our character walking, running and jumping through the scene in a side-scroller style, but we need to add some purpose to the game because wandering around isn’t much fun. Let’s try to improve it a little by adding some coins in the scene for the character to collect, a common feature in this style of game. In next tutorials, we will see what our character wins with these coins.

First, we need a coin model. I’m sure you can find several StaticMesh that you can use as coins from the MarketPlace. I strongly recommend you take some time to visit the MarketPlace. I’m sure that you will find a lot of things that you will love and many of them are FREE!! :D.

Anyway, you can download the FBX of a very simple coin here :S Its not amazing but it works for this tutorial. Import the FBX file to the project (I created a Coin folder). In the importing window, you will notice that Unreal detects that this is a StaticMesh. Expand the advanced options and select the materials and texture options. This way, we’re also importing the model’s material. That is also extremely simple.

Now with the resource imported, let’s create the C++ class that encapsulates all the coin logic. Create a new class in the Editor and named it Coin. This class will inherit from Actor. Select Yes when the Editor asks you if you want to open the class in the IDE. Modify the .h file so it looks like the following:


/** Represents a coins. The character can collect them by colliding with them. */
UCLASS()
class ACoin : public AActor
{
GENERATED_UCLASS_BODY()
   
    /**
     * USphereComponent is sphere shape component generally used to detect simple collisions
     * This will be the root component of the coin and with it we will detect the collisions between the character and the coins.
     */
     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Coin)
    TSubobjectPtr<USphereComponent> BaseCollisionComponent;
   
    /** Coin's StaticMesh, we used previously in the Character. We store the coin's StaticMesh instance. */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Coin)
    TSubobjectPtr<UStaticMeshComponent> CoinMesh;
   
    /**  Boolean variable to enable/disable the coin */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Coin)
    bool bIsActive;
};

I recommend you pay attention to the code comments because I always point out which function has every line. Basically, we define the BaseCollisionComponent attribute to be the root component of our coin which we will use to detect collision. The other attribute is the UStaticMeshComponent which is used to define the StaticMesh that represents the coin in the level. The last one is the bIsActive we will used as a flag to disable the coin when the user collides with it.

Go to the .cpp file and modify the constructor to match the following code:


ACoin::ACoin(const class FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
{
    //Create the USphereComponent instance.
    BaseCollisionComponent = PCIP.CreateDefaultSubobject<USphereComponent>(this, TEXT("BaseSphereComponent"));
   
    //Initialize the Actor's RootComponent with the USphereComponent.
    RootComponent = BaseCollisionComponent;
   
    //Create the UStaticMeshComponent instance
    CoinMesh = PCIP.CreateDefaultSubobject<UStaticMeshComponent>(this, TEXT("CoinMesh"));
   
    //We add the UStaticMeshComponent as child of the root component
    CoinMesh->AttachTo(RootComponent);
   
    //By default the coin will be active
    bIsActive = true;
}

We instantiate the USphereComponent as the coin’s root component. We also create the UStaticMeshComponent instance. We will soon configure it from the Editor and then added it to the RootComponent. Finally, we set the bIsActive variable to true because we want the coin to be active when it’s created.

Adding coins to the scene

Let’s create the Coin’s Blueprint as we did with the Character in the last tutorial. In the ContentBrowser, select the Coin folder, then right click and select the Blueprint option. Finally, set Coin as the base class and name it CoinBlueprint. As you will see, it contains the same components that we define in the USphereComponent constructor, for instance, the RootComponent and the UStaticMeshComponent. Unfold the CoinMesh and select the StaticMesh imported for the coin.

CoinBlueprint component's section. Adding the StaticMesh component

CoinBlueprint component’s section. Adding the StaticMesh component.

 

Now we will add some coins to the scene. Find the CoinBlueprint in the ContentBrowser and drag it into the level at the position where you want to place a coin. This must be a place reachable by the character. It’s important to remember that in this style of game, the character is placed on a fixed Y axis, so you must place the coins at the same Y value as the character in order for him to collide with the coins.

This is the simple level I made:

Level in edition mode with coins added.

Level in edition mode with coins added.

 

We added the coins to the level, but so far they don’t do anything. If you walk into the coins, nothing happens. Besides, the coins look very bad because they’re static. Let’s add some life to them.

Collision mechanism to collect the coins

At the moment, the character hits the coins but there isn’t any feedback. Let’s add the collision detection that, as its name suggests, will detect the collision between the character and the coins. We will have a “Collected coins” variable that we will increase when the character collides with a coin. We will create and call the OnCollected() method in the Coin class where we will disable the coin, delete it from the scene and print a log on the screen to debug the algorithm.

Open the HeroCharacter.h and add the following declarations:


    /** Character's amount of coins collected */
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category=Coins)
    int32 CoinsCollected;

    /** It's called constantly in the character's Tick to check if its colliding with a coin */
    void CollectCoins();
   
    /**
     * Gets executed automatically by the engine every frame
     * @param DeltaSeconds the difference in seconds between the last and current frame.
     */
    virtual void Tick(float DeltaSeconds) OVERRIDE;

Before we get deep in code, let’s theorise on the functionality that we will use. Every Actor in UE4 implement the Tick method. This method is called automatically by the engine every frame. If you are reading this tutorial, you are probably familiar with game development, at least with its theory. If so, you will know this method’s significance. Basically, all of the Actor’s algorithms that must run constantly will be called inside this method. The method receives a float as a parameter. This is the amount of time, in seconds, that has passed since the last tick/frame. This value is commonly used in a variety of tasks. For instance, it can be used as a multiplier when modifying the character’s position or rotation in order to achieve an execution depending on the framerate as to avoid gaps in case that the framerate decreases.

So we will call the CollectCoins method in the Tick method so it runs constantly. Internally, this method checks if a coin is inside the Character’s CapsuleComponent. If it is, that means that the character is colliding with that coin do we should trigger our collision mechanism.

Open the HeroCharacter.cpp file. At the end of the constructor implementation, add the CoinsCollected = 0 line. This causes the CoinsCollected variable to be set to zero when the character is create and is done because hasn’t collected any coins. Add the CollectCoins and Tick methods:


/** It's called constantly in the character's Tick method to check if is colliding with a coin */
void AHeroCharacter::CollectCoins()
{
    //AActors array to save temporary all the actors that are colliding with the character
TArray<AActor*> CollectedActors;
   
    //GetOverlappingActors method of the CapsuleComponent. This method returns all the actors colliding with the character in the array we pass as parameter.
    //All the objects that are currently inside the capsule.
    CapsuleComponent->GetOverlappingActors(CollectedActors);
   
    //We iterate through all the objects inside the CapsuleComponent.
    for(int32 i = 0; i < CollectedActors.Num(); i++)
    {
        //We have to cast the array elements to ACoin because the array is declared as AActors.
        ACoin *Coin = Cast<ACoin>(CollectedActors[i]);
       
        //We make sure that the coin is active and that the Destroy method hasn't been called
        if(Coin != NULL && !Coin->IsPendingKill() && Coin->bIsActive)
        {
            //We increase the coins collected amount.
            CoinsCollected++;
           
            //Finally, we call the OnCollected method of the Coin class to execute all the collect coin logic.
            Coin->OnCollected();
        }
    }
}

/**
* Gets executed automatically by the engine every frame
* @param DeltaSeconds the difference in seconds between the last and the current frame.
*/
void AHeroCharacter::Tick(float DeltaSeconds)
{
    Super::Tick(DeltaSeconds);

    //In every update is called the CollectCoin to constantly check if it's colliding with a coin.
    CollectCoins();
}

Take a minute to read every line’s comments to really understand the collision detection process. This is a very simple collision detection but it’s enough to understand how to work with it in UE4 :). It’s important to clarify that inside the CollectCoins method we have a reference to the ACoin class created by us. To avoid errors, we have to add #include “Coin.h” declaration at the top of the .cpp file below the #include “UE4Demo.h” line.

Only one small thing is left. If you try to build now you will get an error because when a collision is detected, the OnCollected method of the Coin class is called and we haven’t implemented this method yet. Let’s do that now:

Add the method’s declaration to the .h file and the implementation in the .cpp.


/** It's called when a collision is detected */
void ACoin::OnCollected()
{
    //To Debug we print a log on the screen
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "Coin Collected !!");
   
    //We change to false the bIsActive flag
    bIsActive = false;
   
    //We call the Actor's Destroy method, to remove the coin from the scene
    Destroy();
}

This method will be called when a collision is detected. First we use the AddActorLocalRotation method of the framework’s GEngine object. This method is very handy because it allows us to print messages in a specific colour at a certain amount of time on the screen. To debug our code in runtime is very useful. Here we print Coin Collected when the character collides with a coin. Besides that, we set the bIsActive variable as false and we remove the coin from the scene using the Actor’s Destroy method.

Build and run the game one more time. Now walk into a coin … Very nice!! When the character passes through a coin, the coin is removed from the scene. Internally, the amount of coins collected is increased (at the moment there isn’t any visual feedback regarding this value) and then we print the temporary Coin Collected¡! log to the screen.

“Debugging” the scene collisions

If you are good at paying attention to small details, I’m sure you will notice a small problem with this collision mechanism. Try to reach a coin slowly, you will notice that the collision occur before the character actually touches the coin. To find the problem, UE4 provides us with an amazing command that allows us to check the collision component of an actor at runtime. Run the game one more time and pay attention in the upper-right corner of the Editor. You’ll notice there is a text field that allows us to write commands. Write in the field: show COLLISION and press the Enter key. Immediately, the collision component appears over every actor .

Run time game with the show COLLISION command active to debug the Actor's collision component.

Run time game with the ‘show COLLISION’ command active to debug the Actor’s collision component.

 

Notice two things that could the cause the problem. First, the coin component is bigger than the coin itself. Second, the character’s capsule radius could be reduced. End the game execution and open the CoinBlueprint then select ROOT in the components mode. In the Details panel, search for the Shape section that holds the sphere radius. Change its value to 15 and check out the preview showing how the sphere surrounds the coin. Notice how this adjustment better fits the model. Save and try again. You will see how the collision detection has improved as now the character has to be very close to catch the coin.

Modifying the sphere component radius to adjust the collision detection

Modifying the sphere component radius to adjust the collision detection

 

You can do the same with the character’s capsule if you want to continue improving the collision mechanism. It’s important to mention that this value can be defined in the class constructor by code. Try to do it by yourself to practice.

Rotating coins on its own axis.

To add some life to the coins, we will rotate them on their own Y axis. Close the editor, open the Coin.h class and add the following lines:


    /** Coin rotation factor */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Rotation")
    FRotator RotationRate;
   
    virtual void Tick(float DeltaTime) OVERRIDE;

We simply add the Tick method and a new RotationRate attribute of FRotator type. This is the rotation vector we will use to rotate the coin in every Tick call. Actually, there is no need to declare an attribute but in doing so, we gain the opportunity to configure the rotation factor in the editor, thanks to the UPROPERTY macro.

Go to Coin.cpp file. At the end of the constructor, add this two lines:


    //Initialize the coin rotation factor in each update
    RotationRate = FRotator(0.0f, 180.0f, 0.0f);
   
    //Enable the Tick method of this Actor
    PrimaryActorTick.bCanEverTick = true;

The first line is the rotation vector’s initialization. By default, the Tick method isn’t called automatically in an Actor inherited class such as our Coin class. To enable the call to the Tick method in this class, it’s necessary to set the PrimaryActorTick.bCanEverTick attribute as true.

Very well, now add the Tick method’s implementation:


void ACoin::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    //Adds a rotation factor to the coin in each Tick call
    AddActorLocalRotation(this->RotationRate * DeltaTime, false);
}

The code is very simple. Basically we add a rotation factor thanks to the AddActorLocalRotation, in each update.

Ready!! Build, run and test. Super nice, right!! The coins are constantly rotating in the scene, waiting to be caught :).

Implementing the coin logic in the Blueprint.

As I already mentioned in the last tutorial, the decision of whether to use the code or the Editor is up to the individual. I personally follow these two rules of thumb. 1. Implement all the related things only in one place (for maintenance purpose). In other words, don’t have half of the logic in code and the other half in the Blueprint. 2. If the Actor logic is very simple like it is for this coin, the ideal solution is to use the Blueprint. However, when the thing needed to be implemented is large and complex, I personally prefer to use C++.

You could have some practice implementing the coin logic in the Blueprint. Do you dare? It should look something like:

Coin VisualScript

Coin VisualScript

 

Notice that we implement what happens when the OnCollected method is called in the Blueprint. We still have to add in the method’s declaration, the UFUNCTION(BlueprintNativeEvent) macro, or the UFUNCTION(BlueprintImplementableEvent). Next I will explain both:

UFUNCTION(BlueprintImplementableEvent) is designed to be overridden by a blueprint. Do not provide a body for this function; the automatically generated code will include a thunk that calls ProcessEvent to execute the overridden body.

[UFUNCTION(BlueprintNativeEvent) macro]. This function is designed to be overridden by a blueprint, but also has a native implementation. Provide a body named [FunctionName]_Implementation instead of [FunctionName]; the automatically generated code will include a thunk that calls the implementation method when necessary.

For example, if you want to test the OnCollected method, the definition will be like this:


UFUNCTION(BlueprintNativeEvent)
void OnCollected();

And the implementation should look like:


void ACoin::OnCollected_Implementation()
{
    //As Debug we print a log on the screen
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "Coin Collected !!");
   
    //We change the bIsActive flag to false
    bIsActive = false;
   
    //We remove the coin from the scene with the Destroy method
    Destroy();
}

The silver lining is that we can have our code implementation, but we can also override this implementation in the Blueprint … cool, right? UE4 stuffs!! :). Try the coin logic implemented in VisualScript in order to gain confidence in the Blueprint Editor, one of the wonders of UE4.

Conclusion

Well, I think it’s time to call it a day. In this second tutorial, we did several new things. Among them, we made a simple collision detection mechanism We implemented the Tick method. We added the run and jump animations to the character. We configured our game camera to the 3D Side-Scroller style. We saw some variables and macro class functions for the integration between the code and the Blueprint Editor, etc.

In next tutorial, we will continue to build our game. We will add a HUD so the user knows the number of coins collected, and the time available to achieve some goal. We will define the GameMode, the win and lose conditions and so much more ;).

I hope you found this tutorial useful. If you did, please leave me your comments and share this tutorial with the rest of your friends who are also passionate about game development with UE4. Till next time!!… Bye.

Getting Started with Game Development using Unreal Engine 4

In my humble opinion, Unreal Engine 4 is one of the most powerful game engines that exist right now, and the Epic Games team has released a license for all user, for just 19 USD a month.

This first tutorial gives an introduction to UE4. We will create the basis for our game by having a character walk through a level. We will use a fixed camera and basic game controls. This simple start will allow us to:

– Learn how to import 3D models to the project.
– Create the necessary classes to control the character.
– Understand the philosophy followed by Unreal’s framework in its classes model.
– Give an introduction to programming in Unreal Engine using C++ language.
– Understand the communication between the C++ classes and the Unreal Editor.
– Know the animation mechanism of the character.
– Introduce Visual Scripting using the Blueprint Editor.

Getting Unreal Engine 4

Getting the game engine is simple. First, you need to register at https://www.unrealengine.com/register and pay the 19 USD. Trust me, it will probably be the best 19 USD you ever spend. After you’ve paid and registered, you’ll have access to the same tools that the Epic Games team work with.

The next step is to actually get the engine. There are two ways to do this. The first way is launching the Launcher application. This application will allow us to download the latest version of the engine. The other way is to build the engine from the source code. That’s right, we have access to all the source code of the engine.

The official website gives a good explanation on the steps needed to download and build the source code from https://github.com/EpicGames/UnrealEngine/releases We’ll leave this topic here. If you have any problems with this process, please leave me a comment.

Model and Animations of the Main 3D Character

First thing we need to start our game is the main 3D character’s model and its animations. All of the 3D models that are used in a game, including the characters, objects, environments and animations, are created by the designers and animators of our team. We use modeling and animation 3D tools like 3DsMax or Blender. When the modeling and animation processes are over, the resulting model is exported with its skeleton and animations in the FBX format.

The 3D modeling and animation isn’t my strongest suit☺. Let’s start with the FBX resources from one of the sample projects released with UE4. This is exactly what our designer’s team will give us. You can download the resource from: https://d26ilriwvtzlb.cloudfront.net/a/a7/ThirdPerson_FBX.zip

Extract the contents of the .zip file. For now we will only work with the HeroTPP.FBX, Walk.FBX and Idle.FBX files. If you have any 3D modeling software, such as Maya 3D, you can import these files into the software, interact with them and examine them.

HeroTPP.FBX file loaded in Maya 2015. Skeleton view.

HeroTPP.FBX file loaded in Maya 2015. Skeleton view.

HeroTPP.FBX file loaded in Maya 2015. Model view.

HeroTPP.FBX file loaded in Maya 2015. Model view.

 

Hero.FBX is the 3D model of our main character along with its skeleton that will allow us to animate the model. Idle.FBX and Walk.FBX are, as the name suggests, the idle and walk animations. These last two files don’t contain any model because a model isn’t necessary in animation files. Animation files only hold information about the skeleton’s movement. That’s why there’s no need to export the 3D model when we export the animations.

Creating a New Project in Unreal Engine 4

Now that we are ready to begin, let’s start by creating the project. This is a very simple task in UE. We need to open the editor. The editor will show a window with two tabs: the Project tab, that contains all the previously created projects, and the New Project tab, that as its name suggests, allow us to create a new project.

A cool feature when creating a new project is a project group called Templates that we can use as base for our game. Using this, we can select the type of game that we want to develop. The available options are: 3rd Person, 1st Person, Top-Down or Side-Scroller.

Our goal in this first tutorial is only to offer an introduction to UE4. We aren’t going to use any of these relatively advanced templates. Instead, we’re going to create our project practically from scratch. We will select the Basic Template option. Select New Project/Basic Code. In the text field below type in the name of your project. In my case, I will use UE4Demo. Finally, we click the Create Project button.

Window to create and open a Project in Unreal Engine 4.

Window to create and open a Project in Unreal Engine 4.

This will create the project and will automatically open the IDE according to the operating system we’re using, Visual Studio 2013 in Windows case and XCode 5.1 in MAC OS. This tutorial is being developed in MAC OS so I’m using XCode as my IDE. Once the IDE has opened the project, we have to compile the project so we can open the Editor. Click in the upper-left corner to select “Scheme UE4DemoEditor-Mac” and then the “Product/Build For/Running” menu option.

The building process might take a while. Once it’s over we can select the “Product/Run” XCode menu option. This will prompt the Editor with our project opened.

Unreal Engine 4 Editor with our new Project just opened.

Unreal Engine 4 Editor with our new Project just opened.

UE4 provides us a scene with some objects added to it. For now, let’s leave the scene this way. You can select the Play button from toolbar to see all of the objects that compose our current scene. By default, we the control the camera with the mouse and keyboard. We could move through the whole scene, but this isn’t our goal. We will like to add our character to this world :)

Importing our character 3D model.

We already have our 3D models exported as FBX files with its skeleton and its two basic animations☺. Now let’s import them into our Project. In the left-bottom corner panel of the Editor we have the Content Browser. This panel is where we will have all of our game resources. Select the New Folder option and give it a name to the folder. For instance, “Character”. After this operation we have a new folder in the content browser. Go into this folder, select “Import” and search the FBX file of the character: HeroTPP.FBX (we will be importing the FBX files of the animation later on). Remember that this FBX file contains the 3D model and its skeleton. Select “OK” and the FBX “Import” window will appear. The Skeletal Mesh associated with the file will automatically be selected.

FBX Import window of Unreal Engine 4

FBX Import Window of Unreal Engine 4

Let’s take a break and theorize a little. As you can see in this import window, there are three types of resources that could be imported from a FBX file: a Static Mesh, a Skeletal Mesh and an Animation.

Static Mesh: A static object of our game. For instance, a chair, a building, the environment are static meshes. In other words, a Static Mesh is a 3D model without an animations or a skeleton.

Skeletal Mesh: Exactly the opposite of a Static Mesh and also the type of model that we’re importing. A Skeleton Mesh is a 3D model with an associated skeleton that can be animated. So to be clear, all the characters of our game will have a Skeletal Mesh.

Animation: Animations contain the transformation information of each of the bones of a skeleton to bring it to life when performing actions such as walking, jumping, etc. Examples of this are the Idle.FBX and Walk.FBX that we will soon import.

UE4 automatically detects that we’re importing a Skeletal Mesh so isn’t necessary to change anything. The default parameters are suitable to our purpose. The only thing left to do is to click the Import button. Ignore any warnings received during the import process. In the following tutorials, we’ll see the whole Export/Import process and the Animation Rigging Toolset provided to us by Epic Games to prepare the animations and models.

Once the model is imported in the Content Browser, we’ll have three new elements: Hero (SkeletalMesh), Hero_PhysicsAsset (PhysicsAsset) and Hero_Skeleton (Skeleton).

If you double click in the SkeletalMesh, you can open the imported model in the Persona Editor of UE4. Persona is the Skeleton, Skeletal Meshes, Animations Blueprints and other animations elements Editor in UE4.

The Skeletal Mesh of our character in the Persona Editor

The Skeletal Mesh of our character in the Persona Editor

When opening the Skeletal Mesh in the Persona Editor, we have on the left side the Skeleton Tree. This skeleton tree is composed of all of the bones that make up the skeleton’s model. At the bottom, we have the Mesh Details Panel. This is composed of several sections that describe the materials applied to the selected model. At the moment, we don’t have any material. Or rather, we only have the default material that gives an opaque view to our character.

The Hero_PhysicsAsset is the PhysicsAsset generated automatically during the process of importing the Skeletal Mesh. For now, we won’t use this asset. In future tutorials, we will see what it is used for, but if you’re a curious person double click on it, it will open the UE4 specific editor for this kind of assets. In the left-upper corner, there is a Simulate button. Click on the button and see what happens. This will give you an idea of the goal of this resource, generated automatically when importing the Skeletal Mesh.

Finally, the Hero_Skeleton is the model’s skeleton that we’ve just imported. A brilliant feature of UE4 is that we’re able to share the same skeleton between different 3D models that look relatively similar. So instead of importing the 3D model and its skeleton for each of these similar looking models, we will import the skeleton only once and then we will associate the skeleton with the different models.

So now that we’ve all the resources associated with the main character in our project, we’re able to start the main part — the coding :).

Introduction to Programming in Unreal Engine 4

A project in UE4 is basically composed of two big pieces that work together. The levels, which are developed on the Editor, and the programming project that is developed in our programming IDE. We already saw how when we created a project in UE4, the engine creates both of these pieces. We will now focus on the second part, the code.

The programming language used in UE4 is C++. When creating a new UE4 project, a C++ is automatically generated. This C++ project can be edited in XCode if you use MAC OS or Visual Studio if you use Windows. This project already contains the basic classes of our game. Open your IDE and select the project just created. The source code for our game, UE4Demo, is contained in the Source files. All of the UE4 framework source code is contained in the Engine files. The possibility of having access to this framework code is amazing because we are able to inspect the implementation of the framework classes or what is used for a certain property. This code is commented by Epic Games Team. Before we create our first class, we will quickly discuss some comments about the philosophy followed by UE4 in its framework.

In UE4, all the elements that appear in our game are Actors (in other words they inherit of the AActor class). For example, a chair, table, enemy or even the main character are all actors. The game elements that are controlled, non-static, and have a behavior are known as Pawns. There is a special kind of Pawn called Character. This is the Pawn that represents the main character and contains the particular implementations designed only for the model controlled by the gamer. For instance, if in our game we have a main character and an enemy, the first one will be a character and the second one will be a Pawn. All Pawns are controlled by a class called Controller. The Character is controlled by the PlayerController class. The PlayerController class receives input from the game and uses data to control the character in the game. Basically, the PlayerController object represents the human actions in the game. All non-player characters pawns are controlled by the AIController class.

It’s a little bit tricky at first, but bit by bit you will become familiarized with UE4 and come to understand its philosophy, class hierarchy, and their relations.

In the source code for our game, we have a file with the same name as our project. In my case, this file is called UE4Demo. Inside this file are a few classes that we can start working with.

The first class we are going to review is UE4DemoGameMode. This class defines our game’s Game Mode. The Game Mode describes the game’s rules, such as the win and lose conditions. The Game Mode also has the responsibility of defining the PlayerController and the default Pawn, among other things. UE4DemoGameMode is the core of the game. If we open the .h file, we will notice that this class inherits from AGameMode and for now doesn’t contain anything else.

//AUE4DemoGameMode.h
#pragma once

#include "GameFramework/GameMode.h"
#include "UE4DemoGameMode.generated.h"

UCLASS()
class AUE4DemoGameMode : public AGameMode
{
    GENERATED_UCLASS_BODY()
};

The class declaration contains two macros that should have caught your attention: UCLASS() and GENERATED_UCLASS_BODY.

UE4 has a strong system for the object handling. The base class for the objects in Unreal is UObject and the UCLASS macro should be used in classes that inherit from UObject. This informs the UObjects system handler of the existence of the new class.

When including these macros in our classes, we ensure that they have access to some of Unreal’s low level mechanisms, such as garbage collection, serialization, automatic initialization of properties, automatic editor integration, and more.

Now let’s see our GameMode implementation. As you can see, in UE4DemoGameMode.cpp, only the constructor is implemented.


//AUE4DemoGameMode.cpp
#include "UE4Demo.h"
#include "UE4DemoGameMode.h"
#include "UE4DemoPlayerController.h"

AUE4DemoGameMode::AUE4DemoGameMode(const class FPostConstructInitializeProperties& PCIP)
    : Super(PCIP)
{
    PlayerControllerClass = AUE4DemoPlayerController::StaticClass();
}

For now, the constructor only contains the initialization of the PlayerControllerClass attribute. This PlayerController for our game will act as an interface between the user and the character. In here, we initialize the PlayerControllerClass with a static instance of our own UE4DemoPlayerController. That’s why we can move like a ghost through the whole level when we run the project.

We already have a PlayerController but so far we don’t have a Character that represents the physical body of the character. The UE4DemoPlayerController class previously created is the PlayerController implementation for our game. This is empty at the moment. For now, we don’t need to personalize it. All that we need to control our character is already in the APlayerController class. We’re going to leave this class for now but can come back later when we need to add some custom behavior.

That’s enough theory for now. Let’s code!

We’ve already imported all the resources for our character in the Editor, so now let’s bring it to the scene :)

Creating our First Class in UE4

The first thing we’re going to do is to give life to our character, so we will create our character class. As I said earlier, the class in charge of controlling the character is the Pawn class. This is a special kind of Pawn, a Character, that’s why our class has to inherit from it.

The easiest way to add a new class to the project is through the Editor. Go back to the Editor. In the main menu, select “File/Add Code to Project”. Now a window will prompt to select the base class. As we already said we will use Character as our base class. In this window, you can inspect the most common UE4 classes and read a brief description on each of them. So select “Character” and hit the next button. Type in the name of your class. I will use HeroCharacter. Finally, end the class creation process. When the process completed, you will be asked if you want to open the IDE. Select OK and when the IDE opens, our newly created class will be there. The class structure was already described earlier. A class that inherits from ACharacter includes the UCLASS and GENERATED_UCLASS_BODY() macros that import the low level mechanisms (garbage collection etc.) from UE4.

Configuring the Character from the Blueprint Editor

We’ve configure our main character so this is a good time to make a personal comment about one of the mechanisms of UE4 that was very difficult for me to get used to because I came from a background of 2D game development. In game engines like Cocos2d, the majority of game logic and building is done using code. In UE4, the work philosophy is very different. Here we have two options for making our game. The first option is to do things by hand coding. This can cause us to have a slower rhythm in the production of code and is susceptible to bugs. The second option is by using the Editor. The downside of this option is that this new method of programming for can difficult for us programmers to adjust to. In this tutorial, we will work in the Editor or by code in the IDE. We leave the decision to you to choose which method to use to perform different tasks.

To demonstrate the UE4 integration between the code and the Editor, we will configure our character in both ways. This will let us show how amazing the communication between the C++ code and the editor is.

Now that we have our own class to represent the Character, let’s configure its components from the Editor. The first step is to open the Editor. In the Toolbar, we have the Blueprints button. Go ahead and select Blueprints/New Class Blueprint. In the bottom of the prompted window is a section named Custom classes. In that section, search and select the class we’ve just created for our Character, HeroCharacter. Name it, for instance as HeroCharacterBlueprint, and select the folder Game/Character to store the file. Once the process has finished, the Blueprint Editor will open up in the Components mode where we will configure our Character.

imagen_06

In the left side of the editor, we have the Components panel. As its name suggests, this panel contains all the components that compose the Character. The bottom panel shows the properties of the selected component. The CharacterMovements is the component that handles the movement of the Character. For example, here we hold the Max Walk Speed that is the top speed with which our character will be able to move. There are many more properties, and I recommend you to take a quick look over some of these properties to have an overview of all all the things that are configurable regarding movement.

The other component that the Character has is the CapsuleComponent. The CapsuleComponent is in charge of the collision detection. In the Viewport of the Editor, you can see it graphically as a transparent capsule, which represents the collision zone of the character.

At last, inside the CapsuleComponent we have a Mesh and I’m sure you already figured it out that is the Mesh that represents our character. There is also an ArrowComponent which informs us of the direction of the Character.

The only thing left is to finish the configuration of the Mesh. We must select the mesh component in the Component panel and the skeletal mesh property in the Details panel in the Mesh section. Select the dropdown and select the Skeletal Mesh of our character, the one we created when our hero FBX file was imported. We will see our hero model in the viewport. Use the translation and rotation tools to place the Mesh inside the CapsuleComponent and match the direction to the ArrowComponent. Finally, click the Save button in the upper-right corner of the Editor, and now we’ve given a body to our character. Let’s try it out. Close the Editor and run the game to see what we’ve so far.

Character’s Mesh in the right position in the HeroCharacterBlueprint

Character’s Mesh in the right position in the HeroCharacterBlueprint

:( As you noticed there are no changes. We still have game controls thanks to the default PlayerController, but we don’t have yet our character. The problem is that there are some things that we still have to setup.

First, make sure you have properly configured the GameMode. Click in the World Settings button of the Toolbar and make sure that you selected our UE4DemoGameMode class in the GameMode section. Under GameMode are all the elements configured for our game mode. For instance, in the PlayerControllerClass section, we have our PlayerController (UE4DemoPlayerController). Due to that in the UE4DemoGameMode constructor we initialize this property, but Pawn Class has the Default Pawn value, so we’re not over yet. The problem is that we created our character and gave it a body but we haven’t defined in the GameMode/Pawn Class that our HeroCharacter class is our character. Let’s do this by code, once again to demonstrate the amazing communication between the Editor and the code. So next we’re going to close the Editor and open the C++ project, search the UE4DemoGameMode class and modify the constructor implementation to match the below code:

AUE4DemoGameMode::AUE4DemoGameMode(const class FPostConstructInitializeProperties& PCIP)
    : Super(PCIP)
{
    PlayerControllerClass = AUE4DemoPlayerController::StaticClass();
    
    //Gets in PlayerPawnBPClass.Object the HeroCharacterBlueprint class created and configure from the Editor
    static ConstructorHelpers::FObjectFinder<UClass> PlayerPawnBPClass(TEXT("Class'/Game/Character/HeroCharacterBlueprint.HeroCharacterBlueprint_C'"));
    
    if (PlayerPawnBPClass.Object != NULL)
    {
        DefaultPawnClass = PlayerPawnBPClass.Object;
    }
}

Let’s take a minute to explain step by step what we just did cause even if you are an experienced C++ developer the previous syntax could be tricky. In the first line, we search and get the instance of HeroCharacter created in the Editor through the Blueprint Editor. Next we create a variable of FObjectFinder type, this is a public parameterized structure that holds inside another structure this one named ConstructerHelpers. FObjectFinder receives in its constructor the address of the object that we will instantiate, if the object is found successfully its instance it’s stored in the Object property.

It might be useful to seize the opportunity to review the framework source code. You can see the implementation of all structures, such as ConstructorHelpers, FObjectFinder, etc. In XCode, all you need to do is click the name while pressing the cmd key and you’ll be taken to the structure declaration. So take a minute to familiarize with these new structures in order to understand how they work. Also, you must note that to define the route to FObjectFinder we use the TEXT macro. This macro is used for all strings in the engine in order for the compiler to convert the string to the right data type, which in this case is a TCHAR*.

So now we have in PlayerPawnBPClass.Object the instance of our Character (PlayerPawnBPClass is the name that we gave to the variable that we created of FObjectFinder type). The only thing left is to initialize the DefaultPawnClass with this object. The GameMode class has the DefaultPawnClass attribute. This represents the Pawn that our character will use and we only have to set our class.

Now we are ready to play. Compile and execute the game. When the game starts our character is automatically added to the level. This is the level created by default for the selected template in the project creation. The level has an Actor of type Player Start. When the game starts, it searches for an instance of Player Start and adds our character at this position. The bad news is that now we lose the game controls so we are unable to move through the scene and the camera point of view is now the character’s eyes :( Temporarily, we solve this by setting a static camera in our game.

Setting a static camera by code.

Right click inside the viewport in the editor and select the folding menu Place Actor/Camera. Use the translation tools and transformations to aim the camera in the Play Start direction so we will be able to see the character. This is how I did it:

imagen_08

Now let’s inform Unreal that the game view will be taken from this camera. For this we have to come back once again to the theory.

We already discussed how according to Unreal philosophy, PlayerController is the interface between the main character of the game and the human being. So it’s completely natural that the logic regarding the game view will be implemented in this class. PlayerController has the SetViewTargetWithBlend method. This method allows us to control the camera point of view. We will call this method to set the camera we created as the game’s camera. We can change the camera’s point of view any time we want to. In this case, we will configure it to the direction of the camera that we created. In order to do that I will introduce you to one of the most used events in Unreal, the BeginPlay event. All classes that inherit from AActor have this method which is called, as its name suggests, when the game is about to start. Let’s add this method to our PlayerController class (that inherits from AActor) and change the game camera.

Open UE4DemoPlayerController.h file and add the next line: virtual void BeginPlay() override; below the GENERATED_UCLASS_BODY() macro, with this declaration we’re able to override the base class method and define our customized behavior. Go on to the .cpp file to define the method. Add the below code under the class constructor. I recommend you thoroughly review the comments so you can understand what we are doing in each step.

/** Inherit method of the AActor class it’s called automatically by the engine when the game starts. */
void AUE4DemoPlayerController::BeginPlay()
{
    //Call to the Begin Play method in the base class.
    Super::BeginPlay();
    
    //We iterate among all the actors in the level through the TActorIterator
    //TActorIterator is a parametrized iterator that allow us to iterate among all actors in the level.
    for (TActorIterator<ACameraActor> It(GetWorld()); It; ++It)
    {
        //We get the current actor in the loop. As we only have an ACameraActor in the level, the iterator will iterate only once.
        ACameraActor* _mainCamera = *It;
        
        //We configure the new point of view of the camera as our game view.
        //SetViewTargetWithBlend can receive more parameters, but the method has default values and in this case we won’t need more specialization so we’ll leave like this.
        this->SetViewTargetWithBlend(_mainCamera);
    }
}

We’re ready. Compile and run the game. Now you can see the level and our character viewed from the camera that we created.

Setting up a static camera through the Blueprint Editor.

I’ll take a break to talk about the Blueprint Editor. I’m sure that when you start in UE4 you asked yourself the same question that I did when I started – how do I do this? Through the Blueprint Editor or coding it in C++? The answer is a question of taste. As you advance in UE4 you will know which one is the best option for each task.

In order to compare both methods the next thing we will do is to setup the camera like we just did by code using the Blueprint Editor without typing a single line of code. Keep in mind that not all tasks are the same, so maybe the one that you find simplest now won’t be the best option in another case. In the Editor’s toolbar, click the Blueprint button and select the Open Level Blueprint option. This will open a blueprint file that concerns all of the logic for the whole level.

Following the logic that we use in C++, the first thing to do is to implement the BeginPlay event. The Blueprint Editor is a visual scripting editor, so basically we’re still programming but graphically (yes I know this sounds a little crazy at first☺). In here, we can add new variables, system events, specific functions classes, etc., Basically, you can do nearly everything that you can do in C++.

To add the BeginPlay event, right click in the center of the screen and uncheck the Context Sensitive option and search the Begin Play event. We’ve added a node that represents the event to our visual script. Next we will get the reference to the level’s camera and call the SetViewTargetWithBlend method in the PlayerController class passing the camera as parameter. To get a reference to the PlayerController, remember this is a “global” script in the “Level” level and the C++ implementation was made inside the PlayerController. Add a new node of the Get Player Controller type. This node returns the reference to the PlayerController of the game. Now we need to call the SetViewTargetWithBlend. Again we add a new node with the method’s name and now we have all the elements that we need to complete our visual algorithm. There is only one thing left, connect all the nodes.

Our first node (BeginPlay) has a port that represents the output, in other words, the execution of the call to this method. The SetViewTargetWithBlend node has both an input and an output ports. Click the first node port and drag the arrow to the input port of the method node. Release it when a green mark appears. Now when the game starts, the BeginPlay event will be executed and this will call the SetViewTargetWithBlend method. This method belongs to the PlayerController class so we need to define which is its PlayerController. The Get PlayerController node has an output port titled Return value we will connect this port with the SetViewTargetWithBlend port named Target. In doing that, we are defining the reference to the method. Remember that we also have to pass another parameter, the Actor, which we will use to configure the camera point of view. Finally, we need to add the camera node. Save these changes. Close the editor and select in the level the camera was previously added. Now open the editor once again, right click and you will notice that you have a shortcut to add a Camera Actor node. Once added, connect the output port of the camera actor to the New View Target port of the SetViewTargetWithBlend. Now we are done, our visual script is complete. Click the upper-left corner button Compile.

Before we play the game, let’s delete the override declaration and definition of the Begin Play event in the C++ code. You can comment it out if you want to so you don’t lose the code. Close the editor and comment out the UE4DemoPlayerController.h class in the code. Do the same in the .cpp file.

Compile and run the game. As you’ll notice, we’ve achieved the same result☺. So I repeat once again it’s your call either to choose the Editor or the code. You will have to wait to gain your own experiences and find out about the pros and cons of each method.

I recommend you take a break here because we’ve reached the middle of the tutorial and there are many more things to come :)

Setting the character’s animations

So far we only have our static character viewed from a fixed camera, which isn’t very functional, so let’s give it more life.

Remember that our design team :) also gave us the character’s skeleton and the walk and idle animations ready to be imported. Open the Editor and create a new folder in the Content Browser. I named the folder Animations. Open the folder and import the two FBX files: Walk.FBX and Idle.FBX. There is an option that allows us to select the skeleton associated with the animation being imported in the importation process. Select the Hero_Skeleton and click the Import button to finish the process.

Now you can inspect both files. Double click on one of them in the Content Browser and the Persona Editor will open showing a preview of the animations. From this Editor you can see all the animations details, play it and so much more that well have to cover that in future tutorials. The only thing left is to associate the animations with the character.

Creating the state machine and updating the character with the idle and walk animations using the Animation Blueprints

The Animation Blueprint is the tool that UE4 provide us to implement all the animation logic in a really simple way, using the state machines and visual scripting.

So let’s create the Animation Blueprint for our character. In the Content Browser, go to the Animations folder, right click in an empty space and select Animation/Animation Blueprint. This will open the Animation Blueprint window. In the Parent Class section, select AnimInstance and below Target Skeleton section write the name of the skeleton that uses our hero, Hero_Skeleton, and finish the process by clicking on the OK button.

The engine will automatically add an AnimBlueprint to the Content Browser with its name selected in case that you want to change it. Name it whatever you like. I will name it HeroAnimBlueprint. Before we do anything in the blueprint, let’s define that our character will use this blueprint to control its animation. To do this, search in the Content Browser where your character’s blueprint is, in my case is in: Game/Character/HeroCharacterBlueprint, and double click it. In the upper-right corner select the Defaults mode and you will see a section named Animation. In the Animation Mode property, select Use Animation Blueprint and in the Anim Blueprint Generated Class, select the class that we create HeroAnimBlueprint_C. Now we are ready. Save and close the Editor.

imagen_09

Now that everything is well configured, let’s open the HeroAnimBlueprint. This will open the Persona Editor. Right from the start we’ve a node Final Animation Pose. We will connect this node to the output of the node that we’re about to create, but first, as usual, a little bit of theory.

In UE4, we have an amazing and intuitive mechanism to define which animations to play for characters based on the state of the character. For example, if the character is resting, the idle animation will be played but if the character is walking, the walk animation will be the one played. You can do blending between the animations in order to ease the changes between them. In this case, we need define if the character is in the idle state or the walking state. We’ll do this by adding a “velocity” attribute, and say that if this variable value is 0 then the character is idle so the idle animation must be played, but if the velocity value is anything other than 0, the walk animation should be played. We can do all of this through a state machine and visual scripting in the Animation Blueprint.

Next we create the state machine. Inside the AnimBlueprint Editor, right click and select StateMachine/Add State Machine. This will add a state machine node type to the scene. Change its name to something more descriptive, such as HeroStateMachine, or something else you prefer. Now, inside the state machine we define states that our character will have. Double click on the state machine node to edit it. Inside you will find a node named Entry. Right click, select Add State, and name the created node Idle. Now connect, as we did in the level blueprint, the output port of the Entry node to the input port of the Idle node just created.

Now that all is clear, let’s define the Idle state logic. Double click on the node to edit it, then right click and select Animation/Play Idle. This adds the node that represents the Idle animation that we previously imported into the Editor. Finally, connect this node to the Final Animation Pose node.

Let’s check how all the connections are set in each and every one of the levels. From inside to outside, we have the Play Idle node connected to the Final Pose Animation node. In the upper level (HeroStateMachine), we have the Entry node connected to the idle node. Lastly, in the most upper level (AnimGraph), the HeroStateMachine node connected to the Final Animation Pose. Click the compile button in the upper-right corner of the editor. Notice that in the left panel that you can see a preview of the character with the idle animation.

imagen_10.1

imagen_10.2

imagen_10.3

Run the game. Here is our character in its rest state and with the idle animation.

Main character in the game in rest playing the idle animation.

Main character in the game in rest playing the idle animation.

Setting the game controls

So far, we’ve created and set our character, a temporary fixed camera, and the character’s first animation. The next step, as I’m sure you’ve already guessed, will be to make our character walk :)

First of all, we need to define the game controls. In order to do this, go to UE Editor’s menu Edit/Project Settings, Engine/Input section, Bindings block, click the + button in Axis Mapping and unfold the arrow, in the writing field type MoveForward. Click the arrow aside the EditText and unfold the field and select the W key and set a 1.0 value in the scale. Click again in the + aside the MoveForward’s EditText and select in the new combobox the S key and set -1.0 in the scale.

Now click in the + sign of Axis Mappings to create another section at the same level of MoveForward. In the new EditText, write MoveRight and add two children: A with Scale of -1 and D with 1.

imagen_12

We just defined our game controls. We need to give these game controls names to be able to call this entry from code and the value selected in the combobox is the control that will call this action. The scale value is a numeric value that can be retrieved in the code as a parameter. Generally, this value will be either -1 and 1. Another thing to notice is that the scale value allows us to define actions under the same identifier (MoveForward) and define both directions, given it a positive value of 1 and a negative of -1.

Now let’s code our character logic to move through the scene with this controls. This is fairly simple but enough to understand how it all works out. Basically we will need two things: Override the virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) method of APawn class to register the callbacks that will be called when the entries just created (MoveForward and MoveRight) are pressed. Also, we need to implement the character movement according to the entry pressed.

Open the HeroCharacter.h class and modify it to match the code below:

UCLASS()
class AHeroCharacter : public ACharacter
{
    GENERATED_UCLASS_BODY()
    
protected:
    
    /**
     * Is called when the engine detects the configured entry to 'MoveForward'.
     * In this case when the user press the W or S keys
     */
    void MoveForward(float Value);
    
    /**
     * Is called when the engine detects the configured entry to 'MoveRight'.
     * In this case when the user press the A or D keys
     */
    void MoveRight(float Value);
    
    /**
     * APawn class method that allows to configure the controls binding
     * Is called automatically by the Engine
     */
    virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) OVERRIDE;
    
};

So far, we simply defined two methods where we will implement the logic when one of the keys that we defined is pressed and we also added the SetupPlayerInputComponent method statement to our class in order to override it.

Next, go to the corresponding .cpp file and modify it like this:

#include "UE4Demo.h"
#include "HeroCharacter.h"


AHeroCharacter::AHeroCharacter(const class FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
{

}

void AHeroCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
    //Informs the engine that when the MoveForward entry is detected call AHeroCharacter::MoveForward method
    InputComponent->BindAxis("MoveForward", this, &AHeroCharacter::MoveForward);
    
    // Informs the engine that when the MoveRight entry is detected call AHeroCharacter:: MoveRight method
    InputComponent->BindAxis("MoveRight", this, &AHeroCharacter::MoveRight);
}


/**
*   It gets called when the MoveForward entry is detected (When the keys W or S are pressed)
*  Calculates the direction of the character and applies it a movement (positive or negative) in that direction.
*
*  @param Value is equal to 1 when W is pressed and -1 when S is.
*/
void AHeroCharacter::MoveForward(float Value)
{
    if ((Controller != NULL) && (Value != 0.0f))
    {
         //Gets the current rotation
        const FRotator Rotation = Controller->GetControlRotation();
        
         // Creates the direction vector and applies the movement
        const FVector Direction = FRotationMatrix(Rotation).GetUnitAxis(EAxis::X);
        AddMovementInput(Direction, Value);
    }
}

/**
*   It gets called when the MoveForward entry is detected (When the keys W or S are pressed)
*  @param Value is equal to 1 when D is detected and to -1 when A is detected.
*/
void AHeroCharacter::MoveRight(float Value)
{
    if ( (Controller != NULL) && (Value != 0.0f) )
    {
        //Determines the direction of the side movements. Notice that we only have interest in the rotation in the Y axis.
        const FRotator Rotation = Controller->GetControlRotation();
        const FRotator YawRotation(0, Rotation.Yaw, 0);
        
         // Creates the direction vector and applies the movement
        const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
        AddMovementInput(Direction, Value);
    }
}

In here, we’ve a couple of things to comment, but I’m sure by now you already have some thoughts about it. In SetupPlayerInputComponent, we use the method’s parameter to set the callbacks that will be called when an entry is detected. In other words, with InputComponent->BindAxis(“MoveForward”, this, &AHeroCharacter::MoveForward); we are saying that when the Engine detects the MoveForward entry (defined as pressing the W and S keys) will call the method that matches the entry name, and the same principle is followed by the other entry. This is because we name the method as the entry, but this isn’t mandatory.

Next we check out the MoveForward implementation. This method receives a float as a parameter. This is the already known scale value registered in the Editor, so if the user press the W key, the method will receive a 1.0 value, and -1.0 if the S key was pressed. This method decides the rotation of the model and the direction vector in which it is oriented, and through the AddMovementInput method we make the character move in the resulting direction. Notice that the character moves upwards or backwards according to the value. AddMovementInput is APawn method that allows us to apply a movement to the Pawn in case that the character isn’t a physical body, like ours. In the MoveRight case, we apply the same logic but all of the calculations are based on the Y axis.

That’s all that we need for now. Compile and run the gamez. When the game starts, press the W, S, A, and D keys to control the character. Be careful not to fall off the edges of the platform ;). Unfortunately, we still have two problems with this movement. First of all, the character doesn’t rotate in the direction of the movement. Second, when the character is moving through the scene, it isn’t playing the walk animation. Let’s fix these two problems. To solve the rotation problem, open HeroCharacter.cpp and modify the constructor so that it looks like the following:

AHeroCharacter::AHeroCharacter(const class FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
{
    //This property by default has true value for the character
    //But in our displacement model we don’t want that the character rotates based in the controller rotation.
    bUseControllerRotationYaw = false;
    
    // CharacterMovement component configuration
    
     //This way we enable that the character rotates in the movement direction when the movement starts
    CharacterMovement->bOrientRotationToMovement = true;
    
    //Rotation factor to the previous property
    CharacterMovement->RotationRate = FRotator(0.0f, 540.0f, 0.0f);
    
    //We decrease the MaxWalkSpeed default value so the character walks slowly.
    CharacterMovement->MaxWalkSpeed = 400.0f;
}

Do you remember that the character has a CharacterMovement? We modified some values to change the default behavior of the character. I recommend you review the HeroCharacterBlueprint in the Editor to check all the properties that the CharacterMovement contains. Play around with them to see all that can be changed regarding the movement of the character.

Adding walk animation to the character

Now let’s focus on the last problem we have to solve. We need to make the character play the idle animation when he stays in rest, like it is now, but when the character is walking it should play the walk animation. In order to achieve this, we will see one of the two mechanisms that Unreal provide us to ease the transition between animations – the Blend Space.

Blend Space is the Animation Blueprint node that allows us to do blending between animations based in entry values. Unreal provides two types of Blend Space: the Blend Space used for several entries and the Blend Space 1D only for one entry. For this simple example, we will use the second one given that we only need one entry – the displacement velocity.

Once again open the Editor. Inside the Animations folder in the Content Browser, right click and select Animations/BlendSpace1D, name it IdleWalkBlendSpace1D, then double click it to open the respective Editor. We want to define the blending of the two animations using the value of the velocity variable. In the property panel of IdleWalkBlendSpace1D, in the X Axis Label, write Speed (this is only a reference, it could be any other word that represents the value that we have in mind to change from one animation to the other). In the range field, set 0 to 100. Now notice that at the bottom, there is a space with something that seems a graphic axis. From the Asset Browser, drag the idle animation and place it at the start of the x axis. Do the same for the walk animation but place it at the end of the axis. Done, now move the cursor over the axis and notice how in the Preview panel, the animation changes smoothly between the two animations by itself. Save and close the editor.

Setting the IdleWalkBlendSpace1D

Setting the IdleWalkBlendSpace1D

Now let’s modify the HeroAnimBlueprint so we can change the state machine for the behavior of the idle node (if you want to you can rename it to Idle/Walk). Now we will use this node to handle the two state. Enter to edit it, delete the idle node that we’re using and add the IdleWalkBlendSpace1D that we’ve created. This node, unlike the other, has an input port named Speed (the name we used when we created it), so in order for this to work we need to supply a value. There is an icon with a +V from Variable. This will allow us to add a new variable to the graphic. Click it and named it speed, drag it to the workspace and when it asks you Set/Get, select Get. Lastly, connect Speed to IdleWalkBlendSpace1D and this to Final Animation Pose.

imagen_14

We have to define, in the EventGraph, how we will provide the speed value to the idle/walk node. We will implement an algorithm that, in each loop of the animation, will get the Pawn (our character) after the call to the GetVelocity method that returns the displacement vector from the character in a given moment. Once we have the velocity, we calculate the distance of the vector and this will be our speed value. We will do all of this in HeroAnimBlueprint/EventGraph.

We’ve already seen the visual scripting in the Blueprint Editor so this will be a good time to reiterate upon how it works. It’s worth pointing out that we can also do this by code. But in the case, for controlling the logic of the animation behavior, I strongly recommend you to use the Blueprint because the preview tool allows you to easily check the changes and the final result. Also, all the animation mechanisms will be in a single place.

Open the HeroAnimBlueprint/EventGraph, then add and connect the nodes as in the next image. I won’t repeat how to do this step by step because detailed this earlier.

HeroAnimBlueprint/EventGraph algorithm contains the logic to set speed variable value used inside the state machine of the character to change between the idle and walk animations.

HeroAnimBlueprint/EventGraph algorithm contains the logic to set the speed variable value used inside the state machine of the character to change between the idle and walk animations.

Okay, now we’re really done. Compile, save the blueprint and click play to see the game. Pretty cool!!! Right, we have a character smoothly moving through the scene using the walk animation when it’s walking and the idle animation when it’s at rest☺.

Conclusion

We’ve finished this first tutorial: Introduction to Unreal Engine 4. In the next tutorial, we will modify the camera, the game controls and we will restrict the character movement in order to create a side-scroller style game. We will also add some more logic to the game, place some coins in the scene, and have our character collect all the staged coins in a given time in order to win or otherwise lose and start over. Meanwhile, please leave me your comments :). Also, you can follow me in Twitter (@nan2cc) to stay tuned for the next tutorials.

Introducción a la IA en UE4 (Variante en C++)

En Unreal Engine 4 tenemos dos grandes métodos a la hora de implementar alguna parte de nuestro juego. La variante en C++ y la variante en VisualScript mediante los Blueprint. Como ya hemos hablando antes, la decisión de irnos por una vía u otra es de cada cual, con la experiencia, se va logrando soltura a la hora de seleccionar que camino tomar.

En este tutorial no vamos a implementar ninguna funcionalidad nueva en nuestro juego. Vamos a implementar las mismas acciones que tiene el NPC del tutorial pasado, pero en C++. Esto nos va a servir para acercarnos un poco más al Framework C++ que nos brinda el Engine. Veremos como incluir nuevos módulos al proyecto. Cómo iterar por objetos del nivel. Cómo manipular la información guardada en el Blackboard que usa el AIController desde C++ y cómo crear Task y Services para el Behavior Tree totalmente desde C++.

Incluyendo el AIModule en nuestro proyecto.

Uno de los errores más comunes cuando se está comenzando en el mundo de C++ en UE4 y se crean clases que heredan de clases del framework que se encuentran en otros módulos, es que no se le dice a nuestro proyecto que vamos a usar clases de ese otro módulo. Cuando pasa esto e intentamos compilar el código nos da muchísimos errores de tipo “Linker“ que básicamente vienen dados porque estamos usando clases que el compilador no encuentra.

Para nuestro caso, como vamos a hacer referencia a varias clases del módulo de AI, tenemos que incluir el módulo AIModule. Cuando creamos un proyecto automáticamente se crea el archivo PROYECTO.Build.cs, es en este archivo donde tenemos que indicar los módulos que usamos en el código.

Abre la clase UE4Demo.Build.cs y verás que tiene la siguiente línea:

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore"});

Como puedes notar, es un arreglo de strings con el nombre de los módulos que usa nuestro proyecto, por defecto ya vienen incluidos los módulos básicos. Como vamos a usar clases del modulo AIModule, agrega al final del arreglo otro string con el nombre del modulo. Te quedará de la siguiente forma:

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "AIModule" });

Ahora si podemos incluir en nuestro proyecto cualquier clase del Módulo AI y no nos dará error. Si quieres, para probar exactamente lo que pasa, al final del tutorial prueba eliminar este ítem del arreglo y trata de compilar.

Recordando lo que hicimos en el tutorial pasado

En el tutorial pasado implementamos varios algoritmos mediante visualscripting y los usamos en nodos del Behavior Tree del AIController para definir el comportamiento del enemigo. Básicamente implementamos tres algoritmos:

CheckNearbyEnemy que lo usamos en un Service del Behavior Tree y nos sirve para determinar en cada update si el personaje principal está cerca del NPC haciendo uso del MultiSphereTraceForObjects.

UpdateNextTargetPoint que lo usamos en un Task del Behavior Tree para determinar el siguiente punto de la ruta que patrulla el enemigo

MoveToEnemy que lo usamos también en un Task y se llama cuando el algoritmo CheckNearbyEnemy detecta que estamos cerca del enemigo y haciendo uso del método AI Move To hacemos que el enemigo nos persiga.

Para este acercamiento a C++ en Unreal Engine 4 vamos a re implementar estos tres algoritmos pero totalmente desde C++.

Creando el AIController desde C++

Lo primero es crear nuestra clase C++ que heredará de AIController y que será el controlador del NPC. Crea una nueva clase desde el Editor File/Add Code To Project. Selecciona como clase base AIController y de nombre dale AIEnemyCppController, cuando el Editor te pregunte si quieres abrirla en el IDE le dices que sí. Tendrás la siguiente clase creada:


//AIEnemyCppController.h

#pragma once

#include "AIController.h"
#include "AIEnemyCppController.generated.h"

/** Clase Controladora del NPC del juego */
UCLASS()
class UE4DEMO_API AAIEnemyCppController : public AAIController
{
	GENERATED_UCLASS_BODY()
};

//AIEnemyCppController.cpp

#include "UE4Demo.h"
#include "AIEnemyCppController.h"

AAIEnemyCppController::AAIEnemyCppController(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{

}

Compila el proyecto y ejecútalo.

Creando el AIEnemyCppControllerBlueprint.

Aunque pudiéramos hacer completamente el AIController desde C++. Vamos a crear un Blueprint a partir de esta clase para poder extender el comportamiento del AIController desde VisualScripting, en caso que queramos. Crea un Blueprint que herede de AIEnemyCppController. Yo lo llamé AIEnemyCppControllerBlueprint

Abre el AIEnemyCharacter que creamos en el tutorial pasado selecciona el modo Defaults, sección AI, y en el atributo AI Controller Class selecciona AIEnemyCppControllerBlueprint. Guarda y Compila.

Ya cambiamos el controlador de nuestro enemigo, si corres el juego en este punto verás que el NPC no hará absolutamente nada, como es de esperar. Crea un nuevo Behavior Tree para este estudio, podemos usar el anterior, pero vamos a crear uno nuevo para no modificar el anterior y que te quede de referencia. Ya sabes como crear un Behavior Tree (desde el Content Browser: New/Miscellaneous/Behavior Tree) y ponle de nombre AIEnemyCppBehaviorTree simplemente para distinguirlo del otro.

Abre el AIEnemyCppControllerBlueprint y como mismo hicimos en el tutorial pasado, agrega al Blueprint el nodo Event Begin Play conéctalo al Run Behavior Tree y selecciona en BTAsset este nuevo Behavior Tree que creamos.

imagen_01

Creando el AIEnemyTargetPoint mediante C++

En el tutorial pasado definimos el recorrido del enemigo con puntos clave en el nivel. Estos puntos los creamos mediante un Blueprint que hereda de TargetPoint y le agregamos un atributo Position que nos sirve para indicar el orden del recorrido. Vamos a hacer esto mismo, pero totalmente desde C++, de esta forma también veremos algo nuevo, veremos que podemos agregar al nivel directamente una clase creada en C++, sin tener que crear un Blueprint que herede de ella como hemos hecho hasta ahora.

Agrega una nueva clase al código de nombre AIEnemyTargetPointCpp que herede de ATargetPoint. Modifica la declaración de la clase para que te quede de la siguiente forma:

//AIEnemyTargetPointCpp.h

#pragma once

#include "Engine/TargetPoint.h"
#include "AIEnemyTargetPointCpp.generated.h"

/** TargetPoint con el que definimos los puntos claves del recorrido del AIEnemyCharacter */
UCLASS()
class UE4DEMO_API AAIEnemyTargetPointCpp : public ATargetPoint
{
    GENERATED_UCLASS_BODY()

    /** Representa el orden que tiene este TargetPoint en el recorrido del personaje (siendo 0 el punto inicial) */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Tutorial Category")
    int32 Position;
};


//AIEnemyTargetPointCpp.cpp

#include "UE4Demo.h"
#include "AIEnemyTargetPointCpp.h"


AAIEnemyTargetPointCpp::AAIEnemyTargetPointCpp(const class FPostConstructInitializeProperties& PCIP)
    : Super(PCIP)
{
}

Simplemente agregamos a la clase el atributo Position de tipo entero. Gracias al macro UPROPERTY con el atributo EditAnywhere podemos ver Position desde el Editor y editar su valor. Con BlueprintReadWrite lo definimos para que también lo podamos manipular desde el Blueprint y Category representa un nombre de sección para mostrarlo en el Editor. Verás que el atributo Position sale en el panel de detalles del actor en una sección de nombre Tutorial Category.

Cambiando los TargetPoint que tenemos en el nivel por los AIEnemyTargetPointCpp.

Compila y ejecuta el código. Actualmente en el nivel tenemos los cuatro puntos que definen el recorrido del NPC, vamos a cambiar estos por los AIEnemyTargetPointCpp que acabamos de crear desde C++. Selecciona cada uno y elimínalos del nivel. Ahora, desde el panel Modes que tienes en la esquina superior izquierda tienes la sección All Classes, marca esa sección y localiza la clase AIEnemyTargetPointCpp.

Desde aquí podemos agregar al nivel instancias de esta clase sin tener que crear un blueprint como habíamos hecho hasta el momento. Arrastra cuatro instancias de esta clase al nivel, ve seleccionando cada una y modificando en el panel de Detalles dentro de la sección Tutorial Category el valor del atributo Position a 0,1,2,3 como mismo hicimos en el tutorial pasado.

Editor con los AIEnemyTargetPointCpp agregados al nivel. Fíjate en el panel de Detalles la sección Tutorial Category y el atributo Position

Implementando el algoritmo del UpdateNextTargetPoint desde C++

En el tutorial pasado lo primero que hicimos fue implementar el Task UpdateNextTargetPoint que se encargaba de determinar cual era el siguiente TargetPoint al que tenía que moverse el NPC y setearlo en el Blackboard. Vamos a hacer esto mismo pero totalmente desde programación.

Abre la clase AAIEnemyCppController y en la .h agrega la siguiente declaración

/** Usado desde el Task UpdateNextTarhetPointBTTaskNode del Behavior Tree para actualizar el siguiente punto en la ruta que patrulla */
UFUNCTION(BlueprintCallable, Category="Tutorial Category")
void UpdateNextTargetPoint();

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

/** Usado desde el Task UpdateNextTarhetPointBTTaskNode del Behavior Tree para actualizar el siguiente punto en la ruta que patrulla */
void AAIEnemyCppController::UpdateNextTargetPoint()
{
    //Obtiene la referencia al BlackboardComponent del AIController
    UBlackboardComponent* BlackboardComponent = BrainComponent->GetBlackboardComponent();
    
    //Guarda en TargetPointNumber el valor que tiene el Blackboard en el KEY TargetPointNumber
    //Este numero representa el orden en el que se mueve el enemigo por los TargetPoint del nivel
    int32 TargetPointNumber = BlackboardComponent->GetValueAsInt("TargetPointNumber");
    
    //Como solo tenemos 4 TargetPoint, cuando ya esté en el último, que lo reinicie al primero.
    if(TargetPointNumber >= 4)
    {
        //Pone en 0 el valor del KEY TargetPointNumber del Blackboard
        TargetPointNumber = 0;
        BlackboardComponent->SetValueAsInt("TargetPointNumber", TargetPointNumber);
    }

    //Iteramos por todos los AAIEnemyTargetPointCpp que hay en el nivel
    for (TActorIterator<AAIEnemyTargetPointCpp> It(GetWorld()); It; ++It)
    {
        //Obtenemos el TargetPoint actualmente en el ciclo
        AAIEnemyTargetPointCpp* TargetPoint = *It;
        
        //Si el TargetPointNumber del BlackBoard es igual al valor del atributo Position del AAIEnemyTargetPointCpp
        //Este es el siguiente punto al que tiene que moverse el NPC por lo que setteamos el KEY TargetPointPosition con la posicion de ese Actor
        if(TargetPointNumber == TargetPoint->Position)
        {
            //Setteamos el KEY TargetPointPosition con la posicion de ese TargetPoint en el nivel y detenemos el ciclo con el break;
            BlackboardComponent->SetValueAsVector("TargetPointPosition", TargetPoint->GetActorLocation());
            break;
        }
    }
    
    //Por último, incrementamos el valor de TargetPointNumber del Blackboard
    BlackboardComponent->SetValueAsInt("TargetPointNumber", (TargetPointNumber + 1));
    
}

Ve con detenimiento por los comentarios de cada línea para comprender en detalles lo que se hace y como se usan las clases y métodos que brinda el Framework.

En la declaración del método usamos el macro UFUNCTION con el atributo BlueprintCallable. Esto es para poder llamar a este método desde un Blueprint en caso que nos haga falta. El método tiene de tipo de dato void porque no retorna nada, simplemente hace un procesamiento interno sin devolver ningún valor.

En la implementación si tenemos algunos puntos importantes en los que detenernos. Primero, fíjate que para obtener la referencia al BlackBoard usamos el atributo BrainComponent del AIController (la clase padre de nuestra clase). El BrainComponent tiene un método de nombre GetBlackboardComponent que nos permite obtener una referencia al BlackBoard que está usando este AIController para su base de conocimiento. Mediante este objeto de tipo UBlackboardComponent podemos usar el mismo principio que usamos en el Blueprint para settear u obtener un valor del BlackBoard.

Con SetValueAsTIPO_DE_DATO podemos settear el valor de un KEY determinado. El primer parámetro es el KEY y el segundo es el valor. Con el método GetValueAsTIPO_DE_DATO podemos obtener el valor almacenado en un KEY del blackboard, como parámetro espera el nombre del Key y retorna el valor almacenado en ese KEY.

Lo primero que tenemos en cuenta es comparar si el TargetPointNumber es superior al máximo que tenemos en el nivel, si es así, lo ponemos de nuevo en cero, para garantizar que el recorrido del personaje sea indefinido y siguiendo siempre el mismo orden.

A continuación usamos un Iterador muy útil que nos brinda el Framework. Desde C++ podemos usar TActorIterator para iterar por todos los Actores del nivel de la clase que indiquemos. En este caso nuestro objetivo es iterar por todos los AAIEnemyTargetPointCpp

Dentro del loop lo que hacemos es obtener la referencia del objeto actual en el iterador , comparamos el valor del atributo Position con el valor que tenemos en la variable TargetPointNumber que tiene el valor que tenemos en el KEY del BlackBoard, si son iguales, seteamos el KEY TargetPointPosition del blackboard con la posición de ese TargetPoint en el nivel usando el método GetActorLocation() que nos retorna el vector con la posición del actor.

Por ultimo, incrementamos el valor del KEY TargetPointNumber en 1 para que la siguiente vez que se llame este método se obtenga la posición del siguiente punto en el recorrido.

Fíjate que en esta clase estamos haciendo referencia a AAIEnemyTargetPointCpp por lo que tenemos que incluir al inicio, el fichero donde se define esta clase.

Creando el UpdateNextTargetPointTask del Behavior Tree desde C++

Ya tenemos listo el método para determinar el siguiente punto del recorrido, pero como sabes, este método lo tenemos que usar en un Task para que forme parte de un nodo del Behavior Tree. Vamos a crear este Task completamente desde C++

Compila y ejecuta el proyecto. Desde el editor agrega una nueva clase al proyecto que herede de UBTTaskNode y ponle de nombre UpdateNextTargetPointBTTaskNode. Abrela desde el IDE y modifica la .h para que te quede de la siguiente forma:

#pragma once

#include "BehaviorTree/BTTaskNode.h"
#include "UpdateNextTargetPointBTTaskNode.generated.h"

/** Task del Behavior Tree del AIEnemyCppController que ejecuta el método para seleccionar el siguiente punto del recorrido */
UCLASS()
class UE4DEMO_API UUpdateNextTargetPointBTTaskNode : public UBTTaskNode
{
    GENERATED_UCLASS_BODY()

    /* Se llama al iniciar este Task, tiene que retornar Succeeded, Failed o InProgress */
    virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent* OwnerComp, uint8* NodeMemory) override;

    /** Permite definir una descripción para este Task. Este texto se ve en el Nodo al agregarlo al Behavior Tree */
    virtual FString GetStaticDescription() const override;
};

Ahora pasa a la .cpp y agrega la implementación de estos métodos:

#include "UE4Demo.h"
#include "AIEnemyCppController.h"
#include "UpdateNextTargetPointBTTaskNode.h"


/** Constructor de la clase */
UUpdateNextTargetPointBTTaskNode::UUpdateNextTargetPointBTTaskNode(const class FPostConstructInitializeProperties& PCIP)
    : Super(PCIP)
{
    //Definimos el nombre que tendrá este Nodo en el Behavior Tree
    NodeName = "UpdateNextTargetPoint";
}

/* Se llama al iniciar este Task, tiene que retornar Succeeded, Failed o InProgress */
EBTNodeResult::Type UUpdateNextTargetPointBTTaskNode::ExecuteTask(UBehaviorTreeComponent* OwnerComp, uint8* NodeMemory)
{
    //Obtenemos la referencia al AIEnemyController
    AAIEnemyCppController* AIEnemyController = Cast<AAIEnemyCppController>(OwnerComp->GetOwner());
    
    //Llamamos al método UpdateNextTargetPoint que tiene la lógica para seleccionar el siguiente TargetPoint
    AIEnemyController->UpdateNextTargetPoint();
    
    //Finalmente retornamos Succeeded
    return EBTNodeResult::Succeeded;
}

/** Permite definir una descripción para este Task. Este texto se ve en el Nodo al agregarlo al Behavior Tree */
FString UUpdateNextTargetPointBTTaskNode::GetStaticDescription() const
{
    return TEXT("Actualiza el siguiente punto en el recorrido");
}

Es muy fácil crear un Task desde C++. Basta con heredar de UBTTaskNode. Para este caso solo necesitamos sobrescribir dos métodos. El método ExecuteTask que se llama cuando el Behavior Tree ejecuta este nodo. Simplemente obtenemos la referencia al AIEnemyController desde el parámetro OwnerComp con el método GetOwner, llamamos al método del AIEnemyController UpdateNextTargetPoint que acabamos de crear y que se encarga de toda la lógica necesaria para determinar y configurar el siguiente punto del recorrido. Por último retornamos Succeeded, como mismo hicimos en el tutorial pasado, para que el nodo padre, el Sequence, continúe con la ejecución del siguiente nodo.

El método GetStaticDescription nos sirve para retornar un string con una descripción para este Task. Esta descripción se ve en el Behavior Tree y resulta muy útil para el caso en el que sea otra persona la encargada de diseñar el Behavior Tree desde el Editor.

Compila y ejecuta el proyecto. Desde el Editor abre el Behavior Tree, verás que entre los Tasks que puedes agregar tendrás este que acabamos de crear completamente desde C++. Modifica el Behavior Tree para que te quede de la siguiente forma:

Behavior Tree con la secuencia y los Tasks para el recorrido por la zona del NPC. Fíjate que el título que muestra el nodo UpdateNextTargetPoint es el texto con el que inicializamos la variable NodeName y la descripción es el string que retornamos en el método GetStaticDescription.

Compila, guarda y ejecuta el juego. Como vez, tenemos el personaje moviéndose por los cuatro puntos exactamente a como lo logramos anteriormente, la diferencia es que ahora todo lo hemos hecho desde C++.

Implementado el método CheckNearbyEnemy

Vamos ahora a implementar el método CheckNearbyEnemy en el AIController, que es el método que hace uso del MultiSphereTraceForObjects para detectar si nos hemos acercado al enemigo. Agrega la declaración del método en la .h del AAIEnemyCppController

 /** 
 * Chequea si el personaje está cerca y setea una referencia a él en el BlackBoard 
 * Es usado en Service CheckNearbyEnemyBTService del Behavior Tree para estar constantemente vigilando si se acerca alguien a la zona de patrulla.
 */
UFUNCTION(BlueprintCallable, Category="Tutorial Category")
void CheckNearbyEnemy();

Ahora pasa al .cpp y agrega la siguiente implementación:

 /**
 * Chequea si el personaje está cerca y setea una referencia a él en el BlackBoard
 * Es usado en Service CheckNearbyEnemyBTService del Behavior Tree para estar constantemente vigilando si se acerca alguien a la zona de patrulla.
 */
void AAIEnemyCppController::CheckNearbyEnemy()
{
    //Obtenemos la referecia al Pawn del NPC
    APawn *Pawn = GetPawn();
    
    //Guardamos en MultiSphereStart la posición del NPC
    FVector MultiSphereStart = Pawn->GetActorLocation();

    //Creamos un nuevo vector a partir de la posición del NPC pero con un incremente en la Z de 15 unidades.
    //Estos dos valores serán los puntos de inicio y fin del MultiSphereTraceForObjects
    FVector MultiSphereEnd = MultiSphereStart + FVector(0, 0, 15.0f);
    
    //Creamos el arreglo que pasaremos como el parámetro ObjectTypes del MultiSphereTraceForObjects y define los tipos de objetos a tener en cuenta
    TArray<TEnumAsByte<EObjectTypeQuery>> ObjectTypes;
    ObjectTypes.Add(UEngineTypes::ConvertToObjectType(ECC_Pawn));

    //Creamos el arreglo de los actores a ignorar por el método, solamente con el Pawn del NPC que es el único que no puede estar en el resultado
    TArray<AActor*> ActorsToIgnore;
    ActorsToIgnore.Add(Pawn);
    
    //OutHits la usaremos como parámetro de salida. Como el método SphereTraceMultiForObjects recibe la referencia de este parámetro
    //al terminar la ejecución del método en este arreglo se encuentran el arreglo de FHitResult lleno con los Hits que detecte el método.
    TArray<FHitResult> OutHits;
    
    bool Result = UKismetSystemLibrary::SphereTraceMultiForObjects(GetWorld(),                      //Referencia del Mundo
                                                                   MultiSphereStart,                //Punto de Inicio de la recta que define la esfera
                                                                   MultiSphereEnd,                  //Punto de fin de la recta que define la esfera
                                                                   700,                             //Radio de la esfera
                                                                   ObjectTypes,                     //Tipos de objetos a tener en cuenta en el proceso
                                                                   false,                           //No queremos que se use el modo complejo
                                                                   ActorsToIgnore,                  //Los actores que se van a ignorar aunque esten dentro de la esfera
                                                                   EDrawDebugTrace::ForDuration,    //El tipo de Debug
                                                                   OutHits,                         //Parámetro por referencia donde se guardarán los resultados
                                                                   true);                           //si se ignora el propio objeto
    
    //Inicialmente seteamos en NULL el KEY del BlackBoard TargetActorToFollow para en caso de que en el Trace no se tenga resultados, se quede en NULL
    UBlackboardComponent* BlackboardComponent = BrainComponent->GetBlackboardComponent();
    BlackboardComponent->SetValueAsObject("TargetActorToFollow", NULL);

    //Si hay resultados en el Trace
    if(Result == true)
    {
        //Recorremos todos los objetos dentro del OutHits
        for(int32 i = 0; i < OutHits.Num(); i++)
        {
            //Obtenemos el FHitResult actualmente en el ciclo
            FHitResult Hit = OutHits[i];

            //Obtenemos la referencia el Character
            ACharacter* Character = UGameplayStatics::GetPlayerCharacter(GetWorld(), 0);

            //Si el Actor detectado en el Trace es igual al Character
            //setamos el KEY TargetActorToFollow del Blackboard con la referencia al Character
            if(Hit.GetActor() == Character)
            {
                BlackboardComponent->SetValueAsObject("TargetActorToFollow", Character);
            }
        }
    }
}

La lógica que sigue esté método tampoco es necesaria explicarla porque ya lo hicimos en el tutorial pasado cuando lo implementamos mediante VisualScripting, pero si vamos a pasar por el código para ver los detalles del Framework y las clases y método que nos permiten hacer esto mismo desde C++.

Primero preparamos las variables que vamos a pasar como parámetro al método del Trace. El único que creo que vale la pena comentar es la variable ObjectTypes que es un arreglo de TEnumAsByte Este es el tipo de dato del parámetro ObjectTypes que recibe el método del Trace. Fíjate que solamente le agregamos un elemento, el indicador de Pawn. UEngineTypes::ConvertToObjectType nos permite convertir el ECC_Pawn (valor de enum correspondiente al Pawn) a un EObjectTypeQuery. La siguiente variable que declaramos es el arreglo de Actors que va a ignorar el Trace, recuerda que aquí solamente incluimos al Pawn del enemigo, que lo tenemos en la variable Pawn y obtenemos la referencia a este mediante el método GetPawn.

Por último declaramos el arreglo OutHits que lo usaremos como parámetro de salida. El método SphereTraceMultiForObjects recibe este parámetro por referencia y al terminar la ejecución, el arreglo de FHitResults queda poblado con los Hits que detecte el método. Los parámetros por referencia son la variante que tenemos en C++ para tener más de un valor de retorno en una función.

Y ahora el punto clave de este método, la llamada al UKismetSystemLibrary::SphereTraceMultiForObjects. SphereTraceMultiForObjects es un método estático dentro de la clase UKismetSystemLibrary y ya sabemos lo que hace :)

Después de su ejecución seteamos en NULL el KEY TargetActorToFollow del Blackboard para que en caso de que no se encuentre el Pawn del personaje dentro del Trace, que se quede este KEY en NULL. Recuerda que el Decorator que tenemos en el Behavior Tree para determinar si se ejecuta o no el siguiente Task se basa en comprobar si el KEY TargetActorToFollow tiene valor.

Creando el CheckNearbyEnemyService desde C++

Como mismo hicimos para el Task anterior, tenemos que crear el Service del Behavior Tree donde se va a usar este método que acabamos de crear. Compila y ejecuta el proyecto, desde el editor crea una nueva clase que herede de UBTService y ponle de nombre UCheckNearbyEnemyBTService. Abrela en el IDE y modifica el .h para que te quede de la siguiente forma:

#pragma once

#include "BehaviorTree/BTService.h"
#include "CheckNearbyEnemyBTService.generated.h"

/** Service del Behavior Tree del AIEnemyCppController que chequea constantemente si el personaje está cerca de nosotros */
UCLASS()
class UE4DEMO_API UCheckNearbyEnemyBTService : public UBTService
{
    GENERATED_UCLASS_BODY()

    /** Se llama en cada update del Service */
    virtual void TickNode(UBehaviorTreeComponent* OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;   
};

Ahora pasa al .cpp y modifícalo para que te quede de la siguiente forma:

#include "UE4Demo.h"
#include "AIEnemyCppController.h"
#include "CheckNearbyEnemyBTService.h"

/** Constructor de la clase */
UCheckNearbyEnemyBTService::UCheckNearbyEnemyBTService(const class FPostConstructInitializeProperties& PCIP)
    : Super(PCIP)
{
    //Nombre del nodo en el Behavior Tree
    NodeName = "CheckNearbyEnemy";
    
    //Intervalo de Update
    Interval = 0.5f;

    //Aleatorio de desviación para el update
    RandomDeviation = 0.1f;
}

/** Se llama en cada update del Service */
void UCheckNearbyEnemyBTService::TickNode(UBehaviorTreeComponent* OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
    //Llamamos a la implementación de la clase base primero
    Super::TickNode(OwnerComp, NodeMemory, DeltaSeconds);
    
    //Obtenemos la referencia del AIEnemyController
    AAIEnemyCppController* AIEnemyController = Cast<AAIEnemyCppController>(OwnerComp->GetOwner());
    
    //Llamamos al método CheckNearbyEnemy del AIEnemyController que tiene toda la lógica para determinar si el enemigo está cerca o no
    //y configurar el KEY correspondiente del Blackboard
    AIEnemyController->CheckNearbyEnemy();
}

No hay mucho que explicar aquí verdad ? En el constructor inicializamos los valores del Services que ya conoces del tutorial anterior, y en el método TickNode obtenemos la referencia del AIEnemyController y llamamos al método CheckNearbyEnemy que se encarga de todo :)

Compila y ejecuta el proyecto. Abre el Behavior Tree y modifícalo para que te quede de la siguiente forma:

Behavior Tree con el Service que chequea si estamos cerca del enemigo

Guarda, Compila y ejecuta el juego.

Captura del juego corriendo en el Editor después de configurar el Behavior Tree con el Service para determinar si nos acercamos al enemigo.

Muy bien, ya solo nos queda el último paso para terminar el Behavior Tree que implementamos en el tutorial pasado pero en esta ocasión completamente desde C++.

Implementado el método MoveToEnemy

Vamos a implementar el último método del AIEnemyController, el encargado de llamar al MoveToActor para comenzar la persecución :). Abre AIEnemyCppController.h y agrega la siguiente declaración

/**
 *  Hace que el AIEnemyCharacter persiga al actor setteado en el KEY TargetActorToFollow del Blackboard
 *  Es usado en un Task del Behavior Tree para perseguir al personaje principal
 *  @return El resultado del método MoveToActor (Failed, AlreadyAtGoal o RequestSuccessful)
 */
UFUNCTION(BlueprintCallable, Category="Tutorial Category")
EPathFollowingRequestResult::Type MoveToEnemy();

Ahora pasa a la .cpp y agrega la siguiente implementación:

/**
 *  Hace que el AIEnemyCharacter persiga al actor setteado en el KEY TargetActorToFollow del Blackboard
 *  Es usado en un Task del Behavior Tree para perseguir al personaje principal
 *  @return El resultado del método MoveToActor (Failed, AlreadyAtGoal o RequestSuccessful)
 */
EPathFollowingRequestResult::Type AAIEnemyCppController::MoveToEnemy()
{
    //Obtenemos la referencia al BlackBoard
    UBlackboardComponent* BlackboardComponent = BrainComponent->GetBlackboardComponent();
    
    //Obtenemos la referencia al Actor guardado en el KEY TargetActorToFollow del BlackBoard
    AActor* HeroCharacterActor = Cast<AActor>(BlackboardComponent->GetValueAsObject("TargetActorToFollow"));
    
    //Iniciamos el proceso de perseguir al personaje
    EPathFollowingRequestResult::Type MoveToActorResult = MoveToActor(HeroCharacterActor);

    return MoveToActorResult;
}

Fíjate que usamos el método MoveToActor de AIController para comenzar la persecución, lo demás no creo que necesite explicación. Aquí usamos el método MoveToActor pasándole solo el parámetro del Actor que tiene que seguir, pero date una vuelta por la declaración del método para que veas los otros parámetros que se le puede pasar.

Creando el MoveToEnemyBTTaskNode desde C++

Por último nos queda crear el Task que usará este método. Pero, vamos a detenernos aquí para comentar un poco de teoría que se me pasó en el tutorial pasado.

En Unreal Engine 4 los Tasks pueden ser de dos tipos. Los que se ejecutan y tienen un resultado al instante, como el UpdateNextTargetPoint, y los que demoran un tiempo en terminar su ejecución. En este caso se encuentra este Task que vamos a crear, ya que la acción de este Task va a terminar cuando el enemigo llegue al personaje principal, y esto puede demorar un tiempo.

Este tipo de Task generalmente en su ejecución retornan InProgress para que el Behavior Tree sepa que demorará un poco en terminar, y se usa el método TickTask para estar comprobando la condición que se tiene que cumplir para que termine la ejecución del Task y en ese momento se termina la ejecución con una llama al método FinishLatentTask.

Vamos a verlo en la practica con la creación de este Task. Agrega una clase de nombre MoveToEnemyBTTaskNode que herede de UBTTaskNode modifica su .h para que te quede de la siguiente forma:

#pragma once

#include "BehaviorTree/BTTaskNode.h"
#include "MoveToEnemyBTTaskNode.generated.h"

/**
 * 
 */
UCLASS()
class UE4DEMO_API UMoveToEnemyBTTaskNode : public UBTTaskNode
{
    GENERATED_UCLASS_BODY()

    /* Se llama al iniciar este Task, tiene que retornar Succeeded, Failed o InProgress */
    virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent* OwnerComp, uint8* NodeMemory) override;
    
    /* Se llama constantemente. Es usado generalmente en Tasks que en el ExecuteTask retornan InProgress */
    virtual void TickTask(class UBehaviorTreeComponent* OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
    
    /** @return Una descripción para este Task. Este texto se ve en el Nodo al agregarlo al Behavior Tree */
    virtual FString GetStaticDescription() const override;
    
};

Ahora pasa a la .cpp y modifícala para que te quede de la siguiente forma:

#include "UE4Demo.h"
#include "AIEnemyCppController.h"
#include "MoveToEnemyBTTaskNode.h"

/** Constructor de la clase */
UMoveToEnemyBTTaskNode::UMoveToEnemyBTTaskNode(const class FPostConstructInitializeProperties& PCIP)
    : Super(PCIP)
{
    //Definimos el nombre que tendrá este Nodo en el Behavior Tree
    NodeName = "MoveToEnemy";
    
    //Activamos para que se llame el TickTask de este task
    bNotifyTick = true;
}

/* Se llama al iniciar este Task, tiene que retornar Succeeded, Failed o InProgress */
EBTNodeResult::Type UMoveToEnemyBTTaskNode::ExecuteTask(UBehaviorTreeComponent* OwnerComp, uint8* NodeMemory)
{
    //Obtenemos la referencia al AIEnemyController
    AAIEnemyCppController* AIEnemyController = Cast<AAIEnemyCppController>(OwnerComp->GetOwner());
    
    //Preparamos el resultado del Task. En este caso como es un Task que su ejecución no terminará al instante, tiene que retornar InProgress
    EBTNodeResult::Type NodeResult = EBTNodeResult::InProgress;
    
    //Llamamos al método MoveToEnemy del Controller y guardamos en MoveToActorResult el resultado
    EPathFollowingRequestResult::Type MoveToActorResult = AIEnemyController->MoveToEnemy();

    //Este caso sería si se ejecuta este Task estando delante del personaje. En este caso si retorna Succeeded
    if (MoveToActorResult == EPathFollowingRequestResult::AlreadyAtGoal)
    {
        NodeResult = EBTNodeResult::Succeeded;
    }
    
    return NodeResult;
}

/* Se llama constantemente. Es usado generalmente en Tasks que en el ExecuteTask retornan InProgress */
void UMoveToEnemyBTTaskNode::TickTask(class UBehaviorTreeComponent* OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
    //Obtenemos la referencia al AIEnemyController
    AAIEnemyCppController* AIEnemyController = Cast<AAIEnemyCppController>(OwnerComp->GetOwner());
    
    //Llamamos al método MoveToEnemy del Controller y guardamos en MoveToActorResult el resultado
    EPathFollowingRequestResult::Type MoveToActorResult = AIEnemyController->MoveToEnemy();
    
    //Si ya se llega al objetivo se termina la ejecución el Task con FinishLatentTask y un resultado
    if (MoveToActorResult == EPathFollowingRequestResult::AlreadyAtGoal)
    {
        FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);
    }
}

/** @return Una descripción para este Task. Este texto se ve en el Nodo al agregarlo al Behavior Tree */
FString UMoveToEnemyBTTaskNode::GetStaticDescription() const
{
    return TEXT("Persigue al personaje principal");
}

Muy bien, la .h a estas alturas no creo que tenga nada que explicar. Ahora, en la cpp, primero en el constructor tenemos que poner en true el atributo bNotifyTick para que el Engine sepa que tiene que ejecutar el método Tick de esta clase.

En el ExecuteTask simplemente llamamos al método MoveToEnemy, este método nos retorna tres posibles valores Failed, AlreadyAtGoal o RequestSuccessful. Normalmente el ExecuteTask tendrá que terminar en InProgress a menos que el MoveToActor retorne AlreadyAtGoal que sería dado si se llama el método estando ya pegados al personaje. De lo contrario retornamos InProgress para dejarle saber el BT que este Task tomará un tiempo.

Entonces, en el TickTask es donde tenemos que estar comprobando si llega al objetivo, si esto pasa se fuerza a terminar el Task con el método FinishLatentTask.

Compila y ejecuta el proyecto. Modifica el Behavior Tree para agregar este último Task y el Decorator correspondiente como mismo lo hicimos en el tutorial pasado. Te quedará de la siguiente forma:

Behavior Tree completo

Guarda, compila y ejecuta el juego 😉

Captura del juego corriendo en el Editor después de terminar la configuración del Behavior Tree

Conclusión

A pesar de no implementar ninguna funcionalidad nueva en este tutorial, creo que sirvió para acercarnos un poco más al mundo de C++ en el Unreal Engine 4. Puedes bajarte de aquí las clases que hemos creado en este tutorial para una referencia directa.

Con esto terminamos por hoy, en el próximo tutorial le “enseñaremos“ a nuestro personaje a dar puñetazos para que se pueda defender cuando el enemigo lo atrape :) … mientras, me encantaría escuchar tus comentarios.

Tutorial: ¿Cómo hacer un juego side-scroller 3D con UE4 ?

Introducción

En el tutorial pasado hicimos una introducción general al Unreal Engine 4. Vimos varios aspectos importantes como la jerarquía de clases que sigue el framework, el visual scripting, la configuración de las animaciones para el personaje, definimos temporalmente una cámara fija a nuestro juego, entre otras cosas. Si no lo has visto, debieras darle un vistazo antes de seguir con este.

Hoy vamos a crear la base del estilo de nuestro juego: un side-scroller 3D. Actualmente estoy trabajando con mi equipo (www.spissa.com) en un Runner automático, side-scroller 2D para iOS y Android. Puedes darte una vuelta por nuestro sitio en Facebook para que estés al tanto de la salida. Si te gusta este estilo de juego, te aseguro que te encantara el nuestro 😉

Como te decía, en este tutorial vamos a configurar la cámara del juego para lograr una vista side-scroller. Vamos a agregar unas monedas al escenario y usar un mecanismo simple de colisiones para que el personaje las pueda recolectar. Vamos a “enseñarle“ a nuestro personaje a correr y saltar :). Vamos a ver varios macros de variables y funciones de clase para la integración entre el código C++ y el Editor . . . y muchas cosas más. Listo ?! .. pues “manos a la obra“ !!

Modificando el nivel desde el editor

Vamos a comenzar modificando un poco el nivel actual desde el editor para que se acople más al estilo de juego que queremos implementar. Como ya decía, vamos a trabajar en un juego 3D side-scroller. La base de este estilo de juego es tener la cámara paralela al personaje protagónico siguiéndolo contantemente en la misma posición. El personaje, por su parte, se mueve solamente en dos direcciones: hacia arriba/abajo cuando salta y hacia izquierda/derecha cuando se desplaza.

Abre el editor con el proyecto UE4Demo que dejamos del tutorial pasado y comienza eliminando los objetos visibles que no vamos a usar (las sillas, la mesa, la estatua). Solo quédate con un objeto floor (piso). Después, modifica el objeto floor usando las herramienta de transformación y escalado que tienes en la esquina superior derecha del viewport. Después crea copias del objeto, modifícalas indistintamente y repártelas por el nivel. Asegúrate de dejar el Actor Play Start sobre una de las plataformas, para evitar que el personaje cuando inicie el juego se caiga al abismo. En mi caso quedó así ( tu puedes usar mucho más tu imaginación para lograr algo mejor :) ):

Nivel modificado en el Editor para que se acople más al estilo de nuestro juego

Nivel modificado en el Editor para que se acople más al estilo de nuestro juego

 

Como notarás, al lanzar el juego se muestra un cartel rojo que dice: LIGHTING NEEDS TO BE REBUILT. Esto pasa porque modificamos la geometría en el nivel y el motor necesita recompilar las luces para que la iluminación se adapte a la nueva geometría. Puedes recompilar las luces desde el Toolbar/Build/Build Lighting Only. Vuelve a correr, verás que todo volvió a la normalidad.

Bien, ya tenemos un nivel, muy básico, pero suficiente para implementar y probar todo lo que haremos hoy.

Configurando la cámara para un juego side-scroller.

En este punto es importante aclarar que el UE4 ya trae por defecto una plantilla para comenzar un juego de este estilo. De hecho usaremos prácticamente lo mismo que usa esta plantilla, pero la idea es hacerlo desde cero en este tutorial para entender bien el porqué de las cosas.

Lo primero será cambiar la cámara del juego. En el tutorial pasado configuramos una cámara fija bastante simple, pero que nos sirvió para introducirnos tanto en el visual scripting con el Blueprint Editor, como en la programación en C++. Ya no usaremos más esta cámara. Elimina la actual implementación que tengas de la cámara, si te quedaste con la solución por código, comenta el código dentro del Begin Play y si te quedaste con la solución en el Blueprint, elimina todos los nodos o elimina la conexión que sale del Nodo BeginPlay con esto último se rompe el algoritmo porque no continua la ejecución al lanzarse el BeginPlay pero se mantienen el resto de las conexiones y los nodos en el Editor, por si los quieres para una referencia.

Vamos ahora a configurar un nuevo estilo de cámara. Esta vez lo haremos desde C++ en nuestra clase Character. Siempre ten en cuenta que esto mismo lo puedes hacer también desde el Editor, recuerda que tenemos una clase Blueprint que hereda de nuestra clase HeroCharacter.h. Una buena práctica sería que te aventuraras una vez que veas lo que haremos aquí, ha hacerlo por tu parte desde el Editor modificando el HeroCharacterBlueprint.

Abre la clase HeroCharacter.h y y después del macro GENERATED_UCLASS_BODY() agrega las siguientes declaraciones:


/** Brazo para apoyar fijar la cámara al Character al estilo side-scroller */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
TSubobjectPtr<USpringArmComponent> SpringArm;
    
/** Cámara del juego, es adjuntada al socket del brazo para lograr el estilo de cámara de un side-scroller */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
TSubobjectPtr<UCameraComponent> SideViewCamera;

Lo que hicimos fue agregar dos variables de clase a nuestro HeroCharacter, pero lo que seguro más te llama la atención es el macro UPROPERTY que incluimos en cada variable. Este macro nos sirve para definir si queremos reflejar o no estos atributos en el Editor y que opciones tendremos. Como vimos, una de las cosas geniales que tiene Unreal Engine 4 es la perfecta integración entre el código y el editor y este macro es uno de los que nos ayuda a esto. A este macro le podemos pasar parámetros:

VisibleAnywhere: Indica que esta propiedad será visible en el panel de propiedades en el Editor.

BlueprintReadOnly: Esta propiedad podrá ser leída desde un VisualScript en el Blueprint, pero no modificada.

Category: Permite especificar un nombre de categoría bajo la que se mostrará esta propiedad en el Editor

Puedes ver una referencia detallada de todos los parámetros que podemos indicar para UPROPERTY en la clase Runtime/CoreUObject/Public/UObject/ObjectBase.h dentro del namespace UP. Para llegar rápido puedes dar clic sobre cualquiera de las categorías con la tecla cmd presionada.

Después que implementemos el constructor donde inicialicemos estas variables, vamos a ver en el editor el resultado que tiene definirlas con este macro.

Estas dos variables de clase que creamos para HeroCharacter son de un nuevo tipo de datos que no hemos usado antes. La primera variable que creamos la nombramos SpringArm y es de tipo USpringArmComponent, pero fíjate que usamos para definirle el tipo, la clase parametrisada TSubobjectPtr. Con TSubobjectPtr podemos usar el método CreateDefaultSubobject del objeto PCIP que recibimos en el constructor para crear una instancia de cualquier tipo de dato. Lo haremos en un segundo.
USpringArmComponent nos permite fijar un componente a su padre a una distancia fija. Usaremos este componente para fijar la cámara del juego a una distancia fija del personaje.

Además, creamos la variable de clase SideViewCamera de tipo UCameraComponent. El nombre de la clase es bastante descriptivo, esta será la cámara de nuestro juego :)

Hecho esto vamos a inicializar estas variables en el constructor de la clase. Abre el archivo HeroCharacter.cpp y agrega antes de terminar la implementación del constructor el siguiente bloque:


//Inicializando la instancia del USpringArmComponent
SpringArm = PCIP.CreateDefaultSubobject<USpringArmComponent>(this, TEXT("CameraBoom"));
    
//Agregando el springArm al RootComponent del Character (la capsula de colisión)
SpringArm->AttachTo(RootComponent);
    
//bAbsoluteRotation nos permite definir si este apoyo para la cámara rotará junto con el player.
//En este caso no queremos que rote junto al character
SpringArm->bAbsoluteRotation = true;
    
//La distancia entre el brazo y su objetivo. Este valor es el que define la distancia de la cámara.
//Prueba con distintos valores para que veas el resultado.
SpringArm->TargetArmLength = 500.f;
    
//Offset que tendrá el Socket.
//Un Socket es un punto de anclaje para otros componentes.
//Por ejemplo, para el caso de un personaje podemos definir que tenga un socket en la zona de la mano
//de esta forma le podemos agregar otro componente (como un arma, por ejemplo) en la mano
//En nuestro SpringArm, en este socket es donde se agregará la cámara
SpringArm->SocketOffset = FVector(0.f,0.f,75.f);
    
//La rotación relativa que tendrá este brazo con respecto al padre.
//En este caso queremos que este rotada en el eje Y 180 grados para que quede paralela al character a su mismo nivel.
//De esta forma logramos el clásico estilo de cámara en los side-scrollers
SpringArm->RelativeRotation = FRotator(0.f,180.f,0.f);
    
// Creando la intancia del tipo UCameraComponent
SideViewCamera = PCIP.CreateDefaultSubobject<UCameraComponent>(this, TEXT("SideViewCamera"));
    
//El método AttachTo nos permite agregar un objeto a otro objeto en un socket determinado. Recibe dos parámetros.
//el primero, el objeto al que vamos a anclarnos, en este  caso el springArm y el nombre del socket donde lo vamos a anclar
//SocketName de USpringArmComponent retorna el nombre del Socket de este componente.
SideViewCamera->AttachTo(SpringArm, USpringArmComponent::SocketName);

Préstale atención a los comentarios de cada línea para que puedas entender cada una lo que hace. En general creamos y configuramos el objeto USpringArmComponent y lo agregamos al Character. Después, creamos y configuramos el objeto UCameraComponent (la cámara del juego) y la agregamos al USpringArmComponent para fijarla a una distancia del Character. Notar que las instancias de los objetos las creamos a partir del objeto que recibimos en el constructor, con PCIP.CreateDefaultSubobject.

Listo, compila y ejecuta el juego, ahora tendrás la vista del juego al estilo side-scroller :). Puedes probar moverte por el nivel que creaste para que veas como la cámara te sigue en todo momento a la misma distancia.

Nueva vista del juego con la cámara configurada al estilo de un side-scroller

Nueva vista del juego con la cámara configurada al estilo de un side-scroller

 

No cierres el editor, vamos a ver ahora a HeroCharacterBlueprint. En el tutorial pasado este blueprint lo creamos dentro de la carpeta Character. Ábrela y activa el modo Components (esquina superior derecha). Fíjate que ahora en el panel de Componentes del Character tienes un nuevo objeto SpringArm y este tiene como hijo a SideViewCamera. Puedes revisar para que veas que tiene las propiedades que definimos desde programación. Desde aquí también puedes variar todas las propiedades por defecto. Juega un poco con ellas para que veas todo lo que puedes lograr.

Para que veas en la practica el efecto de los atributos que le pasamos al UPROPERTY: Regresa al código y elimina el atributo VisibleAnywhere que le pusimos al macro UPROPERTY y compila de nuevo. Cuando abras el HeroCharacterBlueprint en el editor a pesar de poder ver los componentes, al seleccionarlo no se muestran las propiedades en el panel de detalles.

Modificando los controles del juego para un side-scroller

Ya nuestro juego tiene el estilo de cámara que queremos, pero te habrás dado cuenta de que en este estilo de juego no sirve mucho el control actual, ya que el personaje puede moverse tanto en el eje X como en el Y, cosa que es muy poco común en los side-scroller. Por tanto, vamos a modificar un poco los controles para ajustarlos más a este estilo de juego. Además agregaremos dos nuevos controles y acciones nuevas para nuestro personaje: correr y saltar.

Supongo que ya tengas ideas de como hacer esto si seguiste el tutorial anterior. Entra desde el Editor en Edit/ProjectSettings/Input. Deja solamente MoveRigth. Ahora vamos a agregar otro tipo de input, ActionBinding. Estas entradas, a diferencia de los AxisBinding, son usadas para ejecutar acciones determinadas, saltar por ejemplo o abrir una puerta. Crea una nueva entrada de este tipo, dale de nombre Jump y selecciona la barra espaciadora. Además si te fijas puedes definirle que para que se ejecute tenga que estar presionada otra tecla simultáneamente. En este caso no marques ninguna, solamente con tocar la barra espaciadora el personaje saltará.

Nueva configuración de los controles del juego. Ahora con un control para saltar

Nueva configuración de los controles del juego. Ahora con un control para saltar

 

Ahora vamos al código. Primero, ya no necesitamos el método MoveForward de HeroCharacter, elimina la declaración de este método en el .h y la implementación del .cpp. Además, dentro del método SetupPlayerInputComponent elimina el BindAxis del MoveForward. Por último, tenemos que modificar el código del método MoveRight para cambiar su implementación, ya que actualmente lo que hace el personaje cuando tocamos las teclas A o D es rotar su posición. En este caso lo que queremos es que se mueve hacia delante y atrás respectivamente. Modifica el método MoveForward para que quede así:


/**
 *  Se llama cuando se detecta la entrada de tipo MoveForward (Cuando el usuario toca las teclas A o D).
 *  @param Value Value es igual a 1 cuando se detecta D y -1 cuando se detecta A
 */
void AHeroCharacter::MoveRight(float Value)
{
	if ( (Controller != NULL) && (Value != 0.0f) )
	{
        // Agrega un vector de movimiento hacia la derecha o la izquierda segun el valor de Value
        AddMovementInput(FVector(0.f,-1.f,0.f), Value);
	}
}

Como puedes notar, el método quedó mucho mas simple que la anterior implementación y ya debes entender sin problema que hace. Simplemente aplicamos un vector de movimiento que afecta solo un eje con el valor de Value. Por lo que cuando el usuario toque la tecla D el personaje se moverá hacia delante (hacia la derecha) y cuando toque la tecla A se moverá hacia la izquierda.

Ahora tenemos que implementar el método que se llamará cuando el usuario toque la barra espaciadora para hacer saltar a nuestro héroe. Muy simple, agrega la siguiente línea al método SetupPlayerInputComponent:

InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);

Fíjate en una cosa, el método que se llamará es ACharacter::Jump. O sea, Jump es un método implementado en la clase base de nuestra clase, no en nuestra propia clase HeroCharacter. Este método ya tiene implementado todo lo necesario para hacer saltar al personaje. No es poca la ayuda que nos da el Framework ehh ?? :)

Listo, compila y ejecuta el juego y toca las teclas A y D para que veas como el personaje se mueve correctamente por el escenario, y aunque toques las teclas W o S no pasará nada. Ahora toca la barra espaciadora, eehhh !!! nuestro personaje ya sabe saltar también :) . . . pero algo está mal aquí. Sí, el personaje salta, pero a pesar de estar saltando sigue reproduciendo la animación de caminar. Pues claro, recuerda que solo le tenemos dos animaciones, y no hemos hecho nada para el caso en el que está saltando. Vamos a solucionar esto.

Configurando las animaciones para el salto del personaje

Del tutorial pasado tenemos los recursos que supuestamente nos entregó nuestro equipo de diseño para el personaje principal. Entre ellos tenemos 3 FBX de animaciones Jump_End.FBX, Jump_Loop.FBX, Jump_Start.FBX y Run.FBX. Impórtalas dentro de la carpeta Animations. Puedes abrirlas en el Persona Editor para que les des un vistazo.

Como notarás tenemos tres fbx distintos, básicamente tres animaciones separadas para el salto. Y te preguntarás: ¿Esto por qué?… te cuento. Cuando comenzamos a desarrollar nuestro actual proyecto (puedes darle un vistazo aquí :)) en este juego el personaje salta mucho y a distintas alturas, lo mismo hace un salto simple chiquito, que un salto desde una gran altura y está más tiempo en el aire. El problema que teníamos con esto es que cuando el salto era de gran altura, donde el personaje estaba mucho tiempo en el aire, cuando iniciaba el salto reproducíamos la animación de saltando, que la teníamos en una sola parte, y cuando la animación terminaba su tiempo, como el personaje aun no había caído en el suelo, se quedaba con el ultimo frame de la animación y totalmente tieso, hasta que caía en el suelto y entonces seguía animándose normalmente. Por este motivo es que en la mayoría de los casos es necesario separar las animaciones del salto. Tendremos el pedazo de la animación cuando inicia el salto, después una animación que reproduciremos en repetición (en loop) mientras el personaje este en el aire, y por ultimo el trozo de animación cuando llega al suelo. De esta forma tendremos todo el ciclo del salto animándose perfectamente independientemente de la altura.

Vamos a configurar esto en el Blueprint de las animaciones del personaje. Como ya sabes, para el sistema de animación se usa una maquina de estado que nos permite separar perfectamente todos los estados en los que estará el personaje y configurar la animación para cada estado. Actualmente la maquina de estado de nuestro personaje es sumamente simple. Solamente tiene un estado Idle/Walk. Vamos a modificarla para agregar los estados del salto.

Dentro del HeroAnimBlueprint/AnimGraph, entra en la maquina de estado. Arrastra desde el nodo Idle/Walk para crear un nuevo nodo, ponle el nombre de JumpStart, arrastra desde JumpStart para crear otro nodo y ponle JumpLoop. Repite el procedimiento desde JumpLoop para crear un tercer estado JumpEnd y por ultimo conecta el estado JumpEnd con Idle/Walk para cerrar el ciclo. Te quedará así:

Nueva maquina de estado del Character, con los estados del salto

Nueva maquina de estado del Character, con los estados del salto

 

Lo que acabamos de hacer aquí es definirle 3 nuevos estados que va a tener el personaje y el orden en los que podrá alcanzarlos. O sea, el personaje podrá estar en reposo/corriendo y de ahí puede pasar al estado de JumpStart (inicio del salto). De este estado JumpStart puede pasar a JumpLoop (ciclo del salto). De JumpLoop puede pasar a JumpEnd y por ultimo de JumpEnd regresa nuevamente a Indle/Walk. Fíjate que la conexión entre los estados es una flecha que marca la dirección, o sea, cual es el estado origen y cual el destino. Es bastante lógico, si lo imaginas un poco o te paras un segundito y saltas :) reproducirás estos 4 estados y la transición por cada uno de ellos.

Cada estado tiene sobre la flecha de la transición un iconito arriba que representa la condición que define cuando es que se pasará de un estado a otro. Esta condición las tenemos que programar nosotros (mediante visual scripting con el Blueprint Editor). El primer caso sería cuando el personaje está en Idle/Walk y comienza un salto. La condición que usaremos para pasar a este estado será muy simple, crearemos una variable BOOL que nos permita saber si el personaje está en el aire o no y si está en el aire va a pasar al estado de JumpStart.

Da doble clic en el icono de la transición y entrarás en el modo edición de este elemento, Por defecto tiene un nodo Result con la descripción Can Enter Transition, este nodo espera un parámetro bool (true/false) que le dirá si la maquina activa este estado o no, o sea, si se efectúa la transición. Crea y agrega una nueva variable, como mismo hicimos en el tutorial pasado, pero de tipo bool. Ponle de nombre IsInAir o cualquier otro nombre identificativo que se te ocurra. Agrégala al blueprint en modo GET. Ahora conecta el Puerto de salida de esta variable al Puerto de entrada del nodo Result.

Lo que acabamos de programar aquí mediante visualscripting es: Si la variable IsInAir tiene valor true, entonces ejecuta esta transición. Lo que quiere decir, que el personaje pasara para el estado de JumpStart.

VisualScript de la transición entre Idle/Walk y JumpStart

VisualScript de la transición entre Idle/Walk y JumpStart

 

Guarda y sale del modo de edición de la transición de Idle/Walk a JumpStart y entra en el modo de edición del nodo JumpStart para definir que animación se va a reproducir aquí. Arrastra hacia al blueprint la animación JumpStart que tienes en los recursos y conéctala al nodo final. Para el caso de las animaciones JumpStart y JumpEnd, queremos que se reproduzcan solo una vez, o sea que no se reproduzcan en ciclo como hemos hecho hasta ahora con las animaciones de reposo y caminando, ya que la idea es reproducir JumpStart, al terminar, reproducir JumpLoop (esta sí en ciclo) hasta que el personaje llegue al suelo y por ultimo reproducir JumpEnd, una sola vez. Selecciona el nodo de la animación JumpStart y en el panel de propiedades desmarca la opción Loop.

VisualScript del nodo JumpStart

VisualScript del nodo JumpStart

 

Ahora, ¿cual sería la condición para pasar de JumpStart a JumpLoop?. Pues para este caso usaremos un nuevo nodo. La idea es determinar cuando se esté a punto de acabar la animación de JumpStart para comenzar a reproducir el loop. Da doble clic en el icono de transición de JumpStart a JumpLoop, como ya vimos, por defecto tenemos el nodo Result al que hay que conectar la salida del algoritmo que preparemos aquí y que determina cuando pasa el personaje a este estado. Agrega un nuevo nodo de tipo Time Remaining (Ratio) para Jump_Start Asset. Este nodo nos permite tener en todo momento el tiempo que le va quedando a la animación para que termine. Agrega ahora otro nodo de tipo float < float. Este nodo es un método que nos permite saber si un parámetro A es menor que otro B. Conecta el puerto de salida del nodo TimeRemaining al puerto de arriba de entrada de la comparación. Para el segundo parámetro de la función menor que, no vamos a conectar ningún nuevo nodo, sino que vamos a definir un valor a mano. Como queremos que se comiese a reproducir Jumploop ya cuando esté a punto de terminar el JumpStart, pon en el campo del segundo parámetro de la función menor que: 0.1. Por ultimo conecta la salida de esta funcione al nodo Result. Te quedaría de la siguiente forma: [caption id="" align="alignnone" width="630"]VisualScript de la transición de JumpStart a JumpLoop VisualScript de la transición de JumpStart a JumpLoop[/caption]

 

Básicamente lo que programamos aquí fue: Si el tiempo que le queda a la animación por terminar es menor que 0.1 pasa al siguiente estado.

Guarda estos cambios, sale del modo de edición de la transición y entra en el modo edición del estado JumpLoop, agrega la animación JumpLoop y conéctala al Result. A diferencia de JumpStart, JumpLoop si queremos que se reproduzca en ciclo ya que es esta la animación que se estará reproduciendo mientras el personaje esté en el aire. Para esto, rectifica que en el panel de propiedades del nodo que representa la animación, tenga marcado el atributo Loop.

VisualScript del nodo JumpLoop

VisualScript del nodo JumpLoop

 

Vamos ahora a configurar la transición de JumpLoop a JumpEnd. JumpEnd es la animación cuando termina el salto, para pasar al estado JumpEnd simplemente sería cuando el personaje deje de estar en el aire. Para esto ya tenemos la variable bool isInAir así que vamos a usarla. Da doble clic en el icono que representa la transición entre JumpLoop y JumpEnd, agrega la variable isInAir como GET… un segundo !! isInAir nos dice si el personaje está en el aire, pero, cómo saber si el personaje NO está en el aire ?. Sería la negación de isInAir verdad ?. Entonces, agrega al blueprint un nodo de tipo NOT. Este nodo retorna la negación de su entrada. Conecta isInAir a NOT y NOT a Result. Listo ¡!, en cuanto el personaje deje de estar en el aire pasará a este estado.

VisualScript de la transición de JumpLoop a JumpEnd

VisualScript de la transición de JumpLoop a JumpEnd

 

Sale del modo de edición de esta transición, entra en el modo de edición del estado JumpEnd. Arrastra y conecta al Final Pose la animación JumpEnd y desmárcale la opción de loop.

VisualScript del estado JumpEnd

VisualScript del estado JumpEnd

 

Por último, tenemos que definir las condiciones para pasar de JumpEnd de nuevo a Idle/Walk. JumpEnd es la animación que termina el salto, ya el personaje está en el suelo pero recuperándose de la caída. Por lo que simplemente para pasar a Idle/Walk es esperar a que la animación JumpEnd esté a punto de terminar, como mismo hicimos de JumpStart a JumpLoop. Esto lo debes poder hacer por tu cuenta, así que inténtalo … 😉 te tiene que quedar así:

VisualScript de la transición de JumpEnd a Idle/Walk

VisualScript de la transición de JumpEnd a Idle/Walk

 

Muy bien, ya tenemos la maquina de estado del personaje lista, pero nos falta una cosa. En estos estados estamos usando una nueva variable IsInAir solo en modo GET, pero recuerda, como mismo hicimos para Speed, en algún punto esta variable tiene que tomar valor.

Cierra el AnimGraph y abre el EventGraph, agrega un nodo GetMovementComponent conecta el puerto de salida del TryGetPawnOwner que tenemos desde el tutorial pasado al puerto de entrada del GetMovementComponent, agrega otro nuevo nodo Is Falling, conecta el puerto de salida de GetMovementComponent al de entrada del IsFalling. Agrega la variable IsInAir en modo SET y conecta la salida de IsFalling a la entrada de IsInAir. Por último, conecta el puerto blanco de salida de SET Speed al de entrada de SET IsInAir para la continuidad del algoritmo.

EventGraph del HeroAnimBlueprint modificado para darle valor a la variable IsInAir cuando el personaje esté en el aire

EventGraph del HeroAnimBlueprint modificado para darle valor a la variable IsInAir cuando el personaje esté en el aire

 

Si has llegado hasta aquí desde el tutorial anterior no debes tener problema en entender que acabamos de hacer aquí. Obtenemos el MovementComponent del Character, este tiene un método IsFalling que retorna true/false si el usuario está en el aire o no. Este método es el que usamos para settear el valor de la variable IsInAir.

Listo, ya tenemos la nueva maquina de estado para nuestro personaje. Compila el AnimationBlueprint y corre el juego. Toca la barra espaciadora . . . ya nuestro héroe sabe saltar también :)

Personaje en el medio de un salto animándose correctamente

Personaje en el medio de un salto animándose correctamente

 

Implementando mecanismo para que el personaje corra !!

Ya tenemos a nuestro héroe que sabe caminar, reposar y saltar, pero algo que nos va faltando y es clásico en un side-scroller, la posibilidad de correr, para obtener más impulso, saltos más largos etc. ¿Quién no recuerda en uno de los últimos niveles del primer Super Mario el hueco grandísimo que solo podíamos saltar si lo hacíamos con mucho impulso ehh? :) . . . Pues bien, vamos a darle esta misma habilidad a nuestro héroe. Vamos a implementar que cuando se esté caminando con la tecla Shift presionada el personaje corra.

Primero, abre el editor y agrega en los controles una nueva entrada de tipo ActionBinding ponle el nombre de Run y selecciona al tecla LeftShift. Cierra el editor y abre la clase HeroCharacter.cpp agrega dentro del método SetupPlayerInputComponent las dos siguientes líneas:


//Le dice al motor que cuando detecte la entrada de tipo Run (Shift) estando presionada la tecla, llame al metodo ToggleRunState.
    InputComponent->BindAction("Run", IE_Pressed, this, &AHeroCharacter::ToggleRunState);
    
//Le dice al motor que cuando detecte la entrada de tipo Run (Shift) al soltar la tecla, llame al metodo ToggleRunState.
InputComponent->BindAction("Run", IE_Released, this, &AHeroCharacter::ToggleRunState);

Fíjate en un detalle, estamos llamando al BindAction para la entrada Run dos veces y pasándole el mismo método ToggleRunState (que vamos a implementar ahora) pero la diferencia entre uno y otro es que el segundo parámetro especifica exactamente cuando es que se va a llamar al método. IE_Pressed cuando se presione la tecla Shift y IE_Release cuando se suelte. Lo que queremos hacer es que si se está tocando el shift el personaje corre pero si se suelta deja de correr, algo parecido a la misma combinación que teníamos que hacer en el Super Mario para el super salto !! :)

Bien, ahora vamos a implementar el método ToggleRunState. Agrega en el .h de HeroCharacyer.cpp la declaración del método:


/** 
* Se llama cuando el motor detecta la entrada Run 
* Intercambia el estado de correr del personaje
*/
void ToggleRunState();

Pasa a la .cpp y agrega la implementación:


/**
 * Se llama cuando el motor detecta la entrada Run
 * Intercambia el estado de correr del personaje
 */
void AHeroCharacter::ToggleRunState()
{
//Si el atributo MaxWalkSpeed del CharacterMovement está en 400.f lo aumentamos a 900.f para que el personaje se mueva mas rápido
//De lo contrario lo volvemos a poner en 400.f para que regrese a su velocidad de caminar.
    if(CharacterMovement->MaxWalkSpeed == 400.0f)
        CharacterMovement->MaxWalkSpeed = 900.0f;
    else
        CharacterMovement->MaxWalkSpeed = 400.0f;
}

Muy simple, por defecto la velocidad de desplazamiento del personaje es 400 cuando este método se llama por primera vez (cuando se presiona shift) el MaxWalkSpeed está en 400 y se pasa a 900, lo que hará que el personaje de desplace más rápido, y cuando se suelte el Shift se va a llamar de nuevo y se volverá a poner la velocidad en 400, disminuyendo el desplazamiento.

Una buena tarea sería que intentes implementar este mecanismo de correr en el Blueprint del HeroCharacter. Recuerda eliminar el código C++ si vas a usar el Blueprint. Te tendría que quedar así:

Variante en el Blueprint de la funcionalidad del correr

Variante en el Blueprint de la funcionalidad del correr

 

Yo en lo personal prefiero siempre mantener toda la lógica del personaje desde C++, pero ese ejercicio te puede servir para tomar más soltura en el Blueprint Editor que tanto llama la atención :).

Otra buena tarea sería que expongas los valores máximo y mínimo del MaxWalkSpeed en el Editor para que se puedan modificar fácilmente sin necesidad de llegar al código. Ya sabes que tendrás que usar el macro UPROPERTY, pero este si no te diré como tiene que quedarte, inténtalo por tu cuenta 😉

Compila y ejecuta el juego, a medida que estas caminando deja presionada la tecla shift, verás que el personaje se desplaza mucho más rápido, pero muy feo ya que solamente se afecta su desplazamiento pero no se afecta la animación.

Agregando la animación de correr al personaje

Vamos a agregar otra animación a nuestro personaje, para que cuando esté corriendo se anime correctamente. Para esto usaremos el mismo estado Idle/Walk que ya tenemos, pero más aun, dentro de este usaremos el mismo nodo con el que hacemos blend entre las animaciones de Idle y Walk. Una súper potencialidad de este nodo es que podemos agregarle más animaciones, no solo dos. La idea será modificarlo para configurarle tres puntos de control y no dos como tenemos ahora. Uno al inicio del grafico con la animación Idle, otro en el medio con la animación Walk y otro al final con la animación Run.

Importa Run.FBX de los recursos para el proyecto. Abre el IdleWalkBlendSpace1D que creamos en el tutorial pasado. Cámbiale la propiedad X Axis Range a 900 (que es el valor que toma el personaje al correr) y da clic en el botón Apply Parameter Changes. Ahora agrega al inicio del gráfico la animación de Idle, en el medio la animación de Walk y al final la animación de Run. Asegúrate de tener marcada la opción de Enable Preview BlendSpace y mueve el cursor sobre el gráfico para que veas como se van haciendo los blend, según el valor de speed, entre reposo/caminando/corriendo. Súper genial y fácil ehh ??.

Nueva configuración del IdleWalkBlendSpace1D para Idle/Walk/Run

Nueva configuración del IdleWalkBlendSpace1D para Idle/Walk/Run

 

Guarda, ejecuta el juego y prueba correr. Ya nuestro personaje camina y corre perfectamente.

Personaje corriendo con las teclas D+Shift presionadas

Personaje corriendo con las teclas D+Shift presionadas

 

Agregando monedas para recolectar en el nivel

Ya tenemos nuestro personaje moviéndose por el escenario caminando, corriendo y saltando al estilo side-scroller. Pero ir caminando por ahí sin nada que hacer es algo aburrido, no ? Vamos a tratar de mejorar esto un poco :) . . . vamos a implementar la posibilidad de recolectar monedas por todo el escenario. En futuros tutoriales veremos que gana nuestro héroe con estas monedas.

Primero, necesitamos el modelo de la moneda. En el MarketPlace de seguro que podrás encontrarte muchísimos StaticMesh que puedas usar como moneda que nuestro personaje pueda recolectar. Te recomiendo que te tomes un tiempo para recorrer el MarketPlace, de seguro te encontrarás cosas que te encantarán… y muchas FREE ¡! 😀

De cualquier forma, puedes bajar de aquí el FBX de una moneda MUY SIMPLE Y FEA :S porque ya te he comentado que se me da muy mal el modelado 3D, pero perfecta para nuestro tutorial. Importa este FBX al proyecto (yo creé una carpeta Coin). En la ventana de importar verás que Unreal detecta que este es un StatickMesh, expande la opción avanzada y marca las opciones material y texture. Esto para que importes también el material que tiene aplicado el modelo, que igual, es extremadamente simple pero le da el look de moneda, por su color amarillo.

Bien, ya con el recurso importado vamos a crear la clase C++ que encapsulará toda la lógica de la moneda. Crea una nueva clase desde el editor de nombre Coin. En este caso que herede de Actor. Cuando el Editor te pregunte si quieres abrir la clase en el IDE dile que sí. Modifica el .h de la clase para que te quede de la siguiente forma:


/** Representa una moneda. El personaje puede capturarla colisionando con ella */
UCLASS()
class ACoin : public AActor
{
	GENERATED_UCLASS_BODY()
    
    /** 
     * USphereComponent es un componente en forma de esfera generalmente usado para detectar colisiones simples 
     * Este será el Root de la moneda y con él detectaremos las colisiones entre el personaje y la moneda
     */
     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Coin)
    TSubobjectPtr<USphereComponent> BaseCollisionComponent;
    
    /** StaticMesh de la moneda, ya lo usamos anteriormente con el Character. En él tendrémos la instancia de el StaticMesh de la moneda */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Coin)
    TSubobjectPtr<UStaticMeshComponent> CoinMesh;
    
    /** Simple variable booleana para activar o no la moneda */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Coin)
    bool bIsActive;
};

Préstale atención a los comentarios, como siempre, para los detalles. Básicamente lo que hacemos es definir el atributo BaseCollisionComponent que será el componente raíz de nuestra moneda, y el que usaremos para detectar colisiones con ella y un UStaticMeshComponent para poder definir el StaticMesh que representará a la moneda en el nivel. Por último el atributo bIsActive que lo usaremos como bandera para desactivar la moneda cuando se colisione con ella.

Pasa ahora a la .cpp y modifica el constructor para que te quede así:


ACoin::ACoin(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{
    //Crea la instancia del USphereComponent
    BaseCollisionComponent = PCIP.CreateDefaultSubobject<USphereComponent>(this, TEXT("BaseSphereComponent"));
    
    //Inicializa el RootComponent de este Actor con el USphereComponent
    RootComponent = BaseCollisionComponent;
    
    //Crea la instancia del UStaticMeshComponent
    CoinMesh = PCIP.CreateDefaultSubobject<UStaticMeshComponent>(this, TEXT("CoinMesh"));
    
    //Agregamos el UStaticMeshComponent como hijo del root component
    CoinMesh->AttachTo(RootComponent);
    
    //Por defecto la moneda estará activa
    bIsActive = true;
}

Aquí instanciamos un USphereComponent como el RootComponent de nuestra moneda. Creamos la instancia del UStaticMeshComponent que después configuraremos desde el editor, lo agregamos al RootComponent y por ultimo inicializamos en true bIsActive. Ya que queremos que por defecto la moneda esté active.

Agregando monedas al nivel

Vamos ahora a crear el Blueprint de la moneda, como mismo hicimos con el Character en el tutorial pasado. Entra en la carpeta Coin del ContentBrowser, clic derecho/Blueprint y selecciona Coin como clase base, dale de nombre CoinBlueprint.

Como verás, está compuesta por los mismos componentes que definimos en el constructor un USphereComponent como RootComponent y un UStaticMeshComponent. Despliega el CoinMesh y selecciona el StaticMesh que importamos para la moneda.

Sección Components del CoinBlueprint. Agregándole el StaticMesh

Sección Components del CoinBlueprint. Agregándole el StaticMesh

 

Ahora, vamos a agregar algunas monedas al escenario. Busca en el ContentBrowser el CoinBlueprint y arrástralo al nivel en lugares donde quieras que salga la moneda para que el personaje las pueda alcanzar. Una cosa importante a tener en cuenta, recuerda que en este estilo de juego el personaje siempre tendrá un Y fija por lo que tienes que asegurarte de poner las monedas en la misma Y del personaje para que cuando camine en dirección de la moneda pueda colisionar con ella. Mi nivel es muy simple, quedó así:

Nivel en edición con dos monedas agregadas.

Nivel en edición con dos monedas agregadas.

 

Ya tenemos las monedas en el nivel, pero poco se puede hacer con eso. Si caminas hacia las monedas no pasa nada, además las monedas se ven muy feas estáticas ahí sin moverse. Vamos a arreglar estas cosas

Simple mecanismo de colisión para recolectar las monedas

Como pudiste ver, ahora mismo cuando el personaje le pasa por arriba a la moneda no pasa nada, esta sigue ahí como si nada pasara. Vamos a solucionar esto implementado la lógica para detectar cuando el personaje está sobre una moneda. De momento será simple nuestra implementación, lo que haremos es incrementar un valor de “Monedas recolectadas“ que tendremos en nuestro personaje y llamar al método OnCollected() que crearemos en la moneda para ponerla inactiva, eliminarla del nivel y temporalmente imprimir un log en la pantalla para debuguear este mecanismo.

Abre la clase HeroCharacter.h ya agrega las declaraciones siguientes:


    /** Cantidad de monedas recolectadas por el personaje */
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category=Coins)
    int32 CoinsCollected;

    /** Se llama constantemente en el Tick del personaje para determinar si se está colisionando con una moneda */
    void CollectCoins();
    
    /** 
     * Se ejecuta automáticamente por el Engine en cada frame del juego
     * @param DeltaSeconds la diferencia en segundos entre el frame pasado y el actual
     */
    virtual void Tick(float DeltaSeconds) OVERRIDE; 

Vamos a comentar un poco cual es la lógica que usaremos. Todos los Actors en Unreal Engine cuentan con el método Tick. Este método es llamado automáticamente por el Engine en cada frame del juego. Si estas leyendo este tutorial probablemente estés familiarizado con el desarrollo de juegos, al menos la teoría, y sabrás de sobra la importancia de este método. Básicamente, todos lo algoritmos que queramos que estén en constante ejecución por un Actor determinado tiene que ir dentro de este método Tick. El método Tick recibe como parámetro un float que es la variación en segundos entre un frame y el anterior. Ese valor es de mucha ayuda para usarlo como multiplicador a la hora de modificar la posición del personaje o la rotación del actor para que esto se ejecute dependiente del framerate que tenga el juego en ese momento y evitar saltos en caso que la ejecución del juego baje el framerate.

Entonces, lo que haremos será tener en constante ejecución el método CollectCoins y este método lo que hará será determinar si hay alguna moneda dentro del CapsuleComponent del Character, si es así, es que está arriba de una moneda.

Abre HeroCharacter.cpp y antes de terminar la implementación del constructor agrega la línea CoinsCollected = 0. Muy simple, al crearse el Character no ha recogido ninguna moneda. Ahora agrega la implementación del método CollectCoins() y Tick


/** Se llama constantemente en el Tick del personaje para determinar si se está colisionando con una moneda */
void AHeroCharacter::CollectCoins()
{
    //Arreglo de AActors para guardar temporalmente todos los Actors que se detecten que están colisionando con el personaje
    TArray<AActor*> CollectedActors;
    
    //CapsuleComponent cuenta con el método GetOverlappingActors. Este metodo nos retorna en la llamada dentro del arreglo que le pasamos por parámetro
    //todos los objetos que estan dentro de la capsula en ese momento.
    CapsuleComponent->GetOverlappingActors(CollectedActors);
    
    //Recorremos todos los objetos dentro del CapsuleComponent
    for(int32 i = 0; i < CollectedActors.Num(); i++)
    {
        
        //Como el arreglo es de AActors tenemos que catear cada elemento a ACoin antes de usarlo
        ACoin *Coin = Cast<ACoin>(CollectedActors[i]);
        
        //Nos aseguramos que la moneda está activa y que no ha sido llamado aún el método Destroy
        if(Coin != NULL && !Coin->IsPendingKill() && Coin->bIsActive)
        {
            //Incrementamos la cantidad de momendas recolectadas
            CoinsCollected++;
            
            //Por último llamamos al OnCollected de la moneda para ejecutar toda la lógica de la moneda cuando esta es tomada por el personaje
            Coin->OnCollected();
        }
    }
}

/**
 * Se ejecuta automáticamente por el Engine en cada frame del juego
 * @param DeltaSeconds la diferencia en segundos entre el frame pasado y el actual
 */
void AHeroCharacter::Tick(float DeltaSeconds)
{
    Super::Tick(DeltaSeconds);

    //En cada update del juego llamamos al CollectCoins para estar constantemente determinando si se está colisionando con alguna moneda
    CollectCoins();
}

Este método como dijimos es el que se llamará constantemente en el Tick del player para determinar si se está colisionando con una moneda o no. Tomate unos minutos en los comentarios de cada línea para que entiendas bien el proceso para determinar si se está colisionando con una moneda. Este es un mecanismo de colisión muy simple, pero suficiente para nuestro primer trabajo con colisiones en Unreal Engine 4 :). Una cosa importante, dentro del método CollectCoins hacemos referencia a la clase ACoin que creamos, para que esto no de error ve a la parte de arriba del .ccp y debajo de la línea #include “UE4Demo.h” agrega #include “Coin.h”

Solo nos falta un detallito. Si intentas compilar ahora tendrás un error, porque cuando se detecta una colisión se llama al método OnCollected de la clase Coin, y nosotros nunca hemos implementado ese método. Pues bien, vamos a implementarlo.

Agrega la declaración del método en el Coin.h y la implementación en el .cpp


/** A llamar cuando se detecte la colision con una moneda */
void ACoin::OnCollected()
{
    //A modo de Debug ponemos un log en la pantalla
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "Coin Collected !!");
    
    //Pasamos a false el flag de bIsActive
    bIsActive = false;
    
    //Y con el método Destroy de Actor, eliminamos la moneda del escenario
    Destroy();
}

Este método es el que se llamará cuando el personaje colisiona con la moneda, y de momento es muy simple. Primero usamos el método AddOnScreenDebugMessage del objeto GEngine que nos da el Framework. Este método es muy útil porque nos permite imprimir en la pantalla mensajes de un color determinado por un tiempo determinado. Para ir revisando nuestro código en tiempo de ejecución es genial. Aquí lo usamos para cuando el personaje colisione con una moneda se imprime en pantalla el texto Coin Collected !!”. Además ponemos en false la moneda y la eliminamos del escenario con la ayuda del método Destroy() de la clase Actor.

Listo, compila y ejecuta el juego. Ahora camina hacia una moneda … Bieeen !! al pasarle por arriba a la moneda esta se elimina de la escena, se incrementa la cantidad de monedas colectadas por el personaje (esto es interno, de momento visualmente no mostramos nada al respecto) y por ultimo mostramos ese log temporal en la pantalla de Coin Collected ¡!.

“Debugueando” las colisiones en el escenario

Si le prestas atención a los detalles verás un problemita que tenemos en este mecanismo de colisión. Trata de acercarte poco a poco a la moneda, verás que la colisión se detecta antes de que el personaje esté sobre la moneda. Para encontrar el problema en estos casos el Unreal Engine nos brinda un comando fenomenal que nos permite revisar en tiempo de ejecución el componente que tiene cada actor para las colisiones. Dale Play al juego nuevamente y fíjate que el editor tiene en la esquina superior derecha un campo para escribir comandos. Escribe en ese campo: show COLLISION y toca Enter. Inmediatamente en el juego se ve sobre cada actor los componentes.

Juego corriendo con el comando show COLLISION para debuguear los componentes para colisiones de cada Actor.

Juego corriendo con el comando show COLLISION para debuguear los componentes para colisiones de cada Actor.

 

Fíjate que el componente de la moneda es mucho mayor que el modelo de la moneda y probablemente sea bueno también reducir el radio de la capsula del personaje. Termina la ejecución del juego, abre el CoinBlueprint, selecciona en el modo Componentes el ROOT, en el panel Detalles busca la sección Shape que tiene la propiedad del radio de la esfera. Cambia este valor a 15, verás como en el preview la esfera que rodea a la moneda se acopla mucho más a nuestro modelo. Guarda y prueba de nuevo, verás como mejoró y ahora tiene el personaje que pegarse mucho más a la moneda para atraparla.

Modificando el radio del componente esfera para ajustar las colisiones con la moneda

Modificando el radio del componente esfera para ajustar las colisiones con la moneda

 

Puedes hacer lo mismo para la capsula del personaje si la quieres modificar un poco. Vale aclarar también que estos valores los puedes definir desde un inicio en el constructor de la clase desde C++. Prueba hacerlo como ejercicio.

Haciendo rotar las monedas constantemente en su eje.

Para darle un poco de vida a las monedas en el escenario, vamos a hacer que estas estén rotando siempre en el eje Y. Cierra el editor, abre la clase Coin.h y agrega las siguientes declaraciones:


    /** Factor de rotacion de la moneda */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Rotation")
    FRotator RotationRate;
    
    virtual void Tick(float DeltaTime) OVERRIDE;

Simplemente agregamos la declaración del método Tick que ya conocemos y un atributo nuevo RotationRate de tipo FRotator. Este será el vector de rotación que usaremos para aplicarle a la moneda constantemente en cada Tick. En realidad no tenemos que declarar un atributo para esto, pero haciéndalo así y sobre todo gracias al macro UPROPERTY podemos dejar en configuración desde el Editor el factor de rotación que tendrá la moneda.

Pasa ahora a Coin.cpp, antes de terminar la implementación del constructor agrega estas dos línea


    //Inicializa el factor de rotacion de la moneda en cada update
    RotationRate = FRotator(0.0f, 180.0f, 0.0f);
    
    //Activa para que se llame el método Tick de este actor en cada update del juego
    PrimaryActorTick.bCanEverTick = true;

La primera es la inicialización del vector de rotación que usaremos en el Tick de la moneda y la segunda línea es muy importante. Por defecto en esta clase que hereda de Actor el método Tick NO se llama, para activar en el Engine que se llame el tick de esta clase es necesario inicializar el atributo PrimaryActorTick.bCanEverTick en true.

Bien, hecho esto agrega la implementación del método Tick


void ACoin::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    //Agrega una rotacion a la moneda en cada tick para tenerlas rotando constantemente en el escenario
    AddActorLocalRotation(this->RotationRate * DeltaTime, false);
}

Como vez, muy simple. Básicamente en cada update de este Actor lo que hacemos es agregar un factor de rotación gracias al método AddActorLocalRotation.

Listo !! compila ejecuta y prueba. Super verdad !! ya las monedas se encuentran rotando en el escenario esperando a que las atrapes :)

Implementando la lógica de la moneda mediante el Blueprint.

Como ya hemos dicho, la decisión de implementar una determinada cosa en C++ o mediante VisualScripting con el Blueprint Editor es de uno. Yo en lo personal sigo las dos siguientes premisas. Primero, implementar algo siempre en un solo lugar. O sea, no tener parte de la lógica de un Actor en C++ y otra parte en el Blueprint. Trato de tenerlo todo en un mismo lugar en la medida de lo posible. Segundo, si la lógica a implementar en determinado Actor es muy simple, como es el caso de esta moneda, pues la solución ideal es el Blueprint. Independientemente de que con el Blueprint se pueden implementar algoritmos súper largos y muy complejos, yo para estos casos prefiero usar C++.

Una buen ejercicio es que intentes implementar la lógica de la moneda, todo lo que hemos hecho aquí pero en Blueprint. Te tiene que quedar así:

VisualScript de la moneda

VisualScript de la moneda

 

Fíjate que desde el Blueprint implementamos lo que va a pasar cuando se llama el método OnCollected. Para poderlo agregar aquí tienes que agregar en la declaración del método el macro UFUNCTION(BlueprintNativeEvent) o UFUNCTION(BlueprintImplementableEvent). Te explico ambos:

UFUNCTION(BlueprintImplementableEvent) es un método que NO va a tener implementación en C++, solamente será para agregarlo al Blueprint e implementarlo desde ahí. Por lo que en el .cpp no tendrás nada de este método y en el .h solo la declaración. Es en el Blueprint donde tienes que implementar lo que hará el método.

UFUNCTION(BlueprintImplementableEvent). Este es un poco más interesante. Cuando creamos un método con este macro, en la implementación tenemos que agregar en el nombre del método _Implementation. Por ejemplo, si quieres probar con OnCollected. La definición sería de la siguiente forma:


UFUNCTION(BlueprintNativeEvent)
void OnCollected();

Y la implementación quedaría así:


void ACoin::OnCollected_Implementation()
{
    //A modo de Debug ponemos un log en la pantalla
    GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, "Coin Collected !!");
    
    //Pasamos a false el flag de bIsActive
    bIsActive = false;
    
    //Y con el método Destroy de Actor, eliminamos la moneda del escenario
    Destroy();
}

Lo bueno con esto es que desde C++ tenemos una implementación, pero si queremos podemos sobreescribir esta implementación desde el Blueprint … super verdad ¡!?? cosas del UE4 :). Prueba esto implementado toda la lógica de la moneda desde VisualScripting, para que vayas ganando soltura con el Blueprint Editor, una de las maravillas del Unreal Engine 4.

Conclusión

Bueno, vamos terminando por hoy con este segundo tutorial sobre el desarrollo de juegos con Unreal Engine 4. Hoy aprendimos varias: Un mecanismo simple de detección de colisiones, la utilidad del método Tick, le agregamos las acciones de correr y saltar al personaje, configuramos la cámara de nuestro juego al estilo Side-Scroller 3D, vimos varios macros de variables y funciones de clase para la integración entre el código C++ y el Editor, y otras cosillas. Para el próximo tutorial continuaremos nuestro juego, vamos a agregar un HUD para que el usuario sepa la cantidad de monedas que ha alcanzado y el tiempo que le queda para lograr su objetivo, vamos a programar el GameMode para definir las condiciones de GameOver del juego y muchas cosas más 😉

Espero que te haya sido útil y te guste, si es así, déjame saber tus comentarios y comparte este tutorial con el resto de tus amigos también apasionados por el desarrollo de video juegos con Unreal Engine 4. Hasta la próxima !! . . . bye