Aravid

Tutoriales, Gamer Sense, opinión y más

Archivos mensuales: octubre 2012

UDK: Ratón en la interfaz I

Ahora que tenemos el GameInfo que especifica cuál será la clase que gestionará el HUD, vamos a crear un HUD con la particularidad de que dispondremos de un cursor que podemos mover con el ratón, y mostraremos en la esquina superior izquierda información relacionada con él, en este caso la posición 2D dentro de la ventana, la posición 3D en la que se encuentra el cursor la mostraremos en la siguiente parte, porque requiere una pequeña explicación, y creo que vale la pena. Más adelante podremos utilizar esta información para interactuar con el juego, pero vayamos por partes.

Así terminará el tutorial, con un ratón en forma de flecha y la información de posición en la esquina superior izquierda en un gris sospechosamente claro

Creo que es justo nombrar el artículo de la UDN que tanto me ayudo durante el proceso de creación de esta parte en Dungeon Cleaner. Incluso recomendaría su lectura antes de proseguir (puedo entender que es algo largo, en momentos un poco técnico y tiene mucho UDK script) y es el siguiente:

http://udn.epicgames.com/Three/DevelopmentKitGemsCreatingAMouseInterface.html

De hecho, la mayoría de lo que yo voy a explicar se puede extraer de ahí, la diferencia principal es que el artículo ayuda para la creación de una interfaz genérica y hay ciertas cosas que no realiza que son importantes si queremos utilizar el ratón como método principal de juego, que es lo que ocurre en nuestro caso.

Si lo habéis leído (espero que sí) habla de dos maneras para obtener la posición del ratón y trabajar a partir de ahí, mediante Scaleform y mediante UDK script. Voy a hablar un poco de mi experiencia sobre este tema. No puedo decir que haya trabajado Scaleform de forma exhaustiva en el juego, pero sí que ha tenido algunos problemas y eso que no ha sido utilizado mucho, por eso, me decanto por UDK script.

Además me parece mucho más directo que Scaleform, por eso será el que voy a explicar, si queréis atreveros con el otro, allá vosotros. No digo que no sea igual de bueno, pero siempre he leído que hay que tener algo de cuidado con la cantidad de elementos de Scaleform que se introducen en la escena, porque podrían afectar al rendimiento.

Una aclaración más antes de empezar el trabajo duro, en el artículo divide el código en tres clases distintas, cuando todo se podría realizar en dos, personalmente, me gusta ser ordenado en mi código, y si puedo separarlo en distintas clases, lo prefiero así, por ello voy a mantener la misma organización que utiliza el artículo mencionado anteriormente.

Primero tenemos que crear las clases de HUD y de PlayerController especificadas en el GameInfo, haciendo una consulta rápida a dicho artículo, y siempre manteniendo la coherencia de añadir el DC al comienzo de todos los archivos (cosa que realmente es bastante chorra si uno se para a pensar, al final tendrás una veintena de archivos que todos comienzan por DC y no servirá para nada, incluso un punto en la jerga informática habla sobre esto) son DCleanerHUD.uc y DCleanerPlayerController.uc

Ya se ha comentado antes, pero para hacer un repaso rápido, en DCleanerHUD escribiremos todo el código relacionado con el HUD (era obvio, pero no me cuesta nada recordarlo) y en DCleanerPlayerController se especifica qué acciones realizan los controles y cómo se maneja el Pawn. Aprovecho para decir que desde el HUD podemos acceder al PlayerController y viceversa con un simple cast y  que ambas clases son únicas en una instancia del juego, ya sea monojugador o multijugador (en este caso, se refiere al jugador local).

Comenzamos con el DCleanerPC.uc

class DCleanerPC extends PlayerController;

defaultproperties
{   
  InputClass=class'DCleanerMouseInterfacePI'
}

Bien, no es mucho, nuestro PlayerController no tiene que hacer nada por el momento, lo único, especificar que la clase que gestionará la entrada del juego es DCleanerMouseInterfacePI, que es la clase que trabajaremos a continuación.

class DCleanerMouseInterfacePI extends PlayerInput;

var IntPoint MousePosition; 

event PlayerInput(float DeltaTime)
{

  if (myHUD != None) 
  {
    MousePosition.X = Clamp(MousePosition.X + aMouseX, 0, myHUD.SizeX); 
    MousePosition.Y = Clamp(MousePosition.Y - aMouseY, 0, myHUD.SizeY); 
  }

  Super.PlayerInput(DeltaTime);
}

defaultproperties
{
}

En la primera línea, creamos una variable de clase de tipo IntPoint llamada MousePosition (un punto que almacena valores enteros, no os voy a descubrir nada nuevo) que es donde vamos a almacenar la posición en la que se encuentra nuestro cursor en todo momento, y que será accesible desde las clases, esto es importante porque la clase del HUD tiene que conocer el valor de la posición del ratón para calcular la posición 3D (comentado en futuras entregas).

Después definimos nuestro propio PlayerInput que es un método de clase para que realice lo que queremos, en nuestro caso, lo que queremos es actualizar la variable que contiene la posición del ratón. La primera acción del método es asegurarse que existe myHUD, que es el nombre de la variable de la clase PlayerInput que apunta a nuestro HUD.

Es una buena práctica asegurarse de que las variables que no están contenidas en la clase existan, porque si no existen, el juego no va a detenerse e informarte que has accedido a una variable que no está disponible, lo general es un aviso en el log

El problema (para mi no, porque tengo dos monitores) es que no puedas tener el log y la ventana de juego visible a la vez, que solo utilices el UDK editor que no tiene ventana de log o directamente ignores el log (mala práctica, esto nunca, o el día que te de por utilizarlo, veas que salen mil warnings por segundo. Es gracioso porque con una actualización del repositorio me salían tantos warnings por segundo que ralentizaba mi PC).

Aquí hay algo de miga, pero tampoco mucha, primero explicar el método Clamp, que su labor es asegurarse que la primera variable (la coordenada X o la Y) se encuentre entre los otros valores de entrada del método (esto es entre 0 y el tamaño del HUD), esto es importante porque queremos que la posición del ratón no se salga de la ventana, y en caso de que se exceda, lo ajusta al valor mínimo o máximo.

Un detalle sobre la implementación del artículo y que yo también emplée es que utiliza el incremento de la posición del ratón, por eso se observa que el primer parámetro del método es la suma de la posición actual del ratón y aMouseX o aMouseY , que es el incremento en cada uno de los ejes.

Y no hay mucho más que contar, con eso disponemos de la posición del ratón en nuestra ventana de UDK, ahora tenemos que informar al HUD que queremos dibujar un material en la pantalla en la posición que está el ratón para que el usuario pueda conocer su posición, y también escribiremos un pequeño mensaje en la parte superior izquierda con la posición. No es extremadamente útil en este momento, pero es una buena manera de disponer de información adicional durante la ejecución.

Nota: Si os habéis dado cuenta, la posición del ratón se calcula por incrementos, y una variable no inicializada, tiene sus valores a 0, por lo que al iniciar nuestro juego, el ratón siempre estará en la posición (0,0). Se puede inicializar y poner cualquier valor, como por ejemplo, la mitad de la ventana (myHUD.SizeX/2,myHUD.SizeY/2)

Ahora es cuando comienza lo más interesante, DCleanerHUD.uc

class DCleanerHUD extends HUD;

var const Texture2D CursorTexture; 

event PostRender()
{
  local DCleanerMouseInterfacePI MouseInterfacePlayerInput;

  if (PlayerOwner != None && CursorTexture != None) 
  {
    MouseInterfacePlayerInput = DCleanerMouseInterfacePI(PlayerOwner.PlayerInput); 

    if (MouseInterfacePlayerInput != None)
    {
      Canvas.SetPos(MouseInterfacePlayerInput.MousePosition.X, MouseInterfacePlayerInput.MousePosition.Y); 
      Canvas.DrawTile(CursorTexture, 64, 64, 0.f, 0.f, CursorTexture.SizeX, CursorTexture.SizeY,, true);
      Canvas.SetPos(50, 50);
      Canvas.DrawText("Posición Ratón ("$MouseInterfacePlayerInput.MousePosition.X$","$MouseInterfacePlayerInput.MousePosition.Y$")");
    }
  }

  Super.PostRender();
}

defaultproperties
{
  CursorTexture=Texture2D'DCleanerAssets.flecha'
}

Bien, empezamos por las variables de clase, que se encargan de almacenar la textura que representará el cursor, en nuestro caso, utilizamos una bonita flecha que amablemente descargamos de opengameart.org, al que le borramos el fondo para que fuera transparente y estuviera colocado en la esquina superior izquierda de la imagen, es importante porque cuando decimos que queremos dibujar una textura en pantalla en una coordenada, la esquina superior izquierda quedará en dichas coordenadas.

Nuestro cursor

Ahora viene lo bueno, el evento PostRender() que se ejecuta cuando promete, aquí es donde escribimos nuestro código relacionado con el HUD y que será ejecutado después del proceso de renderizado. Primero nos declaramos la variable que nos servirá para acceder al PlayerInput y comprobamos que tanto el PlayerController como la textura estén disponibles, si no hay problemas, seguimos.

El HUD tiene una variable llamada PlayerOwner que es de tipo PlayerController, por lo que podemos acceder directamente y pedirle el PlayerInput, pero por cosas de la programación orientada a objetos (en la que no voy a entrar, asumo que si estáis leyendo esto, sabéis cómo funciona), es, exactamente, un PlayerInput y no un DCleanerMouseInterfacePI que es lo que queremos, así que tenemos que hacer un cast y guardamos el resultado del cast. Ahora podemos acceder a las variables y métodos de nuestra clase propia.

Nota: La manera de comprobar si el cast es correcto es comprobar si la variable es distinta de None, en caso de que lo sea, el cast se ha realizado correctamente, y en caso contrario, es que el cast no ha podido realizarse.

Una vez sabemos que se ha podido realizar, colocamos el pincel del Canvas en la posición del ratón, que obtenemos mediante nuestra variable, y ahí, dibujamos el cursor con el método DrawTile al que le hemos especificado el tamaño del cursor a 64×64.

Para terminar, también escribiremos en pantalla la posición del ratón, primero moveremos el pincel a la posición donde vamos a escribir el mensaje, a efectos de posición, la esquina superior izquierda es el origen de coordenadas, el eje X es positivo hacia la derecha y el eje Y es positivo hacia abajo, sabiendo eso, colocamos nuestro pincel y escribimos el mensaje en pantalla.

Nota: Si lo probáis, os daréis cuenta de que al mover el ratón, la cámara también se mueve, eso es porque al heredar de PlayerController, éste tiene una funcionalidad por defecto que es el movimiento de la cámara con el ratón y del Pawn con WASD. Por eso se mueve tanto el cursor como la cámara, más adelante nos encargaremos de anular este comportamiento para que al mover el cursor, no se mueva la cámara.

Ha sido un poco largo, pero ya se ha terminado esta primera parte, en la siguiente entrega calcularemos la posición 3D del cursor, modificaremos la cámara a una tercera persona y haremos que no se mueva al mover el ratón.

Anuncios