Game Framework

Sobre la base de los componentes y actores, UE4 construye su Gameframework.

El Gameframework se encarga de dirigir la lógica básica del juego. En este sentido el gameframework se encarga de definir quiénes son los jugadores, la IA, las condiciones de victoria y otras muchas cosas más en relación al gameplay.

GameMode

El gameframework gira en torno de un objeto central llamado GameMode.

Puedes crear nuevos GameMode heredando de la clase GameModeBase:

New Game Mode
New Game Mode

Un GameMode define las condiciones de victoria y las reglas de juego. En multiplayer tiene eventos muy interesantes como cuando un jugador se conecta y desconecta. También es responsable de spawnear los jugadores.

Los GameMode se asocian con cada mapa. Puedes hacerlo desde la pestaña World Settings:

World Settings
Setear el GameMode en World Settings

Por ejemplo, en un juego tipo Counter Strike podríamos tener dos GameMode, uno por cada condición de victoria: para desactivar la bomba y para rescatar los rehenes. El GameMode es responsable de la condición de victoria por lo que debe saber cuando se desactivó la bomba y cuando se rescató a los rehenes.

Siguiendo con el ejemplo de Counter Strike, el GameMode sería el responsable de spawnear los jugadores en sus localizaciones iniciales. De gestionar qué hacer cuando un jugador se desconecta. Cuando un nuevo jugador se conecta. De la transición entre mapas. De los cambios de equipo. Y, en definitiva, de todas las reglas del juego.

En blueprint siempre puedes acceder al GameMode actual en cualquier momento y desde cualquier sitio con el nodo:

Get Game Mode

En C++:

AGameModeBase* GM = GetWorld()->GetAuthGameMode();
AMyGameMode* MyGM = Cast<AMyGameMode>(GM);

Pawn & Controllers

Los actores que están controlados por el jugador ó por la IA se llaman Pawn.

El gameframework define dos tipos de objetos: PlayerController y AIController.

Cuando un Pawn es controlado por un PlayerController responde a las entradas del usuario (ratón, teclado, gamepad, etc.,).

Si un Pawn está conducido por una IA entonces dicho Pawn está poseído por una AIController. En un AIController hay multitud de opciones de configurar la IA. Por ejemplo con BehaviorTree, pero eso es otro tema.

Podemos concluir que un Pawn es un Actor que puede ser poseído por un controller (es decir por un PlayerController o un AIController).

Un Pawn tiene varios atributos interesantes:

Atributos del PlayerController
  • AIControllerClass – define que tipo de AIController por defecto poseerá este Pawn.
  • AutoPossessAI – si el Pawn aún no ha sido poseído por un AIController, entonces será automáticamente poseído cuando sea colocado en la escena por un AIController del tipo AIControllerClass
  • AutoPossessPlayer – si el Pawn no ha sido poseído todavía por un PlayerController entonces será automáticamente poseído por un PlayerController.

Aunque el PlayerController no tiene representación gráfica sí que almacena una rotación. ¿Para qué sirve una rotación en el PlayerController? Enseguida lo vemos.

Desde el propio Pawn se puede actualizar la rotación de su PlayerController:

Add Controller Input en Blueprint

En C++:

// dentro de la clase Pawn
float DeltaAngle = ...;

AddControllerPitchInput(DeltaAngle);
AddControllerRollInput(DeltaAngle);
AddControllerYawInput(DeltaAngle);
	
FRotator ControlRotation = GetControlRotation();
Add Controller Input en C++

Una idea interesante es actualizar la rotación del PlayerController con las entradas del jugador (por ejemplo, con el ratón o el stick derecho del gamepad):

Esta rotación es sumamente útil para hacer vistas en primera persona ó en tercera persona. Por un lado, tenemos almacenada la rotación acumulada de la entrada del usuario en el PlayerController. Y por otra parte tenemos la rotación real del Pawn. Jugando con ambas se pueden lograr hacer éstas vistas muy fácil.

En el Pawn los atributos interesantes para ello son:

  • bUseControllerRotationPitch
  • bUseControllerRotationRoll
  • bUseControllerRotationYaw

Como su propio nombre indica, éstas variables booleanas sirven para hacer coincidir la rotación del Pawn (pitch, roll o yaw) con la del PlayerController.

Puedes usar los plantillas que trae UE4 de primera persona, tercera persona y demás plantillas para ver como están seteados los atributos anteriores.

GameMode revisado

Ahora que sabemos de Pawns y Controllers ya podemos terminar de revisar el GameMode.

En el GameMode podemos setear las clases por defecto de Pawn y Controllers:

Setear el Pawn y Controllers por defecto en GameMode

Y setear el GameMode de cada nivel en World Settings:

Setear el GameMode en un mapa en concreto, desde World Settings

De este modo, cuando el juego empieza el engine consulta el GameMode asociado al mapa. Entonces spawnea el Pawn en la mejor localización (normalmente dónde esté situado el actor Player Start). Luego spawneará el PlayerController indicado en el GameMode. Por último el PlayerController poseerá el Pawn.

El engine no spawneará un nuevo Pawn si ya hay un Pawn en la escena con su AutoPossessPlayer activado. En vez de eso, spawneará el PlayerController que indique el GameMode. Posteriormente poseerá el Pawn que ya estaba en la escena.

Movement Component

Al igual que todos los actores, un Pawn se puede desplazar usando el nodo SetActorLocation y, al igual que todos los actores, también puede usar un componente especial del tipo MovementComponent para hacer un movimiento más sotisficado.

Diagrama de clases componentes

Pero es en un Pawn dónde más sentido tiene añadir un MovementComponent. Tal es así que los Pawn tienen un método GetMovementBase que devuelve el primer componente MovementComponent de la jerarquía.

Character

Hay un Pawn especial que ya nos trae de serie UE4, el pawn Character. Que es perfecto para los personajes humanoides.

Los Character ya traen una jerarquía de componentes interesantes:

  • CapsuleComponent como componente raíz para las colisiones.
  • ArrowComponent para indicar la dirección a dónde está encarando el personaje.
  • SkeletalMeshComponent para añadir el mesh del personaje.
  • CharacterMovement como componente ideal para el movimiento del personaje: permite andar, correr, saltar, nadar y todo ello con soporte multiplayer.

GameState & PlayerState

Como ya hemos visto, el GameMode define las reglas de juego. Cosas como: número de jugadores, dónde spawnearlos, pausar el juego, transición entre niveles, etc.,

El juego normalmente reportará algún tipo de progreso. Por ejemplo el tiempo transcurrido, jugadores restantes, etc., en definitiva información referente al juego que todos los jugadores desean saber. Aquí es dónde entra en juego el GameState.

El GameState almacena la información referente al juego y que es compartida por todos los jugadores.

¿Por qué no tener esta información en GameMode y molestarse en tener un objeto separado de tipo GameState?

La respuesta tiene mucho que ver con el multiplayer. Recuerda que GameMode define las condiciones de victoria, transición entre niveles, etc., por tanto el objeto GameMode reside en el servidor y no está en los clientes. Porque es el servidor quién debe ser responsable de esa lógica.

Sin embargo, los clientes necesitan tener algún tracking de la información del juego. Los GameState se replican entre todos los clientes y es actualizado por el servidor.

El PlayerState es el equivalente del GameState pero para los PlayerController. Un PlayerController ejecuta la lógica del jugador pero los atributos del mismo (equipo, puntuación, muertes, etc.,) están almacenados en un PlayerState.

Se puede acceder al PlayerState desde el propio Pawn.

Camera & ViewTarget

Si en la escena hay varias cámaras, ¿cuál usará el engine como cámara para el jugador? La respuesta es que el PlayerController decidirá.

En concreto, el PlayerController tiene una propiedad llamada ViewTarget de tipo Actor.

¿Qué es ese ViewTarget? Como su propio nombre indica, es dónde el PlayerController intentará encontrar la cámara. Buscará en ese Actor un CameraComponent.

Puedes cambiar el ViewTarget de un player controller de manera manual con:

Por defecto, cuando un player controller posee un Pawn automáticamente setea su propio ViewTarget a ese mismo Pawn. Por ello no es necesario setear el ViewTarget de manera manual.

Si quieres desactivar este comportamiento automático, usa el atributo bAutoManageActiveCameraTarget = false del player controller. Entonces puedes usar el nodo SetViewTarget para hacerlo manualmente.

Si necesitas un control más fino puedes crear tu propio PlayerCameraManager aunque, en general, no debería ser necesario.

El PlayerCameraManager es el responsable último que gestiona el ViewTarget. Puedes sobreescribir el método UpdateViewTarget para actualizar el ViewTarget de una manera personalizada.

Puedes actualizar el PlayerCameraManager dentro del PlayerController:

Player Camera Manager Class

HUD

Y por último tenemos el HUD (Head-up Display). El HUD es el objeto responsable de mostrar la UI en pantalla y otra información relevante como por ejemplo el crosshair del arma.

HUD

Puedes crear tu propia clase HUD y añadirlo al GameMode:

HUD en el GameMode

Siguientes pasos

Si quieres profundizar como funciona el gameframework puedes ver un caso práctico en el post acerca del multiplayer.

Jorge Moreno Aguilera

Jorge Moreno Aguilera