domingo, 30 de marzo de 2014

Elementos en pantalla ACS - ZDoom (Doom in Hexen Format) - Parte 1: Comandos.

Crear huds personalizados es algo que implica un trabajo relativamente complejo. Hay varias formas de llevar a cabo esto. Las dos formas más extendidas es mediante SBARINFO y ACS.

Personalmente voy a explicar los métodos para hacerlo con ACS, puesto que es mucho más flexible para éste tipo de trabajos que SBARINFO.

Con el articulo que sigue, voy a explicar como crear y ubicar un HUD personalizado, sin necesidad de recurrir a otro tipo de scripting.




Es un artículo bastante extenso, que constará de 3 partes. Sin embargo, en compensación, el lector podrá crear huds personalizados para sus proyectos, sin tener experiencia alguna en éste tipo de scripts.

Otra de las ventajas es que las imágenes mostradas por medio de ACS no requieren lumps adicionales y se muestran a full color, sin limitaciones de paleta en modo Software. Ésto es importante, puesto que nos sirve para consolidar el aspecto de nuestro mod.

Comencemos entonces.

Primer paso: Conocer y asimilar los comandos a utilizar.

Es crucial saber qué comando vamos a utilizar, y qué efecto tendrá cada uno de ellos en nuestro mapa. Voy a comenzar explicando los más sencillos y voy a terminar con el más complejo, y a su vez el más importante: Las variables.

Voy a dar una explicación a grandes razgos, para no extender demasiado el artículo, ya que son muchos los comandos que vamos a utilizar. Para el que quiera profundizar en el tema, voy a dejar el link del artículo de cada uno que existe en la wiki de ZDooM (Inglés).

Ya que la entrada es extensa, voy a crear una lista de los comandos que se describen. Ésto es para agilizar la lectura en caso de que solo se busque saciar alguna duda concreta.

1: Delay.
2: HudMessage.
3: SetHudSize.
4: SetFont.
5: ACS_Execute, ACS_ExecuteAlways y ACS_Terminate.
6: Condicionales (While / If).
7: GetCVar.
8: Variables.


Delay: Se utiliza para pausar el script durante una determinada cantidad de tiempo. Se maneja con Tics, que son una unidad propia de ZDoom.

Se utiliza de la siguiente manera:

Delay (Tiempo);

35 tics equivale a 1 segundo aproximadamente. Entonces, si queremos pausar el script 2 segundos, tendríamos que ingresar un delay con un valor de 70.


Articulo en la wiki de ZDoom (Inglés).

                                   

HudMessage: Con éste comando nos es posible imprimir un mensaje en pantalla. Generalmente son textos, a los que se le puede aplicar color y unos cuantos efectos. Sin embargo, en éste articulo vamos a ver que no es lo único que podemos hacer. También se pueden imprimir imágenes mediante él.
Gracias a la nueva versión de ZDoom (2.7.1) ahora es posible darle transparencias a lo que éste comando imprima, lo cual es sin duda una función espectacular que nos va a ayudar con muchos efectos.

Se utiliza de la siguiente manera:

HudMessage (Mensaje a imprimir; tipo de mensaje (Transición), id, color, posición horizontal (x), posición vertical (y), duración, alpha (transparencia) );

El mensaje a imprimir puede ser tanto un mensaje común y corriente como una variable o una imagen (Explicado más adelante en el artículo).

El tipo de mensaje es la transición que nuestro mensaje va a tener. Por ejemplo, FadeOut va a hacer que nuestro mensaje aparezca de golpe, pero desaparezca de manera gradual. FadeInOut va a hacer que nuestro mensaje aparezca y desaparezca de manera gradual.
Podemos asignarle flags, o propiedades especiales, mediante un |. Es decir, podemos hacer FadeOut | Alpha, y más adelante indicarle una transparencia.

El ID nos sirve para identificar el mensaje. Ésto sirve para mostrar varios mensajes a la vez. Utilizar dos veces un mismo ID va a hacer que el primer mensaje desaparezca al aparecer el segundo. Cuando veamos la inclusión de gráficos vamos a ver como aprovechar esta propiedad para cambiar el estado de un ícono.

Color: Es exactamente eso, el color que nuestro mensaje tendrá. Solo funciona con texto, y se maneja con nombres (CR_Green, CR_Grey, etc). Ver el artículo en la wiki de ZDoom para una lista completa.

Mediante la posición horizontal (x) y la posición vertical (y) vamos a ubicar nuestro mensaje. Se maneja con números fijos (Fixed number, en inglés) que manejan números enteros y decimales, y es relativo al porcentaje de la pantalla (1.0 en la posición x significaría que el mensaje se ubica al 100% a la derecha de la pantalla. Por ésto mismo, si escribimos 0.5, el mensaje se encontrará en el centro). Más abajo veremos que cuando introducimos el comando SetHudSize, el comportamiento será totalmente diferente.

La duración será especificada con un decimal de por medio (Si queremos que el mensaje dure 2 segundos, deberemos escribir 2.0). Para que un texto dure indefinidamente, deberemos escribir 0. De ésta manera, el texto solo desaparecerá cuando otro con el mismo ID aparezca en pantalla.

Alpha, o transparencia, es necesario para indicar la opacidad del mensaje. Si no se incluyó el flag ALPHA junto al tipo de mensaje, éste valor será omitido (Por default, el mensaje será opaco). 0.0 significa una transparencia total, y 1.0 es totalmente opaco. Siempre se utilizan decimales, ya que son números fijos (Fixed numbers).

Artículo en la wiki de ZDoom (Inglés).


                                   

SetHudSize: Con éste comando vamos a especificar el tamaño por el cual nuestro hud se va a poder mover. Ésto es muy importante, puesto que configurar un tamaño menor al de nuestra resolución va a limitar el espacio en pantalla que tenemos para ubicar nuestros elementos, y configurar un tamaño mayor hará exactamente lo contrario, aumentando la posibilidad de dejar mensajes fuera de pantalla.

Más adelante vamos a aprender a ajustar ésto de acuerdo a la resolución que estemos utilizando, de modo que nunca vamos a dejar un elemento fuera (Con excepción de sus bordes, pero en ésto también voy a profundizar más adelante, para no crear confusiones).

Se utiliza de la siguiente manera:

SetHudSize (Ancho, largo, barra de estado);

El ancho y el largo lo escribiremos normalmente, sin decimales. El valor de barra de estado solo admite un true (1) o false (0), y es para indicarle al comando si debe tomar en cuenta la barra de estado (Es decir, la gris de toda la vida del Doom original).

Éste comando va a afectar directamente el comportamiento de ubicación de HudMessage. Ésto significa que una vez que ingresamos éste comando, la ubicación de mensaje deberá ser indicada en pixeles. El decimal sigue utilizandose y debe ser incluido, solamente que ahora servirá para indicarle al mensaje por donde debe tomarse. Un decimal de 0, por ejemplo, indicará que el mensaje se mueve desde el centro.

Más adelante voy a explicar como funciona cada uno de los decimales, o bien puedes leerlo ahora en la wiki de ZDoom.

Artículo en la wiki de ZDoom (Inglés).


                                   

SetFont: Con éste comando se indica el tipo de fuente utilizado en el HudMessage que continúe. Debe ser una fuente definida en el lump FONTDEFS, o una por default del port (CONFONT, SMALLFONT, SMALLFONT2 y BIGFONT).

Se utiliza de la siguiente manera:

SetFont (Nombre del lump de la fuente);

Éste comando es crucial para imprimir imágenes con HudMessage.

Artículo en la wiki de ZDoom (Inglés).


                                   

Ejecutar / Terminar scripts: Existen determinados comandos para ejecutar o terminar un script.

Los siguientes son:

ACS_Execute
ACS_ExecuteAlways
ACS_Terminate

La diferencia entre Execute y ExecuteAlways es que el primero ejecuta un script una sola vez, mientras que el segundo permite varias ejecuciones del script simultaneamente.

La manera de utilizarse es la siguiente:

ACS_Execute (Script, mapa, argumento 1, argumento 2, argumento 3);

En Script indicaremos el número del script a ejecutar. Para ejecutar scripts con nombre en vez de números, debemos usar en cambio el siguiente comando:

ACS_NamedExecute (Script, mapa, argumento 1, argumento 2, argumento 3);

Recordemos que si es un nombre, deberemos escribirlo entre comillas. Hay nombres que no se pueden utilizar. Antes de terminar ésta sección voy a dejar la lista.

En Mapa indicaremos el número de mapa en el que se encuentra el script.

Los argumentos no lo utilizaremos en éste artículo, por lo que lo voy a dejar para otro momento.

Articulos en la wiki de ZDoom:

Lista de nombres de script reservados, que no deben utilizarse: http://zdoom.org/wiki/Reserved_ACS_names
http://zdoom.org/wiki/ACS_Execute
http://zdoom.org/wiki/ACS_ExecuteAlways
http://zdoom.org/wiki/ACS_Terminate

                                   

While / IF: Un condicional es un comando que va a ejecutar una serie de funciones si se cumple ciertos requisitos. Como cuando un script se ejecuta, o cuando un item spawnea en el escenario, solo cuando se presiona un switch dentro del juego. Mientras que a While se lo considera un loop, puesto que se repite hasta que la condición deje de cumplirse, IF es un condicional propiamente dicho.

Existe una increíble cantidad de formas de indicarle condiciones, pero la más extendida y útil para nuestro propósito primero será el de comparación numérica básica. Ésto es comparar si un numero es igual, mayor o menor a otro.

Así como suena, lo anterior no tiene mucho sentido, si no fuera porque en vez de indicarle números manualmente le vamos a indicar variables u otros comandos. Ésto significa que tanto While como IF pueden comparar si, por ejemplo, nuestra altura respecto del piso es de 150 y, de ser así, ejecutar lo que le indiquemos (Algo así como un subscript).

Dicho así, los condicionales son la panacea y, honestamente, sí... Lo son.

La diferencia entre IF y While es que IF va a ejecutar una sola vez lo que le indiquemos, y luego seguirá con el resto del script, mientras que While va a repetir la misma serie de comandos hasta que la comparación que le configuramos deje de cumplirse.

Al ser una función compleja y flexible tiene muchos métodos de funcionamiento, pero voy a indicar el principio básico que nos servirá para nuestro propósito:

While (condición) // While literalmente significa "Mientras". Esto significa que Mientras (0 sea igual a 0), por ejemplo, sucederá lo que indiquemos entre las llaves.

{

(Lineas/comandos a ejecutar)

};


If (condición) // If literalmente significa "Si...". Ésto significa que Si (0 es igual a 0), por ejemplo, se va a ejecutar lo que le indiquemos entre las llaves.

{

(Lineas/comandos a ejecutar)

};

Else (Opcional) // Ésto se puede utilizar luego de IF, aunque en la mayoría de casos no es necesario. Significa que si la comparación anterior dio negativa, se ejecuta la linea de comandos entre otras llaves indicadas debajo. Else significa literalmente "Si no". Es útil para reemplazar los posibles valores que se indiquen luego de IF.

En nuestro caso, la condición se puede indicar de las siguientes maneras:

While ( variable, comando o valor == variable, comando o valor ): Comprueba si el primer comando o variable vale igual que el que le sigue. Siempre se utilizan dos iguales para comparar de ésta manera.

While ( variable, comando o valor  != variable, comando o valor  ): Comprueba si la primer variable, comando o valor fijo no vale igual que el que le sigue.

While ( variable, comando o valor  > variable, comando o valor  ): Comprueba si la primer variable, comando o valor fijo vale más que el que le sigue.

While ( variable, comando o valor  < variable, comando o valor  ): Comprueba si la primer variable, comando o valor fijo vale menos que el que le sigue.

While ( variable, comando o valor  >= variable, comando o valor  ): Comprueba si la primer variable, comando o valor fijo vale más o igual que el que le sigue.

While (variable, comando o valor  <= variable, comando o valor  ): Comprueba si la primer variable, comando o valor fijo vale menos o igual que el que le sigue.

En todos los casos, los comandos a ejecutar se indican entre llaves, como si se tratara de un script. Hay que prestar atención, puesto que es fácil confundirse y mezclar las llaves del script con las llaves del while.

Los símbolos que utilizamos para comparación (=, > y <) se llaman operadores.

Es muy importante tener en cuenta que antes de finalizar un While, hay que incorporar un Delay de al menos 1 tic. Ésto es necesario hacerlo en cada bucle que creamos (Sea cual sea la técnica o comando que utilicemos), porque de lo contrario el engine va a generar un error, y el script no se va a ejecutar.

Aquí el artículo en la wiki de ZDoom.


                                   


GetCVar: Éste comando sirve para obtener el valor de una variable de consola (Console variable o CVAR). Aclarar que las variables de consola no son lo mismo que las variables internas de ACS. Por ésta razón, siempre se indican entre comillas.

Lo que más vamos a utilizar para nuestro hud es obtener los valores de la resolución. Las variables que guarda la resolución que se utiliza en el momento son:

vid_defwidth - Para el ancho. Si usamos una resolución de 640x480, éste valor va a dar 640.
vid_defheight - Para el alto. Si usamos una resolución de 640x480, éste valor va a dar 480.

La forma de utilizarse es la siguiente:

GetCVar ("Variable");

Entonces, para obtener la resolución deberíamos hacer ésto:

GetCVar ("vid_defwidth");
GetCVar ("vid_defwidth");

Claro que por sí solo ésto no sirve, por lo que debemos almacenar el resultado en algún lado. Acá es donde entran en juego las variables.

                                   

Variables: Ahora sí! La clave de todo. Las variables son valores que van a cambiar dependiendo ciertos parámetros que le vamos a indicar.

Hay varios tipos de variables, pero al igual que en los comandos anteriores, voy a reducir la explicación para no extender aún más el artículo.

El tipo de variable que vamos a utilizar es "local", se puede utilizar solo en el mapa que estamos editando, y su valor puede tener números o letras. Hay que tomar algo en cuenta, para no confundirse: También existe el tipo de variable "Local", que hace referencia a la variable que solo es válida dentro de un script determinado.

Las variables se declaran indicando el tipo de datos que la preceden. Como los datos que utilizaremos serán enteros (Numeros sin decimales), cadenas (Texto) o números fijos (Fixed Numbers, con decimales), utilizaremos siempre int (Que indica que el tipo de valor que tendrá la variable será entero). Éste tipo de variable en realidad debería servir solo para números enteros pero, por cuestiones técnicas propias del ACS en la que no vamos a profundizar ahora mismo, es posible indicar cualquier tipo de datos. Es por eso que con int, por ahora, nos alcanza:

Int JugadorVerde = 0;

Para indicar el valor en letras o en una frase se debe encerrar la frase entre comillas.

Int JugadorVerde = "Valor de la variable";

En el primer ejemplo declaramos la variable, le damos el nombre (JugadorVerde) y le indicamos que su valor inicial es igual a 0. Entonces por ahora sabemos que JugadorVerde es igual a 0.

Si más adelante queremos cambiar el valor, tenemos que hacer lo siguiente (Siempre dentro de un script):

JugadorVerde = 1;

Ahora JugadorVerde vale 1. El int no hace falta indicarlo, puesto que la variable ya existe y solo estamos redefiniendo su valor. Ahora bien, si lo anterior lo hacemos sin haber declarado (O creado, que es lo mismo) la variable previamente, nos va a tirar error al compilar el script.

También podemos sumarle 5, de la siguiente manera:

JugadorVerde = JugadorVerde + 5;

De ésta manera, a lo que valga JugadorVerde le agregamos 5. Como cuando al redefinirla le dimos un valor de 1, ahora valdrá 6, porque 1 + 5 = 6. Por supuesto que el resultado va a cambiar de acuerdo al valor que tenga JugadorVerde cuando la sumamos, y esa es la gracia del asunto.

Si JugadorVerde antes de la suma valía 5, el resultado será 10, porque 5 + 5 = 10.

El nombre puede ser cualquiera, pero lo ideal es utilizar un nombre que al leerlo sepamos a qué hace referencia. Si queremos crear una variable que lea la vida del jugador, sería ideal ponerle un nombre como "Vida" o "Health", para que sea más fácil identificar a qué se refiere.

Las variables se pueden declarar al principio del ACS, inmediatamente después del #include. De hacer ésto, la variable va a ser leída y válida para todos los scripts que hagamos.
También es posible declararlas individualmente dentro de cada script. De ésta manera, la variable vale solo dentro del script, y el resto de los scripts va a reconocerla como "No declarada", puesto que para ellos esa variable no existe.

A una variable se le puede indicar el valor que resulte de un comando, como bien se explicaba en la sección del comando "GetCVar". Ésto significa, que podemos hacer lo siguiente:

JugadorVerde = GetCVar ("vid_defwidth");

De ésta manera, JugadorVerde va a valer el ancho de la pantalla. Si usamos una resolución de 320x200, JugadorVerde va a valer 320.

Ahora bien, el ejemplo anterior no va a funcionar si la variable la declaramos al principio, fuera del script. Ésto es porque para indicar un comando no hay que poner comillas al declarar la variable, y sí para crear una variable con un valor de texto. ACS entonces va a pensar que se trata de un String, que es precisamente una variable con números texto, y que debe ser ingresada con comillas. Si ingresamos el comando entre comillas, va a leerse como un texto regular, y se va a imprimir en pantalla (En éste caso, se imprimirá el texto "JugadorVerde = GetCVar (vid_deftwidth)".

De todas maneras, es importante que la variable que contiene la resolución sea declarada o redefinida constantemente antes de utilizar su propósito (Digamos, antes de ejecutar los comandos para nuestro hud) para que de esa manera, si el usuario cambia la resolución en el curso del juego, la variable se amolde al nuevo tamaño de la resolución, y el hud se acomode como corresponde. Entonces, declarar una variable dentro del script es la mejor opción para obtener el valor de resolución.

Sin embargo, en ésto vamos a profundizar en la segunda parte de la guía.

                                   

Por ahora ésto es todo respecto a los comandos. En la próxima entrada vamos a pasar a ver un par de ejemplo de como utilizarlos dentro de un script ACS, cuales son los principios básicos a la hora de hacer un script con ACS (Para aquellos que nunca crearon uno) y, finalmente, el ejemplo de un HUD funcional utilizando los comandos anteriormente mencionados.

No hay comentarios:

Publicar un comentario