viernes, 11 de octubre de 2013

3) Triunvirato ganador: PlayerController, Camera y Pawn.

¡Muy buenas!

Como vimos anteriormente, en el fichero de configuración DefaultGame.ini,  tenemos que especificar, qué clase se encarga de controlar los "pawns", representaciones físicas en UDK de personajes, puesto que la clase Pawn se encarga de manejar sus atributos y también tenemos un "GameManager" encargado de controlar qué tipo de juego estamos jugando, y cómo funciona el mismo. Para ello heredamos de la clase base de UDK GameInfo.

Sin más dilación, pasamos a modificar nuestros ficheros base del juego: NULLGameInfo,  NULLPawn, NULLPlayerController.

NULLGameInfo.uc

class NULLGameInfo extends GameInfo;

auto State PendingMatch
{
Begin:
    StartMatch();
}

defaultproperties
{
    //Más adelante podremos crear nuestra propia clase para HUD.
    //La especificaremos aquí
    HUDType=class'GameFramework.MobileHUD'
    //La clase que controlará nuestro Pawn. Claramente la que ya tenemos
    PlayerControllerClass=class'NULLGame.NULLPlayerController'
    //Qué tipo de Pawn aparecerá al iniciar el juego por defecto.
    //Más tarde cuando veamos los arquetipos cambiaremos esto
    DefaultPawnClass=class'NULLGame.NULLPawn'
    //Un flag para que no espere al comenzar el juego
    bDelayedStart=false
}

Como veis, es sólo un esqueleto y básicamente, está todo explicado con pequeños comentarios.

NULLPawn.uc

  class NULLPawn extends Pawn
    //Si queremos poder colocarla en el editor, debemos
    //marcar esta clase como placeable
    placeable
    //Como vimos antes, hace referencia a un fichero de
    //configuración del que se podrá configurar variables
    //Bajo el apartado [NULLGame.NULLPlayerController]
    config(Game)
    //ClassGroup nos sirve para que en la pestaña del
    //Content Browser "classes" nos aparezca bajo
    //Un apartado con este nombre <NULLGame> en este caso
    ClassGroup(NULLGame);

defaultproperties
{

//Componente Skeletal Mesh.Malla del personaje con esqueleto
Begin Object Class=SkeletalMeshComponent Name=MyPawnSkeletalMesh
    //Un SkeletalMesh de la instalación con Unreal Tournament. Si tenéis la limpia
    //Creo que hay otra que debéis buscar en el editor y botón derecho y copiar
    //su Full Name to Clipboard para copiarlo aquí
    SkeletalMesh=SkeletalMesh'UTExampleCrowd.Mesh.SK_Crowd_Robot'
    //AnimSets son colecciones de animaciones para usar con esta malla
    //The udk package should be under UDKGame/Content.
    //Right click on the animset in the content  browser and
    //copy full name to clipboard.
    //There is no animset in the custom installation ready
    //AnimSets(0)=AnimSet’EternalCrisis_Characters.Female.Human_Athletic_AnimSet’
    //The same for the animtree
    //AnimTreeTemplate=AnimTree’EternalCrisis_Characters.Human_Test’
    HiddenGame=FALSE
    HiddenEditor=FALSE
End Object

//Add it to the Actor’s components
Components.Add(MyPawnSkeletalMesh)

//Very important if we don’t want our character embracing the shadows forever
Begin Object Class=DynamicLightEnvironmentComponent Name=MyLightEnvironment
    //Different flags from the component
    bUseBooleanEnvironmentShadowing=false
    bIsCharacterLightEnvironment=TRUE
    bSynthesizeSHLight=TRUE
End Object

//Adding the component again
Components.Add(MyLightEnvironment)

}
Como veis en esta clase, también he colocado comentarios. Algunos en inglés, ya que he sido un poco perro y los he utilizado de mi antiguo blog. Igualmente, está actualizado con respecto a dicha página.

Cosillas interesantes en este caso. En primer lugar, el flag placeable en la declaración de la clase, hace que se pueda colocar este tipo de actor en las escenas a través del Editor. También he añadido el flag ClassGroup, que sirve principalmente para tener un poco ordenado el código y poder encontrar más facilmente las clases que creemos a través del Conter Browser, en la pestaña Classes, bajo el nombre NULLGame en nuestro caso.

Y después entramos en las DefaultProperties. En las clases de UDK, toda variable se coloca en el principio de la clase, declarando básicamente "var tipoDeVar nombreDeVar". Recalco el básicamente, que como ya sabéis hay ciertas estructuras que requieren otra nomenclatura para ser declaradas y que pueden ser Const, Static... Cosas de programadores rancios, ya sabéis a lo que me refiero.

En vez de poder iniciar dichas variables allí mismo como podríais hacer en C, debéis hacerlo en el interior de las llaves de DefaultProperties. ¡Sed cuidadosos! Colocad exactamente DefaultProperties y debajo la apertura de llave (símbolo {) ya que si la colocáis al lado de DefaultProperties, el compilador, mágico y especialito, se quejará y os dirá una línea de otro lugar como el problema, dándoos más de un quebradero de cabeza. Igualmente, DefaultProperties siempre debajo de todas las funciones de código o el compilador ignorará que está delante a la hora de marcaros la línea donde se producen errores, volviéndonos claramente a un estado primario de primate que arranca teclas de un instrumento que no comprende.

Con respecto a cómo están formados los actores, se rigen por una composición de componentes. Es algo parecido a Unity pero sin ser visual. Lo cual da más quebraderos de cabeza. Básicamente un actor se forma con piezas (componentes), que disponen de sus propios atributos y variables que configurar. Un componente se crea mediante la fórmula:

Begin Object Class=<ClaseDelComponente> Name=<NombreQueMeSalgaDelHigo>
<Valores de variables>
End Object

Y posteriormente, posee el actor una estructura llamada Components (básicamente una lista), a la que le "añadimos" los componentes que deseemos. Más información la tenéis en este apartado de la UDN: UDN Components

Y ahora vamos con NULLPlayerController, controlador de nuestro recién creado Pawn.

NULLPlayerController.uc

class NULLPlayerController extends PlayerController
    //Como vimos antes, hace referencia a un fichero de
    //configuración del que se podrá configurar variables
    //Bajo el apartado [NULLGame.NULLPlayerController]
    config(Game);

Como podéis ver, de momento, no vamos a tocar mucho esta clase. Simplemente me aseguro de heredar de la clase más básica de PlayerController con la que contamos (que a su vez hereda de Controller, pero eso ya es otra historia... Que podéis investigar en la carpera Development/Src/Engine/Classes como ya comenté...).

Si nos vamos ahora a nuestro IDE, y ejecutamos, veremos que ya está funcionando nuestro Pawn con una cámara que está dentro de él (mirad hacia abajo con el ratón para descubrir las vergüenzas de la malla elegida para vuestro pawn en el SkeletalMesh).

¡Ya estamos a medio camino de tener una base decente para trastear! Como indico en el título, la cámara que tenemos es un tanto "limitada". Y nos gustaría poder vernos de algún modo.

La primera solución se encuentra en configurar nuestro NULLPlayerController un poco más para indicarle que no queremos una cámara por defecto, si no una propia. Primero confeccionaremos la cámara con funcionalidad TopDown y luego la aplicaremos a nuestro "juego". ¡Al ataquerrrl con la cámara! (Que por cierto, la tenéis que crear en Development/Src/NULLGame/Classes por si se os ha olvidado a alguno).

NULLCameraTopDown.uc

   class NULLCameraTopDown extends Camera;

    /**
    * Query ViewTarget and outputs Point Of View.
    *
    * @param    OutVT        ViewTarget to use.
    * @param    DeltaTime    Delta Time since last camera update (in seconds).
    */
    function UpdateViewTarget(out TViewTarget OutVT, float DeltaTime)
    {

    // Don’t update outgoing viewtarget during an interpolation
    if( PendingViewTarget.Target != None && OutVT == ViewTarget && BlendParams.bLockOutgoing )
    {
    return;
    }

    //AQUÍ colocamos dónde queremos que se coloque la cámara.
    //Esta función nos da una estructura como parámetro de tipo
    //TViewTarget que en su interior tiene un objeto POV
    //Que es el Point of view de la cámara.
    //Tiene un vector Location para modificarla
    //Y también Rotation con el clásico
    //Roll, Pitch y Yaw en unidades de UnrealRotation
    OutVT.POV.Location.Z = 500;
    //Following target in XY plane
    OutVT.POV.Location.X = OutVT.Target.Location.X ;
    OutVT.POV.Location.Y = OutVT.Target.Location.Y ;
    OutVT.POV.Rotation.Pitch = -90 * DegToUnrRot; //DegToUnrRot is a constant to transform degrees to UnrealRotation units
    //UnrRotToDeg exists too

    // Apply camera modifiers at the end (view shakes for example)
    ApplyCameraModifiers(DeltaTime, OutVT.POV);

    }

    DefaultProperties
    {

    }
Bien, simplemente es para ir dando un pequeño bocado a la cámara y su funcionamiento. Básicamente como explico en los comentarios, sobreescribimos la función de la clase base Camera UpdateViewTarget que es la encargada de colocar la cámara donde debería. Las cámaras tienen muchas otras funciones y dependiendo de lo que necesitéis realizar en vuestro proyecto, tendréis que tocar más cosas. Pero ahora estamos con lo básico :)
En este caso, tenemos una Top Down camera (vista cenital) a 500 unidades de Unreal de distancia de nuestro Pawn. Ahora sólo falta indicarle a nuestra clase de PlayerController que queremos utilizar esta cámara.

NULLPlayerController.uc

class NULLPlayerController extends PlayerController
    //Como vimos antes, hace referencia a un fichero de
    //configuración del que se podrá configurar variables
    //Bajo el apartado [NULLGame.NULLPlayerController]
    config(Game);
   
defaultproperties
{
CameraClass=class'NULLGame.NULLCameraTopDown'

Simple, sencillo y para toda la familia: Modificamos la variable CameraClass heredada de la clase base PlayerController y le indicamos que es la clase que acabamos de crear.

Ejecutamos en nuestro IDE ;) Y disfrutamos de la magnificencia de lo logrado (si no es así, toca repasar los pasos anteriores :P).

Como podéis ver, ya tenemos una base para hacer cosas. Sin embargo, necesitamos el poder del Editor para poder configurar la cámara como deseemos o nuestro propio Pawn. Sobretodo si trabajamos con diseñadores o artistas que les gusta jugar con sliders, valores y demás historias que para nosotros son matemática fría :D

Pero eso tendrá que ser en la próxima entrega de "4) Dale al diseñador lo que quiere. Arquetipos para todos".

¡Un saludo!




No hay comentarios:

Publicar un comentario