# Custom Movement Mode en C++ — Wall Run

*Este artículo fue* [*publicado originalmente*](https://dev.epicgames.com/community/learning/tutorials/xj21/unreal-engine-custom-movement-mode-en-c-wall-run) *como un tutorial en la Epic Developer Community.*

---

# Tutoriales

En el siguiente vídeo muestro cómo funciona el **Custom Movement Mode** del componente `CharacterMovementComponent` en C++ y como podemos usarlo para añadir a nuestros personajes un nuevo movimiento. Para ilustrar su uso, implementamos un sencillo prototipo del movimiento de *correr por la pared* (Wall Run).

%[https://www.youtube.com/watch?v=eagZT49l7rw] 

Mientras que en este, terminamos el prototipo añadiendo algunas mejoras que faltaban, como el uso de animaciones adecuadas o curvas para ajustar algunos parámetros del movimiento.

%[https://www.youtube.com/watch?v=Y1mF2gJ9qhU] 

Al final de este artículo hay una lista de mejoras pendientes que podrían ser interesantes de implementar.

# Modos de movimiento

Como es bien sabido, los actores de la clase `Character` incluyen un componente `CharacterMovementComponent` que se encarga del movimiento del actor por la escena, según el **modo de movimiento** activo y la entrada recibida del jugador o —en el caso de los Personajes No Jugador (PNJ)— las indicaciones de la IA.

El `CharacterMovementComponent` soporta por defecto múltiples modos de movimiento, permitiendo al componente cambiar su comportamiento, procesando de manera diferente la entrada del jugador y considerando, según el caso, diferentes fenómenos físicos:

* **Walking**, utilizado cuando el personaje camina o corre por una superficie.
    
* **Falling**, empleado cuando el personaje está cayendo o saltando. En este modo se simula el efecto de la gravedad, que hace que el personaje caiga al vacío.
    
* **Swimminng**, utilizado cuando el personaje tiene que nadar a través de un líquido, de manera que el componente tiene que simular los efectos de la gravedad y la flotabilidad en el medio donde nada.
    
* **Flying**, usado cuando el personaje vuela, por lo que puede desplazarse con total libertad en 3D, ignorando los efectos de la gravedad.
    

Además de estos modos, el Ch`aracterMovementComponent` soporta varios tipos de movimiento adicionales que se pueden utilizar sin cambiar de modo. Por ejemplo, en el modo **Walking**, el personaje puede agacharse y caminar agachado o puede saltar.

Todo esto convierte al actor `Character` y a `CharacterMovementComponent` en herramientas muy potentes, que nos permiten comenzar a prototipar y probar cualquier idea rápidamente, sin tener que consumir tiempo y recursos en implementar un controlador propio.

# Añadir nuevos movimientos

Sin embargo, en muchas ocasiones necesitamos un tipo de movimiento no incluido por defecto en `CharacterMovementComponent`. Esto ocurre, por ejemplo, si queremos que nuestro personaje pueda escalar, esquivar, usar escaleras de mano o hacer algún tipo de *parkour*.

El `CharacterMovementComponent`  ofrece diversas formas de añadir un nuevo tipo del movimiento, siendo mejor una u otra según el caso concreto:

* **Launch**. El método `CharcterMovementComponent::Launch()` permite lanzar al personaje con la dirección y velocidad indicada. Suele ser muy útil para implementar ciertos movimientos sencillos, como la *esquiva* (*dash*).
    
* **Root Motion Source**. Utilizando  **root motion**, el movimiento del personaje en la escena viene determinado por la animación que se reproduce en la malla esqueletal. Sin embargo, este mecanismo ha sido modificado en el `CharacterMovementComponent` para que la información del movimiento también pueda ser generada mediante código. Este código se tiene que implementar en un **Root Motion Source** y este se activa en el `CharacterMovementComponent` cuando se quiere iniciar ese tipo de movimiento.
    
    Este sistema es muy versátil y permite crear todo tipo de movimientos. Es especialmente útil para movimientos que solo deben ejecutarse durante un breve periodo de tiempo, para que luego el personaje vuelva al comportamiento normal determinado por el modo de movimiento activo.
    
* **Custom Movement Mode**. Cuando el personaje puede permanecer durante bastante tiempo moviéndose de cierta forma, puede ser más interesante tener un modo de movimiento propio para ese tipo de movimiento.
    
    El modo **Custom** es uno de los modos de movimiento soportados por el `CharacterMovementComponent`, junto a **Walking**, **Falling** y otros modos mencionados anteriormente. La diferencia es que en este modo el personaje no se mueve, pues es responsabilidad de los programadores implementar el código correspondiente según cómo desean que se mueva el personaje.
    

En el vídeo que acompaña a este tutorial se muestra como hacer uso del **Custom Movement Mode** en C++.

# Custom Movement Mode

El modo **Custom** permite a los desarrolladores implementar varios modos nuevos de movimiento. Para ello, en el momento de activar el modo Custom, se debe especificar un identificador del submodo deseado. Esto se puede observar en nodo Set Movement Mode, con el que se puede cambiar el modo de movimiento activo de un componente `CharacterMovementComponent`:

|  |
| --- |
| Nodo Set Movement Mode en Blueprint |

El valor indicado en **New Custom Mode** al activar el modo **Custom** es entregado al código desarrollado a medida por los programadores para este modo de movimiento. De esta forma se puede saber el submodo activo y, por tanto, se pueden hacer cosas diferentes en cada caso.

# Sobre el tutorial

Comentemos algunos detalles adicionales sobre el vídeo con el que empezamos este tutorial.

## Proyecto inicial

El proyecto está basado en la plantilla Third Person en Blueprint. Solo se han importado previamente:

* Animaciones Wall Run Left y Wall Run Right, extraidas del paquete [All-IN-ONE PARKOUR](https://www.fab.com/listings/9018f998-dd97-4e16-a8b3-b0900405b70f).
    
* Una clase `AWRCharacterBase` en C++ derivada de `ACharacter` y de la que, a su vez deriva el Blueprint del personaje de la plantilla `BP_ThirdPersonCharacter`.
    
* Una malla estática de 300x300 cm. para construir las paredes sobre las que correrá el personaje.
    

## Posibles mejoras

El modo desarrollado solo pretende ilustrar la creación de un **Custom Movement Mode**, por lo que está lejos de mostrar una implementación completa. Estas podrían ser algunas mejoras al código mostrado.

### Tiempo de recuperación (cool down)

Como condición para activar el nuevo modo Wall Running se podría comprobar que haya pasado cierto tiempo configurable de recuperación. Esto evitaría que el jugador pueda volver a utilizar el nuevo modo de movimiento demasiado pronto.

Por ejemplo, evitaría que si mientras está en este modo el personaje cae, pueda volver a iniciar el modo **Wall Running** un poco más abajo en la misma pared, evitando realmente la caída.

### Añadir condición para que velocidad y forward ten la misma dirección y sentido

En `CanStartWallRunning()` se puede añadir la comprobación de que la velocidad del personaje `Velocity` y el *forward* del actor tengan la misma dirección y sentido para entrar en el modo `Wall Running`.

```cpp
const bool bAreVelocityAndForwardNearlyEqual = FVector::DotProduct(Velocity.GetSafeNormal2D(),
    GetOwner()->GetActorForwardVector()) > 0.7f;

// ...

return bIsWallRunnable && bIsFalling && bIsNearMaxSpeed && bIsNearMaxInput
    && bAreVelocityAndForwardNearlyEqual && bIsMovementParallelToWall;
```

Así obligamos al jugador a que tenga que mirar y mover al personaje en la dirección del muro para comenzar a correr sobre él. Si en el último momento el jugador cambia la dirección de la cámara, el personaje no entrará en el modo **Wall Running** y caerá.

### Integración con el modo Falling

Actualmente, para entrar en modo **Wall Running** el personaje debe estar primero en modo **Falling**. Cuando el `CharacterMovementMode` mueve al personaje en este modo puede ocurrir una colisión, que detectamos mediante el evento `OnComponentHit` de la cápsula para comprobar si el personaje ha llegado aún muro por el que puede correr.

Sin embargo, si examinamos el código de `CharacterMovementMode` veremos que no es así como se interrelacionan los diferentes modos implementados. Por lo general, están mucho más acoplados, siendo muy común añadir nuevos modos modificando directamente la clase `CharacterMovementMode`.

Por ejemplo, podemos ir a `PhysFalling()` —donde se implementa el modo **Falling**— y buscar dónde se mueve al personaje:

```cpp
// ...

FHitResult Hit(1.f);
SafeMoveUpdatedComponent( Adjusted, PawnRotation, true, Hit);
		
// ...
		
float LastMoveTimeSlice = timeTick;
float subTimeTickRemaining = timeTick * (1.f - Hit.Time);
		
if ( IsSwimming() ) //just entered water
{
	remainingTime += subTimeTickRemaining;
	StartSwimming(OldLocation, OldVelocity, timeTick, remainingTime, Iterations);
	return;
}

//...
```

La función encargada de mover el personaje es `SafeMoveUpdatedComponent()`, que devuelve una estructura `FHitResult` con información en caso de colisión. Por tanto, en lugar de usar el evento `OnComponentHit`, se podría comprobar directamente la colisión a la salida de `SafeMoveUpdatedComponent()` y llamar a nuestro `CanContinueWallRunning()` para comprobar si debemos entrar en el modo **Wall Running**.

En ese caso, si se activa el modo **Wall Running**, se puede llamar después a `StartNewPhysics()` indicando el tiempo restante del `DeltaTime` —es decir, usando el resultado `DeltaTime - Hit.Time` como nuevo `DeltaTime`—. De esta forma, se llamaría a nuestro nuevo `PhysWallRunning()` inmediatamente, para que el personaje comience a moverse en el nuevo modo de movimiento sin esperar al siguiente *frame*.

Se puede ver un ejemplo de esto en el código anterior cuando se comprueba `IsSwimming()` y, en caso afirmativo, se llama a `StartSwimming()`. De esta forma, el personaje pueden comenzar a nada inmediatamente.

### Animaciones

El resultado podría mejorarse si dispusiéramos de alguna animación de transición entre la que se usa en bucle mientras se está cayendo y las animaciones de Wall Run. También convendría ajustar el **Control Rig** del personaje para que los pies se posicionen correctamente tanto al correr en el suelo como a lo largo de una pared.

# Recursos adicionales

Estos son algunos recursos que pueden resultar útiles para seguir profundizando en el tema\*

* [Unreal Engine 5.5 Documentation — Programming with C++](https://dev.epicgames.com/documentation/en-us/unreal-engine/programming-with-cplusplus-in-unreal-engine).
    
* [Unreal Engine 5.5 Documentation — Setting Up Character Movement](https://dev.epicgames.com/documentation/en-us/unreal-engine/setting-up-character-movement).
    
* [Unreal Engine 4.27 Documentation — Character Movement Component](https://dev.epicgames.com/documentation/en-us/unreal-engine/character-movement-component?application_version=4.27).
