Introducción¿Que es una interfaz?La definición de interfaz es todo lo referido a la forma que tiene el juego de interactuar con el usuario. En otras palabras, la forma en que se controla el juego, la forma en que nosotros jugadores, le comunicamos al juego, que es lo que queremos hacer.
No solo a la hora de jugar, sino también cuando guardamos/cargamos partidas, cuando cambiamos alguna opción, etc etc.
¿Como es la interfaz en Juegos Scumm?En juegos Scumm (Day of Tentacle y anteriores), la interfaz principalmente se distinguía por tener abajo un espacio, donde había varios botones con acciones, y a la derecha de este espacio, se veía el inventario. También había un texto, que decía sobre que estaba el mouse, y que acción estaba seleccionada actualmente.
Principalmente era eso. Principalmente, es lo que siempre la gente quiere copiar. Esas tres cosas, inventario, texto, y botones.
Nadie sabe la razón, pero parece que esa interfaz gustó bastante, todavía se sigue repitiendo en innumerables juegos independientes de aventura gráfica.
¿Me permite hacerlo AGS?En AGS se puede hacer mucho mas de lo que se ve a simple vista. Para hacer una aventura gráfica (de cualquier tipo), basta y sobra, sobra bastante.
Importante: Es necesario usar AGS 3.1 o posterior para completar este tutorial. En caso de usar uno inferior, ver mi versión anterior del tutorial:
http://nahuel36.wordpress.com/ags/Comenzando
Los modos de CursorDebemos decirle a AGS cuantos modos va a haber, con que nombre se identifican, y como se verá el mouse al seleccionarlos.
Aparte de los cursores por defecto, necesitamos 4 más: Abrir, Cerrar, Empujar, Tirar.
Entonces vamos a Cursors en el panel, y renombremos cada uno:
Cursor 0: Ir aCursor 1: MirarCursor 2: UsarCursor 3: HablarCursor 4: Usar invCursor 5: AgarrarCursor 6: PunteroCursor 7: EsperaCursor 8: AbrirCursor 9: Cerrar
y agregamos dos más (Cursors -> click derecho -> New Cursor)
Cursor 10: Empujar
Cursor 11: TirarEn cuanto a la parte estética, si queremos que sea igual a Scumm, debemos tener una imagen de una cruz o una animación (view) de la cruz parpadeando. Y asignar ese view o sprite para todos los cursores por igual, excepto el cursor de Espera, el cual no tendría imagen.
Por defecto en AGS, cuando se selecciona un inventario se pone su imagen como cursor, si no queremos esto vamos a
General Settings -> Inventory ->
Use selected inventory graphic as cursor ->
False.
Las GuíasHagamos esa famosa barra con botones, inventario y texto. Para ello, creamos nueva Guía (
GUIs -> Click derecho -> New GUI).
La seleccionamos, y entre sus
opciones buscamos
Name, y le ponemos
gScummBar. Con ese nombre la reconocerá siempre AGS.
En
Layout se cambia tamaño y ubicación. Si queremos que sea como los de Scumm, entonces debería tener el mismo ancho que la resolución, por ejemplo si el juego es a resolución 320x200 es 320, si es 1024x768 entonces es 1024. El alto como mas les guste. Para la ubicación,
left valdría 0, y
top sería el alto de resolución menos el alto de la guía, por ejemplo si el alto es 56 y la resolución es 320x200, entonces
top es 144.

Ahora hagamos los
botones. Se crean desde arriba a la izquierda (
ver imagen). Luego, para cambiar tamaño y ubicación en forma precisa, se usan las opciones.
Como
Name es recomendable ponerles el nombre de la acción, antepuesta de una
b (esto solo para evitar confusiones). Por ejemplo:
bMirar,
bUsar, etc.
Lo ideal también es que cada botón tenga una imagen normal y otra para cuando está el mouse arriba (
Image y
Mouseover Image).
Faltarían otros dos botones más, que son para cuando hay mucho inventario y necesitamos bajar o subir en los casilleros. Como
Name les pondremos
bSubir y
bBajar.
Lo siguiente sería el
texto , que lo hacemos creando un
Label (
ver sBarra, al lado de botón). Que tendría como
Name bBarra (pueden poner otro. la b, como antes, es para evitar confusiones solamente).
Estaría ubicado sobre los botones, aunque no interesa puede ponerse distinto.
Y pueden hacer que esté centrado (
Text Alignment ->
TopMiddle).
Por último creamos el
inventario (el boton de la taza), y en
Name le ponemos
bInv.
Empezamos con el ScriptTodo muy lindo y prolijo. Pero sin programar, esto no sirve para nada, ahora le tenemos que decir a AGS como debe usar todas estas cosas que armamos.
¿Por donde empezar?Bueno, a veces no es fácil darse cuenta. Hay que pensar que es lo que queremos, y como se puede hacer. Con la práctica van a saber como es el mecanismo de AGS y sabrán armarlo solos
Recomiendo mucho que lean el tutorial de script para mejorar en este tipo de cosas.
Por empezar, vamos a hacer que esos
botones funcionen!!!
Los botonesPara ello, vamos a asignar que al hacerles click, corran determinado script (
Opciones del botón -> Click action-> Run script). Luego, vamos a
Events (
ver imagen), y en Onclick escribimos
gModo. Así se llamará la función donde pondremos que pasa al presionar cada botón.
Hacemos estos pasos con todos los botones de acciones.

Luego, en cualquiera de estos, volvemos a
Events, y hacemos Click en el boton
"..." que está a la derecha de
OnClick. Esto nos llevará al global script, y creará una función con el nombre que le dijimos. Por el momento no pongamos nada dentro. Pasemos a lo próximo.
Luego, con las flechas al lado del inventario, es algo muy similar, pero donde pusimos gModo pondremos esta vez
gScroll. Luego de hacerlo con ambas, hacemos click en el boton a la derecha de
OnClick, para que cree el script, como hicimos antes.
Bueno, perfecto, ahora, a ver, pensemos.
Se necesita que
constantemente el texto de la barra esté actualizandose. Entonces, no alcanzaría con cambiar el texto de la barra solo al apretar los botones, también habría que cambiarlo al pasar el mouse sobre el entorno.
Pero si constantemente cambia, ¿como sabemos cual fue el ultimo botón que apretamos? En otras palabras, el modo de cursor actual.
Por cuestión de comodidad, vamos a crear una
variable global, del tipo String, donde almacenaremos esa Accion actual.
Entonces, vamos arriba de todo del global script (
Scripts -> Global Script), y escribimos:
String Accion;
Ahora si, podremos continuar entonces.
Dentro del mismo global script, vayamos a la función
gModo.
¿Como funciona esto? Miren al lado del titulo, lo que está entre paréntesis son las
variables internas, estas varían según como se
llame a la función (esta funcion tiene una llamada oculta, para mas info lean el tutorial de script)
Entonces, la variable
control nos indica que botón fue presionado, y la variable
button indica con que click (derecho, izquierdo, etc). A esta última no le daremos uso esta vez.
Bien, entonces, identificaremos cada botón con su
Name, y asignaremos su modo de cursor correspondiente. Aparte se almacenará en
Accion el texto de la acción que irá en la barra.
Entonces, sería algo así:
function gModo(GUIControl *control, MouseButton button)
if(control == bCerrar){ mouse.Mode = eModeCerrar;
Accion = "Cerrar";}
else if(control == bAbrir){mouse.Mode = eModeAbrir;
Accion = "Abrir";}
else if(control == bDar){ mouse.Mode = eModeUsar;
Accion = "Dar";}
else if(control == bCoger){mouse.Mode = eModeAgarrar;
Accion = "Coger";}
else if(control == bMirar){mouse.Mode = eModeMirar;
Accion = "Mirar";}
else if(control == bHablar){mouse.Mode = eModeHablar;
Accion = "Hablar";}
else if(control == bUsar){mouse.Mode = eModeUsar;
Accion = "Usar";}
else if(control == bEmpujar){mouse.Mode = eModeEmpujar;
Accion = "Empujar";}
else if(control == bTirar){mouse.Mode = eModeTirar;
Accion = "Tirar";}
}Bueno, por ultimo, vamos a la función
gScroll. Por suerte hay un comando para bajar y subir el inventario, así que no tendremos problemas (antes no había, y menudo lío era):
function gScroll(GUIControl *control, MouseButton button)
{
if(control == bSubir)bInv.ScrollUp();
if(control == bBajar)bInv.ScrollDown();
}No hay mucho que explicar si se entendió lo de la función anterior, sube o baja
Ahora, necesitamos agregar lo demás. Necesitamos una funcion que se ejecute
constantemente.
Dentro de ella, tendremos que poner todo lo que iría en el texto de la barra, pensando en todas las posibilidades (si el mouse está sobre un objeto, sobre el inventario, sobre nada, si está usando un inventario, etc etc).
El texto de la barraHay que pensarlo de una forma, que pueda ejecutarse repetitivamente, y que no se acumulen cosas de más, por ejemplo una mala programación podría hacer que diga "usar pala pala pala" y cada vez se repita mas, por cada vez que se ejecuta la acción.
Yo elijo el siguiente modo: Tendremos una string para el inventario activo, una para un conector (por ejemplo: usar botella
con, dar billete
a) y una para el objetivo. Estas variables comenzarán vacías, según la situación actual, se llenará su contenido o no. Y luego se pondrán todas las variables en el texto de la barra.
De este modo, por cada ejecución se volverá a llenar la barra, pero se evita lo que dije anteriormente.
Bueno, ahora escribiré parte por parte, para que se entienda mejor, luego pongo todo el código junto.
String Objetivo, InvActivo, Conector;
Objetivo = "";
InvActivo = "";
Conector = "";- Primer posibilidad, inventario activo
if(player.ActiveInventory!=null){
InvActivo = player.ActiveInventory.Name;
if(Accion == "Usar") Conector = " con ";
else Conector = " a ";}Además de asignar el nombre del inventario, asigna un valor a
Conector según sea modo
usar o
dar.
- Segunda posibilidad, mouse sobre el inventario
if(InventoryItem.GetAtScreenXY(mouse.x, mouse.y) != null){
InventoryItem* item = InventoryItem.GetAtScreenXY(mouse.x, mouse.y);
Objetivo = item.Name;Es necesario crear una variable del tipo
puntero para poder programar algo sobre un inventario, eso sería lo que hace la segunda línea, basicamente le asigna el nombre
item al inventario sobre el mouse.
Con la segunda linea obtenemos su nombre y lo almacenamos.
Es necesario agregar algo mas a esta última, para evitar el "error" de que se pueda usar un inventario con sí mismo.
if(item != player.ActiveInventory) Objetivo = item.Name;Con esto asignará el nombre solo si el inventario activo es distinto del que está sobre el mouse.
- Tercera y última, cualquier otra cosa que no sea inventario xD
if(GetLocationType(mouse.x, mouse.y)!=eLocationNothing)
Objetivo = Game.GetLocationName(mouse.x,mouse.y);Con esto alcanza, el comando GetLocationType funciona para todo, excepto inventario.
- Por último, asignamos todo a el texto de la barra:
sBarra.Text = String.Format("%s %s%s%s",Accion,InvActivo,Conector,Objetivo );Presten atención a los espacios, es importante que estén bien colocados.
Por último, le pondremos un nombre a la funcion, ¿Que tal ActualizarBarra? Así quedaría completa entonces:
function ActualizarBarra(){
String Objetivo, InvActivo, Conector;
Objetivo = "";
InvActivo = "";
Conector = "";
if(player.ActiveInventory!=null){
InvActivo = player.ActiveInventory.Name;
if(Accion == "Usar") Conector = " con ";
else Conector = " a ";}
if(InventoryItem.GetAtScreenXY(mouse.x, mouse.y) != null){
InventoryItem* item = InventoryItem.GetAtScreenXY(mouse.x, mouse.y);
if(item != player.ActiveInventory) Objetivo = item.Name;}
if(GetLocationType(mouse.x, mouse.y)!=eLocationNothing)
Objetivo = Game.GetLocationName(mouse.x,mouse.y);
sBarra.Text = String.Format("%s %s%s%s",Accion,InvActivo,Conector,Objetivo );
}Ahora, necesitamos
llamar a esta función, y necesitamos que se haga repetidamente. Entonces pondremos su llamada en la función
repeatedly_execute del
Global Scriptfunction repeatedly_execute()
{
ActualizarBarra();
}
Perfecto!!!Un par de detalles y nuestro juego estilo Lucas Arts será funcional.
Ultimos detallesLuego de cualquier click, sería cómodo que vuelva al modo "Ir a", tal como pasaba en los juegos que tanto queremos.
Entonces, vamos a la funcion
on_mouse_click y agregamos estas lineas al final de todo:
mouse.Mode = eModeIra;
Accion = "Ir a";
player.ActiveInventory = null;
Con esto, volverá al modo
Ir a, y aparte quitará el inventario actualmente activo.
Además de esto, hagamos que al hacer click derecho, ejecute la accion
mirar sobre lo que estamos apuntando.
La función quedaría así
function on_mouse_click(MouseButton button)
{
if (IsGamePaused() == 1) // Game is paused, so do nothing (ie. don't allow mouse click)
{
}
else if (button == eMouseLeft)
{
ProcessClick(mouse.x,mouse.y, mouse.Mode);
}
else if (button == eMouseRight)
{
ProcessClick(mouse.x,mouse.y, eModeMirar);
}
mouse.Mode = eModeIra;
Accion = "Ir a";
player.ActiveInventory = null;
}Otro detalle, es que al comenzar el juego, la barra no dice nada. Esto se corrige agregando un par de líneas en la funcion
game_start, quedaría así:
function game_start()
{
mouse.Mode = eModeIra;
Accion = "Ir a";
}
Listo!!!Con esto, el juego quedará bastante funcional.
A la hora de programar su juego, tienen que tener en cuenta que las acciones Empujar, Tirar, Abrir, Cerrar, Usar y Dar, tienen un trato especial.
Las 4 primeras, como verán no aparecen en la seccion
Events de los objetos, hotspots o inventarios. Pero en lugar de ello pueden usar
Any click on.... Luego, dentro de ella, distinguimos entre los distintos modos usando la variable a esta altura tan usada
mouse.Mode.
Por ejemplo, interacciones sobre una caja:if(mouse.Mode == eModeTirar)
player.Say("No puedo tirarla");
if(mouse.Mode == eModeEmpujar)
player.Say("Está atascada");
if(mouse.Mode == eModeCerrar)
player.Say("Ya esta cerrada");
if(mouse.Mode == eModeAbrir)
player.Say("Hay una espada!!!");
Para el inventario, deben usar
Other click on inventory itemEl modo
Usar, debe distinguirse de alguna forma del modo
Dar (ya que si se fijan, ambos tienen el mismo modo de cursor, esto es así porque sino no funciona en AGS).
Para ello, podemos aprovechar que tenemos una variable que contiene el modo escrito en palabras, (el texto de la barra). Entonces, habría que preguntar si esta variable comienza con usar o con dar.
Recuerden que las acciones para Usar y Dar un inventario, van en
Events -> Use inventory on...Un ejemplo nunca viene mal:if(sBarra.Text.StartsWith("usar"))
player.Say("¡¡¡Te pego con mi espada!!!");
else
player.Say("¡¡¡No, es mía!!!");
(con poner else, alcanza para ver si es modo dar, ya que otros modos no va a haber en
Use Inventory)
Bueno, esto es todo por el momento.
Hay algunos errores que corregir, o cosas que perfeccionar y agregar. Pero este es el tutorial básico, con esto el juego ya es bastante funcional, y se puede empezar a programar.
Espero que les haya servido, cualquier cosa pregunten.
Abrazo