Un programa es un conjunto de instrucciones llamadas sentencias, que son interpretadas por GameMaker:Studio con el propósito de hacer que algo ocurra dentro de un juego. Ese "algo" puede ser una acción tan simple como sumar 2 más 2 para obtener 4, o tan complejo como hacer que un enemigo huya cuando su salud baja de cierto nivel. La estructura de un programa varía enormemente depndiendo de las funciones que usa, pero reduciéndolo a su estructura más básica, la representación de cualquier programa sería:
{
<statement>;
<statement>;
...
}
Un programa debe comenzar con el símbolo '{' y terminar con el símbolo '}', y entre estos dos símbolos se ubican las sentencias, cada una separada con un símbolo de punto y coma ';'. Ahora veamos un típico programa de GML , más precísamente un programa creado en el Editor de Scripts de GameMaker:Studio:
Existen diversos tipos de sentencias, las cuales se discutirán en las siguientes secciones del manual.
Como cualquier otro lenguaje de programación, GML usa variables como la unidad básica para la mayoría de las operaciones de programación. Las variables se utilizan para almacenar información en la memoria para su posterior (o inmediato) uso. Se les asigna un nombre para poder llamarlas en funciones y programas. Una variable en GML puede almacenar un número real, (como 100, 2.456575, -56, etc.) o una cadena (como "Hola , mundo").
De manera coloquial, una variable es un contenedor para poner un valor que se usará en una o más operaciones o funciones. Imagina el valor "pi", Un número de tipo real que guarda el valor 3.14159265(etc...). ¿Por qué? Es más fácil decir "pi" que "tres punto catorce quince noventaydos sesenta y cinco. De este modo, darle nombre a los valores facilita su uso, y también nos asegura que aun cuando el valor de la variable cambie, su nombre será siempre el mismo. El nombre de una variable en GML siempre debe comenzar con una letra y sólo puede contener letras, números y el guión bajo '_' (no se admiten cacateres acentuados ni especiales). Su longitud no debe exceder los 64 caracteres. Ejemplos de variables válidas son velocidad, limite_inferior, numero1; nombres no válidos podrían ser 5velocidad, limite superior, o *numero. En otros lenguajes de programación, es necesario "declarar" una variable antes de usarla. Esto quiere decir que se le indica a la computadora el nombre con el que se desea identificar esa variable, de tal modo que se le reserve un espacio en memoria para almacenar cualquier dato que se desea "poner" en la variable. Esto no siempre es necesario en GML, ya que depende del alcance de la variable. Hay cuatro categorías de variables en GameMaker:Studio y cada una tiene su propio alcance (el area de operación o su dominio). Las cuatro categorías son:
- instancia: La categoría más común. Las variables son definidas dentro de la instancia. Son únicas y propias de su instancia y se les puede usar en cualquier evento y cualquier función dentro de esa instancia.
- local: Estas variables se declaran mediante la función "var". Una variable local sólo es válida dentro del evento o script en el cual se crea. En este caso GameMaker:Studio creará la variable, la usará el tiempo que dure el evento y luego se "olvidará" de ella, lo que causará que se obtenga el error "unknown variable" si se intenta usarla posteriormente..
- global: Una variable global pertenece a todo el entorno de juego, y no a una instancia en específico (a pesar que se declare dentro de una instancia). En un principio, tiene que declararse como global, pero después de esto, cualquier instancia puede leer o cambiar su valor. El valor de la variable siempre reflejará la última operación de la que fue objeto, sin importar qué instancia desarrolló la operación.
- Variables internas: Son variables especiales inherentes a los objetos y habitaciones en el entorno. Están ahí desde que una habitación u objeto es creado. Pueden tener alcance de instancia o global, pero nunca local. Hay una gran cantidad de estas variables y cada una tiene usos muy específicos, se les describe con detalle a lo largo del manual en las secciones correspondientes.
Una variable de instancia se crea dentro dela instancia de un objeto y es considerada única para esa instancia: muchas instancias del mismo objeto pueden tener la misma variable, pero cada uno puede guardar un valor distinto, ya que esas variables son únicas a cada instancia. ¿Cómo se crea una variable de instancia? Sólamente hay que asignarle un valor, com ose muestra a continuación:
pociones = 12;
vida = 100;
nombre = "Juan Pérez";
fuerza = 5.5;
armadura = -2;
Sólo es necesario proporcionar un nombre y un valor (numérico o alfabético) para iniciar una variable y dejarla lista para usarse en la instancia del objeto que se está codificando. Desde luego, estas variables se pueden usar y modificar dentro de la instancia de distintas maneras, por ejemplo, restar una cantidad de la variable "vida" en un evento de colisión:
vida -= 5 + armadura;
Si la vida "vida" está en 100, su valor cambiará a 97 (100 - (5 + -2) = 97). Esto es un ejemplo sencillo, se podría reemplazar "armadura" por el valor -2, pero qué pasa si se usa ese valor en distintos lugares y decides cambiarlo? Se tendría que ir por todo el código y cambiar cada -2 por el nuevo valor, lo cual consume tiempo y puede conducir a errores! pero al usar una variable, todo lo que hay que hacer es reasignarle un nuevo valor, el cual será usado automáticamente en el código, aumentando la flexibilidad y facilitando la corrección de errores. Aun si el valor no fuera a cambiar, es más fácil recordar qué uso tiene una variable llamada "vida" que sólo ver un número.
GameMaker:Studio posee además un conjunto de variables internas con las que es importante familiarizarse, ya que accidentalmente se podría crear una variable con un nombre igual, lo que probablemente provocaría erores. Las variables internas se muestran con un color distinto en el editor de código y también aparecen sugeridas en la barra de auto-completar al fondo.
Las variables locales solamente se crean para eventos específicos, cuando dicho evento finalice, la variable se descarta (La única excepción a esto son los scripts, donde una variable declarada como local se mantiene local al scri0pt, y luego se descarta). ¿Por qué habríamos de necesitarla? Las variables ocupan espacio en memoria, y puede ser que sólamente las usaremos para una operación o función, en tal caso sólo es necesario tenerlas en memoria sólo el tiempo que sean usadas. Esto mantiene el código base limpio y ordenado, a la vez que se optimiza el espacio en memoria para las cosas que son realmente necesarias. Para declarar una variable local usamos la función var de esta manera:
var i, numero, texto;
i = 0;
numero = 24.5;
texto = "Hi there!";
Todas las variables del ejemplo anterior se "olvidarán" (desaparecerán de la memoria) al final del evento (o el script) en que fueron creadas. Debes ser cuidadoso y no dar a una variable local el nombre de una variable de instancia ya creada dentro del objeto ejecutando el código, y además, no tener la intención de usar el valor de la variable local fuera del evento donde ha sido declarada. Estas variables son usadas de manera frecuente, sobre todo en ciclos, para contar iteraciones, o cuando un valor se usa recurrentemente en una operación que no volverá a repetirse. Aquí un par de ejemplos:
var i = 0;
repeat (10)
{
inventario[i] = 0;
i+=1;
}
El código anterior crea una variable local de nombre "i" y la inicia en cero, and sets it to 0, todo en la misma línea. En veriones previas de GameMaker era necesario declarar primero la variable local primero y luego asignarle un valo, pero en esta versión se puede declarar y asignar un valor al mismo tiempo. En el ejemplo, el valor de la variable se usa para iniciar un arreglo. Como la variable "i" no va a ser usada en otras operaciones posteriores, puede tener un alcance local. Aquí hay otro ejemplo:
var xx,yy;
xx = x - 32 +irandom(64);
yy = y - 32 +irandom(64);
instance_create(xx, yy, obj_blood);
Aquí se ha usado la variable local para almacenar coordenadas aleatorias qusadas para crear una instancia. Se puede ver que no es estrictamente necesario usar estas variables, pero para propósitos de claridad y facilidad de uso lo hacemos así. Es mucho más claro y obvio lo que se está haciendo, que usar código como este:
instance_create(x - 32 + irandom(64), y - 32 + irandom(64), obj_guts);
Un detalle acerca de las variables locales: Al ser únicas al evento que las ejecuta, ¡también es posible usarlas en otras instancias mediante código! Es decir, podemos usar variables locales para ajustar y cambiar cosas en otras instancias mediante la sentencia "with()" (hay una sección sobre esto en sección Generalidades del lenguaje en el manual). El código luciría así:
var num = instance_number(obj_Enemy);
with (obj_Enemy)
{
if num>10 instance_destroy();
}
Este código funciona porque la variable "num"es local al evento (o script) en el que está contenida, NO A LA INSTANCIA NI EL ENTORNO DE JUEGO, lo que permite usarla en cualquier función en cualquier objeto siempre y cuando esté en el mismo bloque de código.
En términos simples, una variable global es un tipo de variable que una vez declarada no pertenece a ninguna instancia, pero puede ser leída por todas ellas. Las variables globales deben ser declaradas, tal como sucede con las variables locales, pero a diferencia de éstas, una variable global permanece en memoria hasta el final del juego. Por ejemplo, se puede crear una variable global para llevar un registro del número de balas del jugador y sencillamente actualizar esta variable en distintos puntos del juego. Una variable global no pertenece a una instancia en particular y puede ser leída, cambiada y usada por todas las instancias en cualquier momento, pero cualquier cambio hecho a la variable es también "global", es decir, el cambio afecta a todas las instancias que usen dicha variable. Veamos un ejemplo de esto:
globalvar food;
food = 5;
hemos creado una nuava variable llamada "food" la cual ha sido declarada como global. "food" esta disponible para cualquier instancia, por ejemplo, podría haber un objeto "food" contra el que el jugador colisione, y en ese evento de colisión tendríamos:
food +=1;
Podríamos tener otro objeto que dibuje el valor de "food", de esta manera:
draw_text(32, 32, "food = " + string(food));
Mediante variables globales podemos cambiar valores y ver reflejados esos cambios en todas las instancias que hagan referencia a esas variables. De la misma manera que con las variables locales, hay que tener cuidado de no nombrar de igual manera que alguna variable de instancia, ya que esto causaría problemas y se facilitaría la aparición de errores en el juego. Como recurso para evitar estos inconvenientes, podemos llamar a las variables globales usando la palabra reservada "global" seguida de un punto "." antes de la variable. Esto se ilustra a continuación:
global.food = 5;
Con este método, debemos usar la palabra "global" cada vez que deseemos usar la variable, por ejemplo:
global.food += 1;
draw_text(32, 32, "food = " + string(global.food));
GameMaker:Studio también posee una colección de variables globales "predefinidas", por lo que se las debe tener en cuenta para no nombrar de la misma manera a variables de instancia o a variables globales propias. Sin embargo pestas variables son fáciles de detectar ya que se muestran en distinto color en el editor de código y aparecen en la barra de auto-completado en la parte inferior. La mayoría de las variables globales predefinidas son muy específicas y rara vez se les utiliza, pero hay tres variables en particular que son muy útiles y comunes, por lo que se describen a continuación
listed below:
score;
Esta variable tiene un alcance global y se usa para almacenar un valor numérico que generalmente se trata del marcador del juego, sin embargo no necesariamente debe ser así - que el nombre sea "score" (marcador) no significa que tenga que ser usada para el marcador... puede almacenar el valor que se desee.
if place_meeting(x, y, obj_Bullet)
{
score += 10;
instance_destroy();
}
Este código revisa la instancia actual para determinar si hay una colisión con alguna instancia del objeto "obj_Bullet", y si la hay, suma 10 al marcador global y a continuación se destruye.
health;
Esta variable tiene un alcance global y se usa para almacenar un valor numérico que generalmente representa la salud del jugador health, pero hay que tener en cuenta que esto no necesariamente tiene por que ser así. A pesar de que el nombre es "health", se puede usar para almacenar cualquier valor numérico útil para el juego.
if place_meeting(x, y, obj_Enemy)
{
health -= 10;
if health <= 0 instance_destroy();
}
El código anterior revisa si hay una colisión con el objeto indexado en la variable "obj_Enemy", y de haberla, resta 10 puntos de la variable global health y luego determina si el valor de dicha variable es menor o igual a ceroSi se cumple esta condición, la instancia que ejecuta el código se destruye.
Anteriormente se describió cómo crear y usar variables dentro de una instancia, o de manera global, ¿pero qué pasa cuando desde una instancia se quiere acceder a una variable en otra instancia distinta? Hay muchos casos en que se presenta esta situación, por ejemplo, si en un evento de colisión con una bala se quisiera saber cuánto daño causa la bala, leyendo una de sus variables, o si se desea detener el movimiento de todas las balas en la habitación; o mover al protagonista a una posición determinada, por mencionar algunas. Veamos cómo lograrlo mediante GML.
Este método consiste en usar el nombre del objeto como identificador, seguido de un punto ".", seguido del nombre de la variable que se quiere cambiar o asignar. Es decir:
obj_ball.speed = 0;
Lo que hace el código anterior es ajustar la velocidad de TODAS las instancias del objeto "obj_ball", así que este método permite cambiar una variable en todas las instancias de algún objeto. ¿Y si se quiere cambiar la velocidad de sólamente una instancia en particular, en lugar de todas? En tal caso, el principio es el mismo, excepto que en lugar de usar el nombre del objeto como identificador de todas sus instancias, usamos el identificador de la instancia para decirle a GameMaker:Studio que sólo queremos manejar una sola instancia.
Bien, ¿pero qué es identificador de la instancia? Es un número de identificación único asignado a cada instancia creada en el juego. Cuando se coloca una instancia en una habitación desde el editor de habitaciones, este identificador aparece en la parte baja de la pantalla si se posa el puntero del ratón sobre la instancia, pero de igual manera, si se crea una instancia mediante código, también ella posee su identificador único. El id de instancia es siempre mayor o igual a 10,000 (diez mil), y este número puede ser usado también como identificador al lado izquierdo del punto. Nota: El punto es interpretado como un punto decimal en un número real, así que para evitar esto, hay que encerrar el id dentro de paréntesis!
El siguiente ejemplo muestra cómo se debe escribir esto:
(100012).speed = 0;
Adicionalmente, también se pueden usar ciertos objetos especiales o incluso variables para identificar instancias en los juegos. Una objeto especial es un tipo especial de variable que GameMaker:Studio reconoce e interpreta de manera particular. Al tratar con instancias, contamos con los siguientes objetos especiales:
Palabra Clave | Descripción | |
---|---|---|
self | Se refiere a la instancia que está ejecutando el bloque de código actual. | |
other | Se refiere a la otra instancia involucrada en un evento de colisión, o la otra instancia de una sentencia with. | |
all | Todas las instancas activas al momento en la habitación. | |
noone | Ninguna instancia (Puede parecer extraño, pero es muy útil en ocasiones, como se verá a continuación). |
En el caso de usar variables, se trata simplemente de almacenar el identificador de instancia en una variable, la cual se usa entonces antes del punto. En el siguiente ejemplo se ilustra esto...
Usando una palabra clave para ajustar todas las instancias:
if instance_position(mouse_x, mouse_y, all) = noone
{
all.speed = 0;
}
Usando una varible para ajustar una sola instancia:
var nnn;
nnn = instance_create(100, 100, obj_ball);
nnn.speed = 8;
nnn.direction = random(360);
Todas estas son formas válidas de leer y ajustar variables en otras instancias, y funciona porque el punto es en realidad un operador. Toma un valor como operando izquierdo y una variable (dirección) como operador derecho, y devuelve la dirección de esa variable en particular en el objeto o instancia indicada. Todos los nombres de objetos y objetos especiales de los párrafos anteriores representan en realidad valores, estos valores pueden ser tratados como cualquier otro valor. Los Nombres de objetos, los objetos especiales y los identificadores de instancia se pueden usar en muchas otras funciones, ya que GMS los trata como constantes.
write.
Hay dos situaciones en que la palabra reservada other puede ser usada para hacer referencia a una instancia en específico: Cuando se usa en una función with (explicado aquí) o cuando se usa en un evento de colisión, que es el caso que veremos a continuación.
Un evento de colisión ocurre solamente entre un par de instancias. Desde luego que se pueden presentar colisiones múltiples entre múltiples instancias, pero cada colisión es tratada por GameMaker:Studio una a una, entre la instancia en la que se definió el evento de colisión y la "otra" instancia involucrada. Imaginemos que tenemos un objeto jugador, objetos enemigos y objetos bala disparados por los enemigos. Se puede asignar a cada enemigo una instancia de bala, pero con distinta capacidad de daño calculada de manera aleatoria cuando se crea dicha instancia, por ejemplo:
var nnn;
nnn = instance_create(x, y, obj_Bullet);
nnn.damage = 5 + irandom(5);
nnn.speed = 8;
nnn.direction = point_direction(x, y, obj_Player.x, obj_Player.y);
Observa que estamos ajustando variables mediante el método del punto antes explicado. Ahora, ¿qué pasa con el objeto jugador? ¿Cómo hacer para que sepa cuánto daño va a recibir? Usando other en el evento de colisión (del objeto jugador).
hp -= other.damage;
if hp <= 0 instance_destroy();
el código de arriba leerá el valor almacenado en la variable "damage" de la otra instancia y lo restará de la variable "hp" del jugador, luego revisará si "hp" es menor o igual a 0. Si lo es, destruirá la instacia del object jugador. Es importante subrayar que este uso de other sólo funciona en el evento de colisión y que hay que asegurarse que las variables de la otra instancia existan, o de lo contrario causará error. Sin embargo, mediante other se pueden asignar valores a variables e incluso crear nuevas , usándola en el evento de colisión así:
other.mana += 10; //suma diez al a variable "mana" de la otra instancia
other.hit = true; //pon la variable "hit" de la otra instancia como verdadero, creándola si es que no existe
Una asignación es el método usado para almacenar un valor en una variable. Toda asignación presenta la siguiente estructura:
<variable> = <expresión>;
La expresión más sencilla puede ser un simple valor, pero también puede ser más compleja (una o varias operaciones). Además de asignar valores, también podemos sumar un valor al valor actual de la variable, usando +=, por ejemplo:
a += b;
De igual manera, se puede sustraer usando -=, multipicar usando *=, dividir usando /=, o usar operadores bit a bit |=, &=, o ^=. También se puede sumar o restar
uno de una variable, mediante ++, --.
a++; //Sumar 1 al valor almacenado en a