Lenguaje GML
Lenguaje GML
Lenguaje GML
más flexibilidad y control que las acciones estándar. Nos referiremos a este lenguaje como el GML (de Game Maker
Language). Hay diferentes lugares en los que puedes escribir programas con este lenguaje. El primero, cuando defines
scripts. Un script es un programa en GML. Segundo, cuando agregas una acción de código a un evento. En una acción
de código debes escribir un programa en GML. Tercero, en el room creation code. Y finalmente, en cualquier momento
que necesites especificar algún valor en una acción, puedes también emplear una expresión en GML. Una expresión,
como veremos más adelante no es un programa completo, sino una pieza de código que devuelve un resultado.
En este capítulo describiremos la estructura básica de los programas en GML. Cuando desees usar programas en GML,
se debe tener cuidado con ciertos aspectos. Primero que nada, para todos tus recursos (sprites, objetos, sonidos, etc.)
debes emplear nombres que inicien con una letra y que sólo consistan de letras, números y el guión bajo "‘_". De otra
forma no podrás referirte a ellas desde el programa. Mantente seguro que todos los recursos tengan nombres
diferentes, también ten cuidado de no nombrar a tus recursos self, other, global o all porque estas son palabras que
tienen un significado especial dentro del lenguaje. Tampoco debes usar ninguna de las palabras reservadas indicadas a
continuación.
La estructura básica del GML se trata con detalle en los siguientes capítulos:
Un programa.
Un programa consiste de un sistema de instrucciones, llamados sentencias. Un programa debe comenzar con el símbolo
‘{‘ y terminar con el símbolo ‘}’ . Las sentencias deben separarse con el símbolo ';'. La estructura global de todo
programa es:
{
<sentencia >;
<sentencia >;
...
}
Hay un número de diferentes tipos de sentencias, de las cuales vamos a ver más abajo.
Variables
Como en cualquier lenguaje de programación, el GML contiene variables. Las variables son las posiciones de memoria
que guardan la información. Las variables pueden almacenar valores reales o cadenas de texto. Las variables no
necesitan ser declaradas como en otros lenguajes. Hay un gran número de variables internas. Algunas son generales,
como mouse_x y mouse_y, las cuales indican la posición actual del cursor, mientras otras son locales para la instancia
del objeto para el cual se ejecuta el programa, como “x” e “y” que indican la posición actual de la instancia. Una
variable tiene un nombre que debe iniciar con una letra, y puede contener sólo letras, números, y el símbolo ‘_’ (La
longitud máxima es de 64 caracteres). Cuando haces uso de una nueva variable, ésta es local para la instancia actual y
no es conocida en los programas de otras instancias (aún del mismo objeto), aunque existe la posibilidad de hacer
referencia a variables de otras instancias; mira más abajo para mayor información.
Asignaciones
Una asignación pasa el valor de una expresión a una variable. Una asignación tiene la siguiente forma:
<variable> = <expresión>;
Una expresión puede ser un simple valor pero también puede ser más complicada. Además de asignar un valor a una
variable, también se le puede sumar usando +=, restar usando -=, multiplicarla usando *=, dividirla usando /=, o
Expresiones
Las expresiones pueden ser números reales (p. Ej. 3.4), números hexadecimales, comenzando con el signo ‘$’ (p. Ej.
$00FFAA), cadenas entre comillas simples o dobles (p. Ej. ‘hola’ o “hola”) u otras más complicadas. Para las
• &&, ||: funciones Booleanas (&& para la función and, || para la función)
• <, <=, ==, !=, >, >=: comparaciones, el resultado es true (1) o false (0)
• | & ^: operadores de bit (| = bitwise or, & = bitwise and, ^ = bitwise xor)
• << >>: operadores de bit (<< = shift left, > > = shift right)
• +, -: adición, sustracción
Nota que el valor x div y es el valor de x/y redondeado en la dirección de cero al número entero más cercano. El
operador mod devuelve el resto obtenido dividiendo sus operandos. En otras palabras, x mod y = x – (x
div y) * y. También, los operadores de bit existen:
• !: not, convierte un valor verdadero en falso y uno falso en verdadero
• -: cambio de signo
Como valores se pueden emplear números, variables o funciones que devuelvan algún valor. Las sub-expresiones se
pueden colocar entre paréntesis. Todos los operadores funcionan para valores reales. Las comparaciones también
Ejemplo
Puedes crear nuevas variables al asignándoles un valor (no es necesario declararlas antes). Si simplemente usas un
nombre de variable, la variable será almacenada sólo para la instancia actual. Por lo que no esperes encontrarla cuando
manejes otro objeto (u otra instancia del mismo objeto). También se puede cambiar y leer variables de otros objetos
colocando el nombre del objeto con un punto antes del nombre de la variable.
{
if (global.hacer)
{
// hacer cualquier cosa
global.hacer = false;
}
}
A veces quieres variables que solo estén dentro del actual piece of code o de un script. De esta manera evitas perder
memoria y estás seguro que no hay ningún conflicto con los nombres. Esto es también más rápido que usar variables
globales. Para hacer esto debes declarar las variables en el comienzo del código, usando la palabra “var”. Esta
declaración se ve así:
{
var xx,yy;
xx = x+10;
yy = y+10;
instance_create(xx,yy,pelota);
}
Como se dijo antes, puedes alterar variables en la instancia actual usando sentencias como:
x = 3;
Pero en ciertos casos querrás acceder a variables en otra instancia. Por ejemplo, para detener el movimiento de todas
las pelotas, o para mover al personaje principal a cierta posición, o, en el caso de una colisión, cambiar el sprite de la
otra instancia involucrada. Esto puede lograrse antecediendo el nombre del objeto y un punto al nombre de la variable.
pelota.speed = 0;
Esto cambiará la velocidad de todas las instancias del objeto pelota. Hay ciertos “objetos” especiales.
other.sprite_index = sprite5;
all.speed = 0;
global.message = 'Un buen resultado';
global.x = pelota.x;
Ahora tal vez te estés preguntando lo que la última tarea realiza cuando hay más de una pelota. Bien, se toma la
Pero qué tal si deseas establecer la velocidad de una pelota en particular, en lugar de la de todas ellas. Esto es un poco
más difícil. Cada instancia tiene un id único. Cuando colocas instancias en un cuarto en el diseñador, este id se muestra
cuando colocas el ratón sobre la instancia. Estos números son mayores o iguales a 100000. Puedes emplear estos
números como la parte a la izquierda del punto. Pero ten cuidado, el punto será interpretado como el punto decimal en
el número. Para evitarlo, colócalo entre paréntesis. Así por ejemplo, asumiendo que el id de la pelota es 100032,
puedes escribir:
(100032).speed = 0;
Cuando creas una instancia en el programa, la llamada devuelve su id. Una pieza de programa válido es:
{
nnn = instance_create(100,100,pelota);
nnn.speed = 8;
}
Esto crea una pelota y establece su velocidad. Nota que hemos asignado el id de la instancia a una variable y usamos
esta variable como indicación antes del punto. Esto es completamente válido. Déjame explicarlo un poco mejor. Un
punto es de hecho, un operador. Toma un valor como el operador de la izquierda y una variable (dirección) como el
operador de la derecha, y devuelve la dirección de esta variable en particular para el objeto o instancia indicados.
Todos los nombres de objetos, y los objetos especiales nombrados antes representan valores y pueden ser tratados
como con cualquier otro valor. Por ejemplo, el siguiente programa es válido:
{
obj[0] = pelota;
obj[1] = bandera;
obj[0].alarm[4] = 12;
obj[1].id.x = 12;
}
La última sentencia debiera interpretarse como sigue. Tomamos el id de la primera bandera. Para la instancia con ese
id establecemos a 12 su coordenada x.
Los nombres de objetos, objetos especiales y los id de las instancias pueden también emplearse en otros programas.
Arrays
Puedes emplear arrays de una o dos dimensiones en el GML. Simplemente coloca el índice entre corchetes cuadrados
para un array unidimensional, y los dos índices con una coma entre ellos para los arrays bidimensionales. En el
momento en que emplees un índice el array es generado. Cada array inicia en el índice 0. Por lo que debes tener
cuidado al usar índices muy grandes ya que se ocupará memoria para un array grande. Nunca emplees índices
negativos. El sistema coloca un límite de 32000 para cada índice y 1000000 para el tamaño total. Por ejemplo, puedes
escribir lo siguiente:
{
a[0] = 1;
i = 1;
while (i < 10) { a[i] = 2*a[i-1]; i += 1;}
b[4,6] = 32;
}
Sentencia If
o
if (<expresión>) <sentencia> else <sentencia >
La sentencia también puede ser un bloque. La expresión se evaluará. Si el valor (redondeado) es <=0 (false) se ejecuta
la sentencia después del else, de otra forma (true) se ejecuta la otra sentencia. Es un buen hábito colocar siempre
if (<expresión>)
{
<Sentencia >
}
else
{
<Sentencia >
}
Ejemplo
{
if (x<200) {x += 4} else {x -= 4};
}
Sentencia Repeat
Ejemplo
{
repeat (5) instance_create(random(400),random(400),pelota);
}
Sentencia While
ciclos while. Puedes fácilmente hacer que se repitan eternamente, en cuyo caso el juego se bloqueará y ya no
Ejemplo
El siguiente programa trata de colocar el objeto actual en una posición libre (esto es casi lo mismo que la acción para
{
while (!place_free(x,y))
{
x = random(room_width);
y = random(room_height);
}
}
Sentencia Do
do <sentencia> until(<expresión>)
La sentencia (que puede también ser un bloque) es ejecutada hasta que la expresión sea verdadera. La sentencia se
ejecuta por lo menos una vez. Ten cuidado con los ciclos do. Puedes fácilmente crear uno que se repita
indefinidamente, en cuyo caso el juego se bloqueará y ya no responderá a los eventos generados por el usuario.
Ejemplo
El siguiente programa intenta colocar el objeto actual en una posición libre (esto es lo mismo que mover un objeto en
{
do
{
x = random(room_width);
y = random(room_height);
}
until (place_free(x,y))
}
Sentencia For
Funciona de la manera siguiente. Primero se ejecuta la sentencia1. Entonces se evalúa la expresión. Si es verdadera, se
ejecuta la sentencia3; entonces la sentencia2 y luego se evalúa nuevamente la expresión. Esto continúa hasta que la
Puede sonar complicado. Debes interpretarlo de la manera siguiente. La primera sentencia inicializa el ciclo for. La
Ejemplo
El siguiente programa inicializa un array llamada “lista” de longitud 10 con los valores 1-10.
{
for (i=0; i<=9; i+=1) lista[i] = i+1;
}
Sentencia Switch
En ciertas situaciones querrás llevar a cabo alguna acción dependiendo de un valor en particular. Puedes lograrlo
empleando varias sentencias if pero es más sencillo si empleas la sentencia switch. Una sentencia switch tiene la
siguiente forma:
switch (<expresión>)
{
case <expresión1>: <statement1>; ... ; break;
case <expresión2>: <statement2>; ... ; break;
...
default: <statement>; ...
}
Funciona así: primero se ejecuta la expresión. Después se compara con los resultados de las diferentes expresiones
delante de las sentencias case. La ejecución continúa después de la sentencia case con el valor correcto, hasta que se
encuentre una sentencia break. Si no se encuentra una sentencia case con el valor correcto, la ejecución continúa
después de la sentencia default. (No es necesaria la sentencia default. Nota que se pueden colocar múltiples sentencias
case para la misma sentencia. También, no es necesaria la sentencia break. Si no existe una sentencia break, la
Ejemplo
El siguiente programa lleva a cabo una acción según la tecla que se presione.
switch (keyboard_key)
{
case vk_left:
case vk_numpad4:
x -= 4; break;
case vk_right:
case vk_numpad6:
x += 4; break;
}
Sentencia Break
break
Si se emplea en un ciclo for, while, repeat, en una sentencia switch o with, finaliza el ciclo o sentencia. Si es empleada
Sentencia Continue
continue
Si se emplea dentro de un ciclo for, while, repeat o con una sentencia with, continua con el siguiente valor del ciclo for
o de la sentencia with.
Sentencia Exit
exit
Simplemente termina la ejecución del programa/script actual. (¡No termina la ejecución del juego! Para ello necesitas la
Una función tiene la siguiente estructura: nombre de la función, seguido por uno o varios argumentos entre paréntesis,
<función>(<arg1>,<arg2>,...)
Hay dos tipos de funciones. En primer lugar, tenemos una gran cantidad de funciones internas, para controlar todos los
aspectos del juego. Después, cualquier scipt que definas en el juego puede ser usado como una función.
Nota que para una función sin argumentos aún se necesitan los paréntesis. Algunas funciones devuelven valores y
Nota que es imposible usar una función como el lado izquierda de una asignación. Por ejemplo, no puedes escribir
(instance_nearest(x,y,obj)).speed = 0.
Scripts
Cuando creas un script, querrás tener acceso a los argumentos enviados a él (ya sea cuando uses una acción script, o
cuando llames al script como una función desde un programa u otro, o inclusive desde el mismo script). Estos
argumentos se almacenan en las variables argument0, argument1, …, argument15. Por lo que puede haber como
máximo 16 argumentos. (Nota: cuando se llama un script desde una acción, sólo se pueden especificar los primeros 5
Los scripts también pueden devolver un valor, por lo que pueden ser empleados en expresiones. Para ello debes
return <expresión>
Ejemplo
{
return (argument0*argument0);
}
Para llamar un script desde una pieza de código, solo hazlo como cuando se hacen las llamadas a funciones. Esto es,
Construcciones With
Como se indicó antes, es posible leer y cambiar el valor de las variables en otras instancias. Pero en ciertos casos
querrás hacer mucho más con esas otras instancias. Por ejemplo, imagina que deseas mover todas las pelotas 8 píxeles
hacia abajo. Pudieras pensar que eso se logra con el siguiente código
pelota.y = pelota.y + 8;
Pero no es correcto. El valor a la derecha de la asignación obtiene la coordenada y de la primera pelota y le suma 8.
Entonces este nuevo valor se toma como la coordenada y para todas las pelotas. Por lo que el resultado es que todas
pelota.y += 8;
tendrá exactamente el mismo efecto porque es simplemente una abreviatura de la primera declaración. Entonces,
¿cómo logramos esto? Para ello existe la declaración with. Su forma general es
<expresión> indica una o más instancias. Para esto puedes emplear el id de la instancia, o el nombre de un objeto
(para indicar todas las instancias de este objeto) o uno de los objetos especiales (all, self, other, noone). <declaración>
se ejecuta para cada una de las instancias indicadas, como si la instancia fuera la instancia (self) actual. Así, para
with (pelota) y += 8;
Si deseas ejecutar múltiples declaraciones, colócalas entre corchetes. Por ejemplo, para mover todas las pelotas a una
with (pelota)
{
x = random(room_width);
y = random(room_height);
}
Nota que, dentro de las sentencias, la instancia indicada se ha vuelto la instancia self. Entonces, la instancia self
original ahora es la instancia other. Así, por ejemplo, para mover todas las pelotas a la posición de la instancia actual,
puedes usar
with (pelota)
{
x = other.x;
y = other.y;
}
El uso de la declaración with es muy poderoso. A continuación te muestro unos cuantos ejemplos más. Para destruir
Si una bomba explota y tu quieres destruir todas las instancias cercanas a ella puedes usar
with (all)
{
if (distance_to_object(other) < 50) instance_destroy();
}
Comentarios
Puedes agregar comentarios a tus programas. Todo en una línea después de // no se ejecuta. Puedes hacer también
El GML contiene un gran número de funciones y variables internas. Con ellas puedes controlar cualquier parte del
juego. Para todas las acciones existe una función correspondiente por lo que de hecho no necesitas emplear ninguna
acción si prefieres emplear código. Pero hay muchas más funciones y variables que controlan aspectos del juego que no
se pueden acceder sólo empleando acciones. Por lo que si deseas crear juegos más avanzados se te recomienda leer los
siguientes capítulos para tener un panorama general de todo lo que es posible lograr. Por favor nota que estas
variables y funciones pueden también emplearse cuando se envían valores para las acciones. Por lo que aún si no
planeas emplear código o escribir algún script, aún obtendrás beneficios de esta información.
En los capítulos siguientes se emplean las siguientes convenciones. Los nombres de variables marcados con un * son
Haciendo cálculos
Game Maker contiene un gran número de funciones para realizar determinadas tareas. Aquí tienes una lista completa
de estas funciones.
Constantes
true equivale a 1.
false equivale a 0.
pi equivale a 3.1415...
Estas son las funciones disponibles para trabajar con números reales:
max(val1,val2,val3,...) Devuelve el mayor de los valores. La función soporta 16 argumentos. Deben ser
todos números reales o cadenas de texto.
lengthdir_x(len,dir) Devuelve la componente horizontal (x) del vector determinado por la longitud y
dirección indicadas.
lengthdir_y(len,dir) Devuelve la componente vertical (y) del vector determinado por la longitud y
dirección indicadas.
Estas son las funciones disponibles para trabajar con cadenas de texto:
chr(val) Devuelve una cadena con el carácter al que hace referencia el código asci VAL.
ord(str) Devuelve el código asci del primer carácter de la cadena de texto STR.
real(str) Convierte una cadena de texto en un número real. STR puede contener signos negativos, puntos
string(val) Convierte el número real en una cadena de texto utilizando el formato estándar (sin decimales
cuando se trata de un número entero y un máximo de dos dígitos decimales en cualquier otro caso).
string_format(val,tot,dec) Convierte VAL en una cadena de texto utilizando nuestro propio formato:
TOT indica el máximo de dígitos y DEC el número de dígitos decimales.
string_delete(str,index,count) Devuelve una copia de la cadena STR con una parte borrada, que
empieza en INDEX y de una longitud definida por COUNT.
string_letters(str) Devuelve una cadena de texto que solo contiene las letras de la cadena STR.
string_digits(str) Devuelve una cadena que solo contiene los números de la cadena STR.
string_lettersdigits(str) Devuelve una cadena que solo contiene los números y las letras de la
cadena STR.
Game Maker dispone de varias funciones para trabajar con fechas y horas. La fecha y la hora se almacenan como un
número real. La parte entera es el número de días que han pasado desde 12/30/1899 y parte decimal de este valor es
la fracción de un día de 24 horas que ha transcurrido hasta el momento. Estas son las funciones disponibles:
date_inc_month(date,amount) Devuelve una nueva fecha N meses después de la fecha indicada. N debe
ser un número entero.
date_inc_day(date,amount) Devuelve una nueva fecha N días después de la fecha indicada. N debe ser
un número entero.
date_inc_hour(date,amount) Devuelve una nueva fecha N horas después de la fecha indicada. N debe
date_month_span(date1,date2) Devuelve el número de meses que hay entre las dos fechas. Reporta
los meses incompletos como una fracción.
date_week_span(date1,date2) Devuelve el número de semanas que hay entre las dos fechas. Reporta
las semanas incompletas como una fracción.
date_day_span(date1,date2) Devuelve el número de días que hay entre las dos fechas. Reporta los días
incompletos como una fracción.
date_hour_span(date1,date2) Devuelve el número de horas que hay entre las dos fechas. Reporta las
horas incompletas como una fracción.
date_minute_span(date1,date2) Devuelve el número de minutos que hay entre las dos fechas. Reporta
los minutos incompletos como una fracción.
date_second_span(date1,date2) Devuelve el número de segundos que hay entre las dos fechas.
Reporta los segundos incompletos como una fracción.
date_date_of(date) Devuelve la parte de la fecha del valor fecha-hora indicado, estableciendo la hora a 0.
date_time_of(date) Devuelve la hora del valor fecha-hora indicado, estableciendo la fecha a 0.
date_datetime_string(date) Devuelve una cadena indicando la fecha y hora definidos, en el formato
predeterminado para el sistema.
Game play
Hay una gran cantidad de variables y funciones que puedes emplear para definir el game play (jugabilidad). Estas en
Moviéndose
Obviamente, un aspecto importante de los juegos es el movimiento de las instancias de los objetos. Cada instancia
tiene dos variables internas x e y que indican la posición de la instancia. (Para ser precisos, indican el lugar donde se
encuentra el punto de origen del sprite). La posición (0,0) es la esquina superior izquierda del cuarto. Puedes cambiar
la posición de la instancia al cambiar los valores de sus variables x e y. Es lo que debes hacer si deseas movimientos
más complicados. Este código normalmente se coloca en el evento step del objeto.
Si el objeto se mueve con velocidad y dirección constantes, hay una manera más fácil de lograrlo. Cada instancia tiene
una velocidad horizontal (hspeed) y vertical (vspeed). Ambas se indican en píxeles por paso (step). Una
velocidad horizontal positiva indica movimiento a la derecha, una velocidad horizontal negativa indica movimiento a la
izquierda. La velocidad vertical positiva es movimiento hacia abajo y la negativa indica movimiento hacia arriba. Por lo
que sólo debes establecer estos valores una vez (por ejemplo en el evento de creación) para dar al objeto un
movimiento constante.
Hay otra manera muy diferente de especificar el movimiento, usando dirección (en grados 0-359), y velocidad (no debe
ser negativa). Puedes configurar y leer estas variables para especificar un movimiento arbitrario. (Internamente se
Para concluir, cada instancia tiene las siguientes variables y funciones referentes a su posición y movimiento:
x Su posición x.
y Su posición y.
place_free(x,y) Devuelve si la instancia colocada en la posición (x, y) está libre de colisión. Normalmente se
emplea para revisar antes de mover la instancia a la nueva posición.
place_empty(x,y) Devuelve si la instancia colocada en la posición (x, y) no se encuentra con nadie. Esta
función también toma en cuenta las instancias no sólidas.
ese objeto. También puede ser el id de una instancia, o la palabra especial other.
place_snapped(hsnap,vsnap) Devuelve si la instancia está alineada con los valores de snap hsnap y
vsnap.
move_random(hsnap,vsnap) Mueve la instancia a una posición libre, y la alinea con los valores hsnap y
vsnap, al igual que la acción correspondiente.
move_bounce_all(adv) Rebotar contra todas las instancias, en lugar de sólo con las sólidas.
move_contact_solid(dir,maxdist) Mover la instancia en la dirección dir hasta que haya contacto con
un objeto sólido. Si no hay collision en la posición actual, la instancia es colocada justo antes de donde ocurre una
colisión. Si ya hay una colisión en la posición actual, la instancia no se mueve. Puedes especificar la distancia máxima a
mover la instancia maxdist (emplea un número negativo para indicar distancia arbitraria).
move_contact_all(dir,maxdist) Igual que la función anterior pero esta vez se detiene hasta que haya
contacto con cualquier objeto, no solo sólidos.
máxima a mover (usa un valor negativo para indicar una distancia arbitraria).
move_outside_all(dir,maxdist) Igual que la anterior pero se mueve hasta estar fuera de alcance de
cualquier objeto, no solo objetos sólidos.
distance_to_point(x,y) Devuelve la distancia de la caja límite de la instancia actual hacia el punto (x,y).
distance_to_object(obj) Devuelve la distancia de la instancia actual a la instancia más cercana del
objeto obj.
Paths
En Game Maker puedes definir caminos o trayectorias (paths) y ordenar a las instancias que los sigan. Aunque puedes
usar las acciones para esto, existen funciones que te dan más flexibilidad:
path (una velocidad negativa indica que la instancia se moverá al revés sobre el path). endaction indica que
debería ocurrir cuando la instancia llegue al final del camino. Puedes usar los siguientes valores para esto:
0 : parase
1: continuar desde la posición inicial del path (s el path no está cerrado saltamos a la posición inicial)
El argumento absolute debe ser true o false. Cuando es true se usan las coordenadas absolutas del path. Cuando
es false el path es relativo a la posición actual de la instancia. Para ser más precisos, si la velocidad es positiva el
punto inicial del path se colocará en la posición actual de la instancia y se seguirá desde ahí. Cuando la velocidad es
negativa, el punto final del path se colocará en la posición de la instancia y el path se seguirá al revés desde ahí.
path_positionprevious Posición previa en el path. Esto se puede usar en eventos de colisión para colocar
la instancia en la posición anterior antes de una colisión.
path_speed Velocidad (en píxels por paso) con la que la instancia sigue el path. Con una velocidad negativa el
path se recorre en sentido inverso.
path_orientation Orientación (antihoraria) en la que se realiza el path. 0 es la orientación normal del path.
path_scale Escala del path. Auméntala para hacer el path más grande. 1 es el valor normal del path.
path_endaction La acción que se debe ejecutar al finalizar el path. Puedes indicar los valores explicados más
arriba.
La planificación del movimiento te ayuda a mover una instancia de un punto a otro esquivando otras instancias que
pudiera encontrarse por el camino (por ejempo, paredes). Resulta imposible dar funciones generales que funcionen en
cualquier situación. Así mismo, las operaciones necesarias para calcular un camino libre de colisiones consumen
bastantes recursos, así que debes usar estas funciones con criterio. Ten todo esto en cuenta cuando uses las siguientes
funciones.
Game Maker dispone de diferentes formas de planificar el movimiento. La más simple consiste en hacer que una
instancia de un paso hacia la posición final, intentando ir en línea recta pero tomando otra dirección si esto último
resulta imposible. Estas funciones deben usarse en el evento step de la instancia y se corresponden a las acciones ya
comentadas:
dirección para tratar de esquivar el objeto, moviéndose alrededor de él. Puede que no siempre se consiga llegar a la
meta, pero la función siempre intentará acercar lo más posible a la instancia. Devuelve true si se llega a la meta.
primero la instancia intenta moverse en línea recta hacia la meta. Para ello, mira un número de pasos adelante para ver
si hay algún obstáculo. Este número de pasos corresponde al valor ahead (por defecto 3). Reduciendo este valor la
instancia comenzará a cambiar su dirección más tarde si encuentra un obstáculo. Aumentándolo cambiará antes de
dirección. Si detectamos una colisión, la función mira a la derecha y a la izquierda para encontrar un camino libre. Esto
se realiza en pasos de tamaño rotstep (por defecto 10). Reduciéndolo conseguimos que la instancia tenga más
posibilidades para moverse pero la función será más lenta. El parámetro maxrot (por defecto 30) indica cuánto
puede cambiar como máximo la dirección en un paso. Así que aunque pueda moverse en línea recta hacia la meta no lo
hará si debe girar más de lo indicado por este parámetro. Aumentándolo conseguimos que la instancia pueda girar más
en cada paso, haciendo que sea más fácil encontrar un camino aunque éste será menos uniforme. Disminuyendo su
valor el camino será más suave pero la instancia realizará giros más largos, haciendo que a veces no pueda llegar
exactamente a la meta. Cuando la instancia no se puede mover en ninguna dirección el comportamiento dependerá del
valor de onspot. Si onspot es true la instancia rotará en su posición la cantidad indicada por maxrot. Si es
false se parará (esto es útil para coches, por ejemplo, pero reduce las posibilidades de encontrar un camino hacia la
meta).
Observa que el acercamiento potencial sólo usa información local. Así que sólo encontrará un camino si la información
es suficiente para determinar la dirección correcta. Por ejemplo, normalmente no podrá encontrar el camino para
escapar de un laberinto.
El segundo tipo de funciones calcula un camino libre colisiones. Una vez que el camino se ha calculado puedes
asignárselo a la instancia para que se mueva hacia la meta como si fuera un path normal que tú hubieras creado. El
cálculo del camino tarda un poco pero una vez hecho la ejecución del path es muy rápida. Por supuesto, esto es válido
si la situación no cambia (por ejemplo, si los obstáculos se mueven). Entonces necesitarás volver a calcular el path. De
nuevo, estas funciones pueden fallar en algunas circunstancias. Estas funciones sólo están disponibles en la
anteriores.
path. Si no consigue encontrar un camino, la función devolverá un path hasta la posición donde la instancia quedó
bloqueada.
stepsize e intentando evitar colisionar con los obstáculos. Utiliza pasos potenciales como la función
mp_potential_step() y los parámetros se pueden configurar con mp_potential_settings(). El
path indicado debe existir con anterioridad a la llamada de la función y será sobreescrito por el nuevo path (consulta el
capítulo sobre cómo crear y destruir paths). La función devolverá si se ha encontrado un camino. Para evitar que la
función continúe calculando para siempre debes especificar un factor mayor que 1. La función se detendrá y
devolverá un mensaje de error si no puede encontrar un camino que sea más corto que la distancia del origen a la
meta multiplicada por factor. Un factor de 4 es normalmente suficiente pero si crees que la instancia tendrá un
camino largo puedes aumentarlo. Si la función falla se crea el camino en dirección a la meta pero la instancia no
llegará la meta.
Las demás funciones usan un mecanismo mucho más complejo basado en rejillas (un algoritmo A*). Tiene más sexito a
la hora de encontrar caminos y hacerlos más cortos, pero requiere más trabajo por tu parte. Además, también puede
fallar en algunas ocasiones. El funcionamiento es como sigue: primero situamos una rejilla sobre la parte del cuarto
afectada. Puedes usar si quieres usar una rejilla fina (más lento) o más espaciada. Después, determinamos las celdas
de la rejilla ocupadas por objetos relevantes (usando colisión precisa o la caja de contorno) y marcamos estas celdas
como prohibidas. Así que una celda estará prohibida si parte de un obstáculo la está ocupando. Finalmente
especificamos la posición inicial y final, que deben estar en celdas libres de la rejilla y la función calcula el camino más
corto entre ellas. El camino irá de centro a centro de las celdas libres. Así que las celdas deben ser lo suficientemente
grandes como para que la instancia entre totalmente dentro de ellas. Ahora puedes asignar el path a una instancia y
Debes determinar la zona del cuarto sobre la que situar la rejilla y el tamaño de las celdas con la mayor precisión
posible. También debes decidir qué objetos deben tomarse en cuenta y si es necesaria la colisión precisa o no. Todos
En particular, el tamaño de las celdas es crucial. Recuerda que las celdas deben lo suficientemente grandes como para
que la instancia que se mueve entre totalmente dentro de ellas (ten cuidado con la posición del origen de la instancia y
recuerda que puedes mover el path para hacer que el centro del objeto coincida con el centro de la celda). Por otro
lado, cuanto menores sean las celdas más caminos diferentes podrás encontrar. Si haces las celdas demasiado grandes
puede que unos pocos objetos ocupen todas las celdas cerrando todos los caminos posibles.
mismo tiempo. left y top indican la posición de la esquina superior izquierda de la rejilla y hcells y
vcells indican el número de celdas horizontales y verticales respectivamente. Finalmente, cellwidth y
cellheight indican la anchura y altura de las celdas.
mp_grid_destroy(id) Destruye la rejilla indicada y libera la memoria usada. No olvides llamar a esta función
cuando no necesites usar más la rejilla.
mp_grid_add_instances(id,obj,prec) Marca todas las celdas que intersectan una instancia del
objeto indicado como prohibidas. También puedes especificar una id de una instancia concreta, o la palabra clave all
para indicar todas las instancias. prec indica si hay que usar colisión precisa (sólo funcionará si en el sprite de la
instancia está activada la misma opción).
nuevo path (consulta el capítulo sobre cómo crear y destruir paths). xstart e ystart indican el comienzo del
path y xgoal e ygoal las coordenadas de la meta. allowdiag indica si se permiten movimientos diagonales
entre celdas o sólo horizontales y verticales. La función devuelve si consiguió calcular un path (observa que el path es
mp_grid_draw(id) Esta función dibuja la rejilla marcando las celdas libres y prohibidas (muy útil para buscar
errores).
Detección de colisiones
Al planificar movimientos o decidir ciertas acciones es importante comprobar si ocurren colisiones con otras instancias
en otras posiciones. Las funciones siguientes se utilizan para esto. Todas ellas tienen 3 argumentos en común: el
argumento obj puede ser un objeto, la palabra clave all, o la id de una instancia. El argumento prec indica si se
debe usar colisión precisa o la caja de contorno de la instancia (la colisión precisa sólo funciona si el sprite de la
instancia tiene activada la misma opción). El argumento notme indica si no se debe tener en cuenta a la instancia que
llama a la función. Todas estas funciones devuelven la id de una de las instancias con las que se detecta colisión. Si no
rectángulo (sólido) con las esquinas indicadas e instancias del objeto obj. Por ejemplo, puedes usar esta función para
ver si un área está libre de obstáculos.
circunferencia (sólido) con centro (xc,yc) y radio r e instancias del objeto obj. Puedes usar esta función para ver si
un objeto está cerca de una posición.
elipse (sólida) con las esquinas indicadas e instancias del objeto obj.
collision_line(x1,y1,x2,y2,obj,prec,notme) Comprueba si hay una colisión entre la línea
que va de (x1,y1) a (x2,y2) e instancias del objeto obj. Esta función es muy poderosa. Puedes usarla para
comprobar si una instancia puede ver a otra chequeando si entre ellas hay una pared
Instancias
Las unidades básicas del juego son las instancias. Durante el juego, puedes cambiar varios aspectos de estas
instancias. También puedes crear o destruir instancias. Además de las variables de movimiento y las de dibujo cada
object_index* Índice del objeto del cual ésta es una instancia. No se puede cambiar.
id* La id única de la instancia (>= 100000) (Al definir cuartos la id de la instancia bajo el puntero del ratón es
indicada). No se puede cambiar.
mask_index Índice de l sprite usado como máscara para las colisiones. Si indicas -1 la máscara será igual al sprite
de la instancia.
sólo hay una instancia de un objeto puedes acceder a ella usando el nombre del objeto pero normalmente necesitas
conocer la id de la instancia. Este identificador único se puede usar en construcciones with y para identificar la
Observa que la asignación de las instancias al instance_id[] cambia en cada step, así que debes actualizar este
valor. Por ejemplo: imagina que cada unidad en tu juego tiene un poder y quieres encontrar la más poderosa de todas.
{
maxid = -1;
maxpower = 0;
for (i=0; i<instance_count; i+=1)
{
iii = instance_id[i];
if (iii.object_index == unit)
{
if (iii.power > maxpower)
{maxid = iii; maxpower = iii.power;}
}
}
}
Después del bucle maxid contendrá la id de la instancia más podersa (No destruyas instancias durante un bucle como
instance_find(obj,n) Devuelve la id de la instancia n+1 de tipo obj. obj puede ser un objeto o la
palabra clave all. Si no existe se devuelve el objeto especial noone. Recuerda que el orden de las instancias
cambia en cada step así que no puedes usar valores de steps anteriores.
instance_exists(obj) Devuelve si existe alguna instancia del objeto obj. obj puede ser un objeto, la id
de una instancia o la palabra clave all.
instance_number(obj) Devuelve el número de instancias de tipo obj. obj puede ser un objeto o la
palabra clave all.
instance_position(x,y,obj) Devuelve la id de la instancia de tipo obj en la posición (x,y). Cuando hay
varias instancias en esa posición se devuelve la id de la prtimera. obj puede ser un objeto o la palabra clave all. Si
no existe se devuelve el objeto especial noone
instance_nearest(x,y,obj) Devuelve la id de la instancia de tipo obj que esté más cercana en ese
momento a (x,y). obj puede ser un objeto o la palabra clave all.
instance_furthest(x,y,obj) Devuelve la id de la instancia de tipo obj que esté más lejana en ese
momento a (x,y). obj puede ser un objeto o la palabra clave all.
instance_place(x,y,obj) Devuelve la id de la instancia de tipo obj encontrada cuando la instancia actual
se coloca en la posición (x,y). obj puede ser un objeto o la palabra clave all. Si no existe se devuelve el objeto
especial noone.
position_destroy(x,y) Destruye toda las instancias cuyo sprite pasa por el punto (x,y).
position_change(x,y,obj,perf) Cambia todas las instancias en la posición indicada a otras del tipo
obj. perf indica si se deben ejecutar los eventos de destrucción y creación.
Desactivando instancias
Cuando creas un cuarto muy grande, por ejemplo en juegos de plataformas, con una vista (view) muy pequeña,
muchas instancias se quedan fuera de la vista. Aunque no sean visibles, estas instancias siguen ejecutando sus
eventos. También, al efectuar chequeos de colisión son tomadas en cuenta. Esto puede hacer que el juego se ralentice.
Para remediar esto, Game Maker contiene unas funciones para desactivar o activar instancias. Pero antes de usarlas
Cuando desactivas instancias es como si las eliminaras del juego. No son visibles, no ejecutan sus eventos,…así que
para todas las funciones y acciones estas instancias ya no existen y no son tomadas en cuenta. Así consigues que el
juego sea más rápido. Pero ten cuidado, ya que esto puede generar errores en tu juego. Por ejemplo, al eliminar todas
las instancias de un objeto, las instancias que estén desactivadas no serán eliminadas! Así, una llave que recoja el
jugador no podrá abrir una puerta que esté desactivada, por ejemplo.
El error más crucial que puedes hacer es el de desactivar la instancia que se encarga de activar las demás instancias.
Para evitar esto algunas funciones permiten especificar si la instancia que desactiva a las demás debe ser desactivada o
no.
dentro de la región indicada). Si inside es igual a false las instancias completamente fuera de la región son
desactivadas. Si notme es true la instancia actual no es desactivada (normalmente es lo que se desea).
instance_activate_all() Activa todas las instancias del cuarto.
instance_activate_object(obj) Activa todas las instancias en el cuarto del objeto especificado.
También puedes indicar all para activar todas las instancias o la id de una instancia concreta para activarla.
instance_activate_region(left,top,width,height,inside) Activa las instancias dentro
de la región especificada. Si inside es false las instancias fuera de la región son activadas.
Por ejemplo, para desactivar todas las instancias fuera de la vista y activar las que estén dentro podemos poner este
{
instance_activate_all();
instance_deactivate_region(view_xview[0],view_yview[0],
view_wview[0],view_hview[0],false,true);
}
Los buenos juegos requirieron de cuidado especial de los tiempos en que las cosas se llevaban a cabo (timing).
Afortunadamente el Game Maker se ocupa de la mayor parte del timing por ti. Se asegura de que las cosas ocurran con
un ritmo constante. Este ritmo es definido al definir los cuartos. Pero puedes cambiarlo usando la variable global
room_speed. Así por ejemplo, puedes incrementar lentamente la velocidad del juego, haciéndolo más difícil, agregando
una muy pequeña cantidad (como 0.001) a room_speed en cada step. Si tu máquina es lenta la velocidad del juego
pudiera no alcanzarse. Esto puede comprobarse usando la variable fps que monitorea constantemente el número actual
de cuadros por segundo. Finalmente, para un timing avanzado puedes usar la variable current_time que te da el
número de milisegundos desde que la computadora fue iniciada. Aquí está la colección completa de variables
room_speed Velocidad del juego en el cuarto actual (en steps por segundo).
fps* Número de cuadros que son dibujados por segundo.
current_time* Número de milisegundos que han pasado desde que el sistema fue iniciado.
current_year* El año actual.
current_month* El mes actual.
current_day* El día actual.
current_weekday* El día actual de la semana (1=domingo, …, 7=sábado).
current_hour* La hora actual.
current_minute* El minuto actual.
current_second* El segundo actual.
Algunas veces querrás detener el juego por un corto periodo. Para esto, usa la función sleep:
Como debes saber, cada instancia tiene 12 diferentes alarmas que puedes configurar. Para cambiar los valores (u
alarm[0..11] Valor de la alarma indicada. (Nota: ¡las alarmas solo se actualizan cuando el evento de alarma
para el objeto contiene acciones!)
Hemos visto que para los problemas de un timing complejo puedes usar el recurso de las líneas de tiempo (time lines).
Cada instancia puede tener un recurso time line asociado con ella. Las siguientes variables están relacionadas con esto:
timeline_position Posición actual dentro de la time line. Puedes cambiarla para saltar o repetir ciertas
partes.
timeline_speed Normalmente, en cada step la posición en la time line se incrementa en 1. Puedes cambiar esta
cantidad configurando esta variable a un valor diferente. Puedes usar números reales, por ejemplo 0.5. Si el valor es
mayor que uno, varios momentos pueden ocurrir dentro del mismo tiempo del step. Se realizarán en el orden correcto,
Rooms
Los juegos funcionan en cuartos. Cada cuarto tiene un índice que se indica por el nombre del cuarto. El cuarto actual es
almacenado en la variable room. No puedes asumir que los cuartos están numerados en un orden consecutivo. Por lo
que nunca sumes o restes un número de la variable room. En lugar de ello usa las funciones y variables indicadas
abajo. Por lo que una típica pieza de código que usarás sería:
{
if (room != room_last)
{
room_goto_next();
}
else
{
game_end();
}
}
room Índice del cuarto actual; puede cambiarse para ir a un cuarto diferente, pero mejor usa las rutinas listadas
abajo.
room_previous(numb)Devuelve el índice del cuarto anterior a numb (-1 = ninguno) pero no va a él.
Muchos juegos ofrecen al jugador la posibilidad de guardar el juego y cargar un juego guardado. En el Game Maker
esto ocurre automáticamente cuando el jugador presiona <F5> para guardar y <F6> para cargar. También puedes
guardar y cargar juegos desde una pieza de código (nota que la carga sólo se lleva a cabo al final del step actual).
Ten en cuenta que sólo los datos básicos del juego son guardados. Por ejemplo, si guardas cuando una canción está
sonando, al cargar el juego la canción no sonará desde ese momento. Los recursos editados tampoco son guardados, ni
Score
Otro aspecto importante de muchos juegos es el score (marcador), la energía, y el número de vidas. El Game Maker
mantiene el score en la variable global score y el número de vidas en la variable global lives. Puedes
cambiar el score simplemente cambiado el valor de esta variable. Lo mismo se aplica para la energía y las vidas. Si la
variable lives es mayor que 0 y se vuelve menor o igual a 0 se ejecuta el evento no-more-lives para todas las
instancias. Si no quieres mostrar el score y las vidas en el título, pon la variable show_score, etc., a falso.
También puedes cambiar el título del marcador, de las vidas o de la energía. Para juegos más complicados mejor
Generando eventos
Como sabes, el Game Maker está completamente manejado por eventos. Todas las acciones ocurren como resultado de
eventos. Hay una gran cantidad de eventos diferentes. Los eventos de creación y destrucción ocurren cuando una
instancia es creada o destruida. En cada step, el sistema maneja primero los eventos de alarma. Después los eventos
de teclado y ratón, y luego el siguiente evento step. Después de esto las instancias son colocadas en su nueva posición
después de lo cual se maneja el evento de colisión. Finalmente el evento draw se usa para dibujar las instancias (nota
que cuando empleas múltiples vistas el evento draw es llamado varias veces en cada step). También puedes aplicar un
evento a la instancia actual desde una pieza de código. Se tienen las siguientes funciones:
event_perform(type,numb) Realiza el evento numb del tipo type para la instancia actual. Se pueden
emplear los siguientes tipos de eventos:
ev_create
ev_destroy
ev_step
ev_alarm
ev_keyboard
ev_mouse
ev_collision
ev_other
ev_draw
ev_keypress
ev_keyrelease
Cuando hay varios eventos del tipo dado, numb puede usarse para especificar el evento preciso. Para el evento de
alarma numb puede tener un valor de 0 a 11. Para el evento de teclado puedes usar el código de tecla para la tecla.
Para los eventos de ratón puedes usar las siguientes constantes:
ev_left_button
ev_right_button
ev_middle_button
ev_no_button
ev_left_press
ev_right_press
ev_middle_press
ev_left_release
ev_right_release
ev_middle_release
ev_mouse_enter
ev_mouse_leave
ev_mouse_wheel_up
ev_mouse_wheel_down
ev_global_left_button
ev_global_right_button
ev_global_middle_button
ev_global_left_press
ev_global_right_press
ev_global_middle_press
ev_global_left_release
ev_global_right_release
ev_global_middle_release
ev_joystick1_left
ev_joystick1_right
ev_joystick1_up
ev_joystick1_down
ev_joystick1_button1
ev_joystick1_button2
ev_joystick1_button3
ev_joystick1_button4
ev_joystick1_button5
ev_joystick1_button6
ev_joystick1_button7
ev_joystick1_button8
ev_joystick2_left
ev_joystick2_right
ev_joystick2_up
ev_joystick2_down
ev_joystick2_button1
ev_joystick2_button2
ev_joystick2_button3
ev_joystick2_button4
ev_joystick2_button5
ev_joystick2_button6
ev_joystick2_button7
ev_joystick2_button8
Para el evento de collision proporcionas el índice del otro objeto. Finalmente, para el evento other puedes usar las
siguientes constantes:
ev_outside
ev_boundary
ev_game_start
ev_game_end
ev_room_start
ev_room_end
ev_no_more_lives
ev_no_more_health
ev_animation_end
ev_end_of_path
ev_user0
ev_user1
ev_user2
ev_user3
ev_user4
ev_user5
ev_user6
ev_user7
ev_user8
ev_user9
ev_user10
ev_user11
ev_user12
ev_user13
ev_user14
ev_user15
Para el evento step puedes dar el índice usando las siguientes constantes:
ev_step_normal
ev_step_begin
ev_step_end
event_perform_object(obj,type,numb) Esta función funciona igual que la anterior pero esta vez
puedes especificar eventos en otro objeto. Nota que las acciones en estos eventos se aplican a la instancia actual, no a
event_user(numb) En los eventos other también puedes definir 16 eventos definidos por el usuario. Estos son
ejecutados solo si llamas esta función. Numb debe tener valores de 0 a 11.
event_inherited() Ejecuta el evento heredado. Esto sólo funciona si la instancia tiene un objeto padre.
Puedes obtener información sobre el evento actualmente ejecutado usando las siguientes variables de sólo lectura:
Aquí puedes ver algunas variables y funciones que se refieren a los errores.
Las siguientes funciones te permiten saber si ciertas variables existen, darles un valor o leerlo. En todas ellas el nombre
{
if variable_global_exists('ammunition')
global.ammunition += 1
else
global.ammunition = 0
}
También puees usar estas funciones para pasar variables a los scripts usando referencias, utilizando el nombre de las
computación. -2 y -1 son valores por debajo de lo normal, así que otros procesos tendrán prioridad sobre el juego. 0 es
el valor normal. +1 y +2 son valores de alta prioridad, que pueden hacer que el juego se ejecute más suavemente y a
mayor velocidad. Otros procesos tendrán menos tiempo de computación. +3 indica ejecución en tiempo real: todo el
tiempo de computación se pone en disposición del juego. Esto puede crear problemas con otras aplicaciones que se
estén ejecutando en el ordenador. Además, los eventos de teclado o pinchar en el botón para cerrar la ventana pueden
dejar de ser atendidos por Windows. Así que sólo debes usar este valor si realmente necesitas todo el procesamiento
No hay juego sin interacción con el usuario. La manera estándar de interactuar con el usuario en el Game Maker es
colocando acciones en los eventos del ratón o del teclado. Pero en ocasiones se necesita más control. Desde una pieza
de código puedes chequear la posición del ratón o si alguno de sus botones es presionado. Normalmente esto se
chequea en el evento step de algún objeto controlador y llevas a cabo las acciones adecuadas.
El teclado
Para interacción con el teclado (keyboard) las siguientes funciones y variables existen:
keyboard_lastkey Código de la última tecla presionada. Mira más las constantes para los códigos de tecla.
Puedes cambiarlo, p. ej, ponerlo a 0 si tu lo manipulaste.
keyboard_key Código de tecla de la tecla presionada actualmente (mira mas abajo; 0 si no se presiona ninguna)
keyboard_lastchar Último carácter presionado (como string)
keyboard_string Cadena de caracteres de los últimos 1024 caracteres tipeados. Esta cadena solo contendrá
caracteres imprimibles en la pantalla. También responde a la tecla de retroceso borrando el último carácter.
En ocasiones es útil mapear una tecla a otra. Por ejemplo pudieras permitir al jugador emplear tanto las teclas del
cursor como las del teclado numérico. En lugar de duplicar las acciones puedes mapear el teclado numérico a las teclas
del cursor. También pudieras implementar un mecanismo en el que el jugador pueda seleccionar las teclas a usar. Para
Para chequear si una tecla o botón del ratón en particular han sido presionados puedes emplear las siguientes
más. En este caso puedes emplear los códigos vk_lshift, vk_lcontrol, vk_lalt, vk_rshift, vk_rcontrol y vk_ralt para
Las siguientes rutinas puedes ser usadas para manipular el estado del teclado:
keyboard_get_numlock()Indica si BloqNum está activada.
keyboard_set_numlock(on) Activa (on=true) o desactiva (on=false)
BloqNum.
keyboard_check_direct:
Por ejemplo, asumiendo que tienes un objeto que el usuario puede controlar con las teclas del cursor puedes colocar el
{
if (keyboard_check(vk_left)) x -= 4;
if (keyboard_check(vk_right)) x += 4;
if (keyboard_check(vk_up)) y -= 4;
if (keyboard_check(vk_down)) y += 4;
}
Claro, esto es mucho más fácil si simplemente lo ponemos en los eventos del teclado.
keyboard_clear(key) ‘Limpia’ el estado de la tecla mencionada en “key”. Esto significa que no generará
eventos de teclado hasta que se vuelva a presionar.
El ratón
Para chequear si un botón particular del ratón se presionó puedes usar estas funciones. Esto es muy útil cuando
mouse_check_button(numb) Indica si se presiona el botón del ratón numb (como valores de numb emplea
mb_none, mb_left, mb_middle, o mb_right).
mouse_check_button_pressed(numb) Indica si el botón del ratón fue presionado desde el ultimo step.
mouse_check_button_released(numb) Indica si el botón del ratón se soltó desde el último step.
mouse_clear(button) “limpia” el estado del botón del Mouse. Esto significa que no se generarán mas eventos
del ratón hasta que se vuelva a presionar otra vez.
El joystick
Tenemos algunos eventos asociados con los joysticks (mandos de control, controles, palancas de mando, palancas de
juego, etc.) Pero para tener control total sobre los joysticks hay un grupo de funciones para tratarlos. El Game Maker
soporta hasta dos joystick. Por lo que todas estas funciones reciben el id del joystick como argumento.
Una parte importante de un juego son los gráficos. Game Maker normalmente toma el control de casi todo y en juegos
sencillos no hay necesidad de preocuparse. Pero a veces necesitas tomar mayor control sobre ellos. Para algunos
aspectos hay acciones, pero mediante código puedes controlar muchos más aspectos. Este capítulo muestra todas las
variables y funciones disponibles para esto y da más información sobre lo que está sucediendo realmente.
Imágenes y sprites
Cada objeto tiene un sprite asociado. Puede ser una imagen simple o compuesta por varias subimágenes. Para cada
instancia del objeto, el programa dibuja la imagen correspondiente en la pantalla, con su origen (definido en las
propiedades de sprite) en la posición (x,y) de la instancia. Cuando hay varias subimágenes, ésta se reproduce a través
de las subimágenes para crear un efecto de animación. Hay varias variables que afectan en el modo en que se dibuja la
imagen. Estas pueden sert utilizadas para cambiar los efectos. Cada instancia posee las siguientes variables:
visible : Si visible es cierto (1) la imagen se dibuja, en caso contrario no se dibuja. Las instancias invisibles aun
son activas y crean eventos de colisión; Simplemente no puedes verlas. Definir la visibilidad como falso es útil para por
ejemplo objetos controladores (hazlos también no sólidos para evitar eventos de colisión con ellos) o palancas ocultas.
sprite_index Este es el índice del sprite actual para la instancia. Puedes cambiarlo para asignar un sprite
diferente a la instancia. Como valor puedes usar los nombres de los diferentes sprites que definas . Cambiando el
sprite_width* Indica el ancho del sprite. Este valor no puede ser cambiado, pero puedes utilizarlo.
sprite_height* Indica el alto del sprite. Este valor no puede ser cambiado, pero puedes utilizarlo.
sprite_xoffset* Indica el offset horizontal del sprite, como esta definido en las propiedades del sprite. Este
valor no puede ser cambiado, pero puedes utilizarlo.
sprite_yoffset* Indica el offset horizontal del sprite, como esta definido en las propiedades del sprite. Este
valor no puede ser cambiado, pero puedes utilizarlo.
image_number* El numero de subimágenes del sprite actual de la instancia (no puede ser cambiado).
image_index Cuando la imagen tiene varias subimágenes, el programa las recorre cíclicamente. Esta variable
indica la subimagen dibujada actualmente (están numeradas desde 0). Puedes cambiar la imagen actual cambiando
esta variable. EL programa continuará reproduciendo el ciclo de subimágenes, pero comenzando desde el nuevo índice.
(El valor puede ser fraccional. En este caso, se redondea siempre hacia abajo para obtener el índice de la subimagen
dibujada.
image_speed La velocidad con que se reproduce el ciclo de subimágenes. Un valor de 1 indica que en cada paso
se muestra la siguiente subimagen . Valores menores, reducirán la velocidad de visualización de la animación,
dibujando la misma subimagen varias veces. Valores mayores harán que se salten subimágenes para aumentar la
velocidad de la animación. A veces quieres que una subimagen en particular sea visible y no quieres que el programa
muestre todas las demás subimágenes. Puedes conseguir esto definiendo la velocidad igual a 0 y eligiendo la
subimagen correcta. Por ejemplo, asumamos que tenemos un objeto que puede rotar y has creado un sprite que tiene
varias subimágenes para las diferentes orientaciones (en sentido contrario a las agujas del reloj) Puedes introducir el
{
image_index = direction * image_number/360;
image_speed = 0;
}
depth Normalmente las imágenes son dibujadas en el orden en que se crean las instancias. Puedes cambiar este
orden cambiando la profundidad de la imagen. El valor por defecto es 0, hasta que le introduzcas un valor diferente en
las propiedades del objeto. Cuanto mayor es el valor más al fondo se dibujará la instancia. (También puedes utilizar
valores negativos.) Las instancias con mayor profundidad permanecerán debajo de las instancias con menor
profundidad. Definiendo la profundidad garantizamos que las instancias son dibujadas en el orden que queremos. (Ej. El
avión sobre la nube). Las instancias de fondo tendrán una alta profundidad (valor positivo) y las imágenes frontales
image_xscale Un valor de escala para hacer mayores o menores las imágenes. Un valor de 1 indica el tamaño
normal. Debes separar la escala horizontal xscale y la vertical yscale. Cambiando la escala también cambian el ancho y
el alto de la imagen e influye en los eventos de collisión, como podrías esperar. Cambiar la escala puede ser utilizado
para dar un efecto 3-D. Puedes utilizar un valor de -1 para invertir horizontalmente la imagen.
image_yscale Escala vertical yscale. 1 no modifica el tamaño. Puedes utilizar un valor de -1 para invertir
verticalmente la imagen.
image_angle El ángulo con que se rota la imagen. Se especifica en grados en sentido contrario a las agujas del
reloj. Un valor de 0 indica que no hay rotación. Esta variable solo puede modificarse en la versión registrada!
image_alpha El valor de transparencia (alpha) que se aplica al dibujar la imagen. Un valor de 1 es la opacidad
normal; un valor de 0 es totalmente transparente.
image_blend Teñido de color que se aplica al dibujar la imagen. Un valor de color blanco (c_white) es el que se
utiliza por defecto. Cuando especificas un color diferente, la imagen se tiñe de dicho color. Esto puede ser utilizado para
colorear el personaje en tiempo de ejecución. Esta variable solo puede modificarse en la versión registrada!
bbox_left* Lado izquierdo de la caja de contorno utilizada por la imagen de la instancia (Se toma en cuenta el
escalado).
Fondos
Cada room (cuarto) puede tener hasta 8 fondos. Así mismo, también puede tener un color de fondo. Todos los aspecto
de estos fondos se pueden cambiar con código usando las siguientes variables (observa que algunas son vectores con
background_foreground[0..7] Indica si el fondo está en primer plano (se dibujará encima de todo lo
demás, tapando todo lo que quede por debajo).
background_yscale[0..7] Factor de escalado vertical del fondo: un número entre 0 y 1 hará la imagen
más pequeña y un número mayor que 1 la hará más grande. (Debe ser un valor positivo)
background_hspeed[0..7] Velocidad horizontal de scrolling del fondo, en píxeles por step. El scrolling es el
movimiento del fondo, por lo tanto esto se refiere al movimiento horizontal del fondo.
background_vspeed[0..7] Velocidad vertical de scrolling del fondo, en píxeles por step. El scrolling es el
movimiento del fondo, por lo tanto esto se refiere al movimiento vertical del fondo.
background_blend[0..7] Color de teñido usado al dibujar el fondo. El valor por defecto es c_white. Esta
variable sólo se puede usar en la versión registrada!
transparentes).
Dibujando sprites y fondos
Normalmente los objetos tienen un sprite asociado que se dibuja en la pantalla. Pero también puedes usar el evento
draw. para dibujar otras cosas. Esta sección y las dos siguientes te darán información sobre este aspecto. Primero de
todo, hay un grupo de funciones que sirven para dibujar sprites y fondos de distintas maneras. Estas funciones te dan
un gran control sobre la apariencia gráfica del juego y te permiten hacer cosas como dibujar sólo un trozo de un fondo,
rotar un sprite...
con índice sprite de forma que llene la región con esquina superior-izquierda en (x,y), anchura w y altura h.
draw_sprite_tiled(sprite,subimg,x,y) Dibuja el sprite repitiéndolo horizontal y verticalmente de
forma que llene toda la pantalla. (x,y) es la posición donde se dibuja una de las copias del sprite.
left y top (coordenadas de la esquina superior-izquierda) y width y height (anchura y altura del trozo que
queremos dibujar).
draw_background(back,x,y) Dibuja el fondo en la posición (x,y), sin teñirlo de ningún color y sin
transparencia.
Las funciones siguientes son versiones extendidas de las funciones anteriores. Estas funciones sólo están
draw_sprite_ext(sprite,subimg,x,y,xscale,yscale,rot,color,alpha) Dibuja el
sprite escalado con factores xscale (horizontal) e yscale (vertical) y rotado rot grados en sentido
antihorario. color indica el color con el que queremos teñir el sprite (usa el color blanco c_white si no quieres teñir
el sprite de ningún color) y alpha indica el factor de transparencia. Un valor igual a 0 hace la imagen totalmente
transparente y un valor igual a 1 la hace totalmente opaca. Con valores intermedios conseguirás que la imagen sea
parcialmente transparente. Con esta función se pueden crear efectos muy espectaculares como explosiones semi-
transparentes.
draw_sprite_part_ext(sprite,subimg,left,top,width,height,x,y,xscale,ys
cale,color,alpha) Dibuja la parte indicada del sprite con factores de escala, color de teñido y factor de
transparencia.
draw_sprite_general(sprite,subimg,left,top,width,height,x,y,xscale,ysc
ale,rot,c1,c2,c3,c4,alpha) La función más general para dibujar sprites. Dibuja la parte indicada de la
subimagen subimg (-1 = subimagen actual) del sprite con índice sprite situando su esquina superior-izquierda en la
posición (x,y) y con factores de escala, ángulo de rotación, un color de teñido para cada una de las 4 esquinas del
escalado, rotado, teñido del color especificado (usa c_white si no quieres teñirlo) y con factor de transparencia
alpha (0-1).
draw_background_stretched_ext(back,x,y,w,h,color,alpha) Dibuja el fondo escalado
de forma que ocupe la región indicada con color de teñido y factor de transparencia.
draw_background_part_ext(back,left,top,width,height,x,y,xscale,yscale,
color,alpha) Dibuja la parte indicada del fondo situando su esquina superior-izquierda en la posición (x,y) con
factores de escala, color de teñido y factor de transparencia.
draw_background_general(back,left,top,width,height,x,y,xscale,yscale,r
ot,c1,c2,c3,c4,alpha) La función más general para dibujar fondos. Dibuja la parte indicada del fondo
situando su esquina superior-izquierda en la posición (x,y) con factores de escala, ángulo de rotación, un color de
teñido para cada una de las cuatro esquinas (en el orden arriba-izquierda, arriba-derecha, abajo-derecha y abajo-
izquierda) y un factor de transparencia. Observa que el sprite se rotará sobre su esquina superior-izquierda del trozo
Dibujando formas
Game Maker dispone de una amplia colección de funciones para dibujar diferentes formas. También hay otras funciones
para dibujar texto (consulta el capítulo siguiente). Estas funciones de dibujo sólo se pueden usar en el evento draw ya
que no tienen sentido si se colocan en otro evento. Debes tener en cuenta que las colisiones entre las instancias se
determinan según sus sprites y sus máscaras de colisión y no según lo que cada instancia dibuja en la pantalla. Las
draw_arrow(x1,y1,x2,y2,size) Dibuja una flecha desde (x1,y1) hasta (x2,y2). size indica el tamaño
de la flecha en píxeles.
draw_healthbar(x1,y1,x2,y2,amount,backcol,mincol,maxcol,direction,show
back,showborder) Con esta función puedes dibujar una barra de vida (o una barra que indique cualquier otra
cosa como poder, magia,…). Los parámetros x1, y1, x2 e y2 indican el área total de la barra. amount indica el
porcentaje de la barra que debe estar relleno (debe estar entre 0 y 100). backcol es el color de fondo de la barra.
mincol y maxcol indican el color cuando el porcentaje de llenado (parámetro amount) es 0 y 100
respectivamente. Para un porcentaje intermedio el color se interpola entre estos dos. Así puedes crear fácilmente una
barra que vaya de verde a rojo, por ejemplo. direction es la dirección en la que la barra se dibuja: 0 significa que
la barra está fijada a la izquierda, 1 a la derecha, 2 arriba y 3 abajo. Finalmente, showback indica si debe mostrarse
una caja de fondo y showborder si la caja de fondo y la barra deben tener un borde negro.
Muchas de las funciones anteriores utilizan los colores y transparencia generales de dibujo que pueden ser cambiados
c_aqua
c_black
c_blue
c_dkgray
c_fuchsia
c_gray
c_green
c_lime
c_ltgray
c_maroon
c_navy
c_olive
c_purple
c_red
c_silver
c_teal
c_white
c_yellow
Sin embargo, también puedes usar las funciones siguientes para crear tus propios colores:
screen_save(fname) Salva una imagen bmp de la pantalla en el archive especificado. Útil para crear
screenshots.
Fuentes y texto
A veces necesitas dibujar texto. Para ello, primero debes indicar la fuente que quieres usar. Las fuentes se pueden
definir creando un recurso de fuente (directamente en Game Maker o usando las funciones de modificación de
recursos). Para dibujar texto disponemos de varias funciones distintas. En todas ellas debes indicar el texto a ser
dibujado y la posición del mismo. Además, existen dos funciones para definir la alineación del texto en horizontal y
draw_set_font(font) Indica la fuente que debe usarse para dibujar texto. Puedes indicar el valor -1 para
usar la fuente por defecto (Arial 12).
draw_set_halign(halign) Indica la alineación horizontal del texto. Debes indicar uno de los siguientes
valores:
fa_left izquierda
fa_center centrada
fa_right derecha
draw_set_valign(valign) Indica la alineación vertical del texto. Debes indicar uno de los siguientes
valores:
fa_top arriba
fa_middle centrada
fa_bottom abajo
como caracteres de salto de línea. De esta forma puedes dibujar textos de varias líneas (Usa \# para dibujar el símbolo
#).
string_width(string) Anchura que el texto indicado ocuparía si se dibujara con la fuente actual usando la
función draw_text(). Puedes usar esta función para posicionar los gráficos con precisión.
string_height(string) Altura que el texto indicado ocuparía si se dibujara con la fuente actual usando la
función draw_text(). Puedes usar esta función para posicionar los gráficos con precisión.
string_width_ext(string,sep,w) Anchura que el texto indicado ocuparía si se dibujara con la fuente
actual usando la función draw_text_ext(). Puedes usar esta función para posicionar los gráficos con precisión.
string_height_ext(string,sep,w) Altura que el texto indicado ocuparía si se dibujara con la fuente
actual usando la función draw_text_ext(). Puedes usar esta función para posicionar los gráficos con precisión.
Las siguientes funciones te permiten dibujar texto escalado o rotado e incluso aplicarle gradientes de color. Estas
En los capítulos anteriores hemos descrito las funciones básicas de dibujo. En este capítulo veremos funciones más
avanzadas que te otorgan muchas más posibilidades para crear los gráficos de tus juegos. Primero, hay unas funciones
que sirven para dibujar formas coloreadas con gradientes o efectos de difuminado. Después veremos las funciones que
sirven para dibujar polígonos básicos y por último veremos la forma de dibujar polígonos texturizados.
Las siguientes funciones son versiones extendidas de las funciones de dibujo básicas. Todas ellas admiten parámetros
para el color, por lo que ninguna de estas funciones usará el color general de dibujo.
e inferior-izquierdo respectivamente. outline indica si sólo debe dibujarse el contorno del rectángulo (true) o si
éste debe estar relleno (false).
draw_triangle_color(x1,y1,x2,y2,x3,y3,col1,col2,col3,outline) Dibuja un
triángulo. Los tres colores son los colores en los vértices del triángulo (el color se interpola en las demás zonas entre
estos tres colores). outline indica si sólo debe dibujarse el contorno del triángulo (true) o si éste debe estar relleno
(false).
Dibujando superficies
Esta funcionalidad sólo está disponible en la versión registrada de Game Maker
En algunas situaciones puede que no quieras dibujar directamente en la pantalla sino en un lienzo que más tarde
puedas usar para dibujar otras cosas. Por ejemplo, puedes querer que el usuario dibuje en la pantalla. En vez de dejar
que el usuario dibuje directamente en la pantalla (lo que no funcionaría, ya que la pantalla se refresca en cada step)
puedes hacer que dibuje en un lienzo a parte y copiar este lienzo a la pantalla en cada paso. También podrías querer
usar una textura que cambie con el tiempo, crear un efecto de transiciones entre cuartos, efectos de iluminación…
Las superficies hacen posibles cosas como éstas. Las superficies son muy sencillas de usar: primero creas una
superficie indicando su tamaño. Luego indicas que vas a dibujar sobre la superficie. Desde este momento, todas las
funciones de dibujo se aplicarán a la superficie. Cuando termines de dibujar, indicas que ya no vas a dibujar más en la
superficie. Ya tienes lista la superficie, ahora puedes dibujarla directamente en la pantalla, usarla como
textura…Consulta el final del capítulo para conocer algunos detalles con los que debes tener cuidado al trabajar con
superficies.
surface_create(w,h) Crea una superficie con la anchura y altura indicadas. La función devuelve la id de la
superficie que debe ser usada en las demás funciones. Observa que la superficie no se “limpia” (es decir, que no se
pinta toda ella de un mismo color). Para esto, debes indicar que vas a dibujar en la superficie y llamar a la función
surface_reset_target() Vuelve a fijar la pantalla como objetivo de dibujo. Es decir, las funciones de
dibujo que se llamen a partir de ahora funcionarán normalmente ¡No olvides llamar a esta función cuando termines de
surface_getpixel(id,x,y) Devuelve el color del píxel en la posición (x,y) de la superficie. Esta función no
es muy rápida así que úsala con moderación.
surface_save(id,fname) Guarda una imagen bmp de la superficie, creando para ello el archivo con el
nombre indicado. Se puede usar para crear screenshots, por ejemplo.
surface_save_part(id,fname,x,y,w,h) Igual que la función anterior, pero esta vez sólo se copiará
la parte de la superficie que indiques.
draw_surface_tiled(id,x,y) Dibuja la superficie en la posición (x,y) y la repite una y otra vez para que
ocupe todo el cuarto.
draw_surface_part_ext(id,left,top,width,height,x,y,xscale,yscale,color
,alpha) Dibuja la parte indicada de la superficie en la posición (x,y) pero con factores de escala, transparencia y
color.
draw_surface_general(id,left,top,width,height,x,y,xscale,yscale,rot,c1
,c2,c3,c4,alpha) La función más general de dibujo. Dibuja la parte indicada de la superficie con su origen en
la posición (x,y), con factores de escala, transparencia y rotada rot grados en sentido antihorario. Además debes
indicar 4 colores para cada una de las esquinas de la superficie en este orden: esquina superior izquierda, esquina
Observa que no hay funciones para copiar parte de la pantalla a una superficie. Esto es imposible debido a la diferencia
de formato entre la pantalla y las superficies. Si necesitaras usar esto puedes indicar la superficie como objetivo de
dibujo y dibujar todo el room. Luego usando las funciones para copiar superficies puedes copiar partes de ella a la
pantalla.
También puedes crear sprites y fondos de superficies. Esto se explica con más detalle en la sección de modificación de
recursos.
• Nunca debes cambiar el objetivo de dibujo mientras dibujas estás dibujando sobre la pantalla. Es decir, nunca
• Las superficies no funcionan correctamente en modo 3D. Puedes usarlas mientras no estés en modo 3D
(llamando a la función d3d_end() antes de usarlas) pero una vez que empieces a usar el modo 3D de nuevo
• Por motivos de velocidad, las superficies se mantienen en la memoria de video constantemente. Como
resultado de esto, podrías perder las superficies al cambiar la resolución de la pantalla o cuando salte el
salvapantallas.
Tiles
Como ya debes saber, puedes añadir tiles a los cuartos. Una tile es una parte de un fondo. En realidad, una tile es
simplemente una imagen visible: no reaccionan ante eventos y no generan colisiones. Como resultado de esto, las tiles
funcionan mucho más rápido que los objetos. Así pues, todo lo que no sufra colisiones o no reaccione ante eventos
funcionará mucho mejor si lo haces con tiles. Incluso a veces es mejor utilizar un tile para mostrar los gráficos y poner
por debajo un objeto invisible que se encargue de las colisiones. Para definir un tile necesitas un fondo. Sobre este
fondo indicas la esquina superior (top) izquierda (left), la anchura (width) y la altura (height) de un rectángulo. La
parte del fondo que queda dentro de este rectángulo pasa a ser una tile.
Puedes añadir tiles al diseñar el room, pero también puedes hacerlo mientras el juego se ejecuta. Puedes cambiar su
posición, escalarlas o hacerlas transparentes. Una tile tiene las propiedades siguientes:
Para cambiar las propiedades de una tile debes conocer su id. Cuando creas tiles desde el editor de rooms la id se
muestra en la barra inferior de información. Pero también existen funciones para conocer la id de una tile en una
posición particular.
funciones.
Las funciones siguientes sirven para manipular las propiedades de las tiles:
Las funciones siguientes manejan capas de tiles, es decir, grupos de tiles que tienen la misma profundidad:
El display
El display es todo el área del monitor. Tiene varias características como el tamaño (típicamente 1024x768 ó
1280x1024), la profundidad de color (que es el número de bits que se utilizan para representar 1 píxel) y suele ser de
16 bits (color de alta densidad) ó 32 bits (color verdadero) y la frecuencia de refresco, que es el número de veces por
segundo que se refresca el display, es decir, cuántas veces por segundo se vuelven a dibujar los píxeles en la pantalla
(típicamente entre 60 y 120). Estos parámetros se pueden cambiar desde las propiedades de la pantalla en Windows.
Pero para algunos juegos, sobre todo los que se ejecuten en pantalla completa, es importante poder controlar estos
aspectos. Todos ellos se pueden inicializar desde la pestaña Game Settings. Para cambiar estos parámetros durante el
juego Game Maker dispone de un grupo de funciones que veremos a continuación. Debes tener en cuenta que al
cambiar estos parámetros el sistema necesitará un poco de tiempo para volver a ajustarlo todo. Las funciones para
mismo valor para lograr animaciones fluidas de 60 frames por segundo. La función devuelve true si los cambios se
no quieras cambiar utiliza -1. La función devuelve true si los cambios se aplicaron con éxito y false en caso contrario.
permitidos. La función no aplica los valores al display, sólo chequea si son válidos. Para los valores que no quieras
cambiar usa el valor -1. La función devuelve true si los cambios se pueden aplicar y false en caso contrario.
display_reset() Resetea las características del display a los originales, es decir, a los que estaban presentes
cuando se inició el programa.
A veces es útil poder conocer la posición del puntero en el display o poder cambiarla. Para ello se usan las funciones
siguientes:
La ventana
El juego se ejecuta en una ventana. Esta ventana tiene varias propiedades como si tiene borde, si ocupa toda la
pantalla...Normalmente estos valores se fijan desde la opción Game Settings. Pero también puedes cambiarlos
room en lugar de dejar que Game Maker lo haga automáticamente. El título de la ventana sólo es visible cuando ésta
cr_default
cr_none
cr_arrow
cr_cross
cr_beam
cr_size_nesw
cr_size_ns
cr_size_nwse
cr_size_we
cr_uparrow
cr_hourglass
cr_drag
cr_nodrop
cr_hsplit
cr_vsplit
cr_multidrag
cr_sqlwait
cr_no
cr_appstart
cr_help
cr_handpoint
cr_size_all
Por ejemplo, para hacer que no se vea el cursor o puntero usa window_set_cursor(cr_none).
para que ocupe toda la ventana o una parte concreta. Si usamos el valor 1 el cuarto no será escalado. Si usamos el
valor 0 el cuarto se escalará para ocupar toda la ventana. Si usas un valor negativo el cuarto será escalado al máximo
valor dentro de la ventana que le permita seguir conservando su relación de aspecto entre altura y anchura. El segundo
parámetro, adaptwindow, indica si el tamaño de la ventana debe adaptarse al cuarto si éste, una vez escalado, no
entra por completo dentro de la misma. Esto sólo suele ser útil cuando el primer parámetro (el que indica el escalado
La ventana tiene una posición en la pantalla y un tamaño. Cuando hablamos de posición y tamaño siempre nos
referimos a la ventana sin los bordes. Puedes cambiar estos valores aunque raramente querrás utilizarlo desde el
juego. Normalmente, se determinan automáticamente o el jugador escoge los que le interesan. Las funciones
siguientes te permiten cambiar estos aspectos de las ventanas. Observa que estas funciones sólo funcionarán si la
ventana no está a pantalla completa. Si la ventana está a pantalla completa los cambios no se aplicarán hasta que la
él.
window_set_rectangle(x,y,w,h) Indica la nueva posición y medidas de la ventana (es como usar las
dos funciones anteriores a la vez).
automáticamente.
En algunas ocasiones puede que quieras conocer la posición del ratón respecto a la ventana (normalmente se usa la
posición del ratón respecto a un cuarto o a una vista). Las siguientes funciones sirven para esto:
Vistas
Como ya deberías saber puedes definir hasta 8 vistas diferentes al diseñar un cuarto. Una vista se define por su área en
el cuarto y por su puerto o viewport en la pantalla (la región de dibujo dentro de la ventana). Usando vistas puedes
mostrar diferentes partes del cuarto en diferentes lugares de la pantalla. Además, puedes asegurar que un objeto
Puedes controlar las vistas con código. Puedes hacerlas visibles o invisibles, cambiar su posición o tamaño dentro de la
región de dibujo o en el cuarto (lo cual es muy útil cuando no indicas ningún objeto que deba ser seguido por la vista).
Puedes cambiar el tamaño de los bordes horizontal y vertical alrededor del objeto a ser seguido. Si el objeto que
quieres que sea seguido cambia a otro tipo de objeto durante el juego (por ejemplo, si acumula mucha experiencia y
evoluciona en un personaje más poderoso) puede que las vistas siguientes no lo sigan correctamente. Para solucionar
esto, puedes indicar este código en el evento de creación de todos los objetos del personaje (en este ejemplo se
{
view_object[0] = object_index;
}
Las siguientes variables actúan sobre las propiedades de las vistas. Todas, excepto las dos primeras, son vectores con
8 elementos: el primer elemento (0) indica la primera vista y el último elemento (7) la última.
Observa que el tamaño de la imagen en la pantalla se calcula a partir de las vistas visible al comienzo del cuarto. Si
cambias las vistas durante el juego puede que ya no entren correctamente en la pantalla. Para solucionar esto, puedes
ella. La ventana siempre se adaptará si usas escalado fijo (Consulta la función window_set_region_scale() en el
capítulo La ventana).
A veces necesitarás conocer la posición del ratón. Normalmente puedes hacer esto con las variables mouse_x y
mouse_y. Cuando hay muchas vistas, estas variables dan la posición del ratón respecto a la vista en la que el ratón se
encuentra. Pero puede ser que en alguna ocasión necesites conocer la posición del ratón respecto a una vista concreta,
incluso cuando el ratón se encuentre fuera de esa vista. Para hacer esto, puedes usar las siguientes funciones:
window_views_mouse_set(x,y) Indica la posición del ratón respecto a la primera vista que es visible.
Transiciones
Al moverse de una room a otra puedes seleccionar una transición. También puedes seleccionar la transición que se
utilizará en el frame siguiente sin tener que cambiar de cuarto. Para esto usamos la variable transition_kind.
Asignándole un valor entre 1 y 13 se usará la transición correspondiente. Si usamos un valor igual a 0 significa que no
se aplicará ninguna transición. La transición sólo se aplicará la próxima vez que se dibuje un frame.
Observa que es muy sencillo crear tus propias transiciones. Por ejemplo, para crear un efecto de oscurecimiento puedes
dibujar un rectángulo negro que tape toda la pantalla y hacer que su factor de transparencia vaya aumentando poco a
poco. O puedes cambiar la posición y el tamaño de una vista para mover el cuarto fuera de la zona visible. Utilizando
Redibujando la pantalla
Normalmente al final de cada paso del juego (step) el cuarto se dibuja en la pantalla. Pero en algunas ocasiones puede
que quieras dibujar el cuarto en otro momento. Esto ocurre cuando tu programa toma el control, por ejemplo: antes de
que el programa se congele durante un largo período es recomendable dibujar el cuarto. O cuando el juego muestra un
mensaje y espera a que el jugador presione un tecla para continuar es necesario dibujar el cuarto en medio de esta
existe una imagen donde se dibuja todo. Esta imagen no es visible en la pantalla. Sólo al final de un paso del juego,
cuando se ha dibujado todo, la imagen de la pantalla se reemplaza por esta imagen interna (esta técnica se conoce
como “double buffering”). La primera función scrren_redraw() dibuja la imagen interna y luego refresca la imagen de
la pantalla. La segunda función screen_refresh() sólo refresca la imagen de la pantalla, pero no actualiza la imagen
interna.
Ahora deberías entender por qué no puedes usar las acciones o funciones de dibujo en otros eventos a parte de los
eventos de dibujo. Si lo haces así, dibujarías elementos en la imagen interna pero no en la pantalla. Cuando se ejecuta
el evento draw, primero se dibuja el fondo del cuarto, tapando y borrando todo lo que habías dibujado en la imagen
interna. Pero cuando usas screen_refresh() después de dibujar algo, la imagen se volverá visible en la pantalla. Por
ejemplo, podemos crear un trozo de código que dibuje un texto en la pantalla, llame a la función de refresco y espere a
{
draw_text(screen_width/2,100,'Pulsa cualquier tecla para
continuar');
screen_refresh();
keyboard_wait();
}
Observa que al dibujar en un evento distinto del evento draw sólo puedes dibujar en la imagen interna, ¡nunca en una
vista! Así que las coordenadas que usas serán las mismas que si no hubiera vistas. Ten cuidado al usar esta técnica:
asegúrate de que has entendido bien como funciona y ten en cuenta que el refresco de la pantalla tarda un poco de
tiempo.
Cuando estás dibujando el propio cuarto puede ser útil no dejar que se dibuje automáticamente. Por ejemplo, puedes
querer que el cuarto sólo se dibuje cada 5 pasos. Para ello puedes usar la función siguiente:
Finalmente, hay una función que te permite sincronizar el dibujo con la frecuencia de refresco del monitor:
También puedes obligar a que el programa espere hasta la próxima sincronización vertical:
screen_wait_vsync() Espera hasta la siguiente sincronización vertical del monitor.
Música y sonido
Actualmente, el sonido juega un papel crucial en los videojuegos de computadora. Estos deben ser agregados al juego
en forma de recursos de sonido, pero asegúrate de que los nombres que uses sean nombres de variable válidos.
Como ya habrás leído, puedes indicar cuatro diferentes tipos de sonido: normal, música de fondo, 3D, y sonidos que
Los sonidos normales son usados para efectos de sonido y por lo general, son archivos WAV. Puedes reproducir muchos
del tipo NORMAL a la vez (incluso puedes reproducir múltiples instancias del mismo sonido a la vez) y se caracterizan
El sonido de fondo comúnmente consiste en archivos MIDI pero algunas veces también pueden usarse WAV y a estos
también se les puede aplicar efectos de sonido. La única diferencia que tienen con los sonidos NORMALES, es que los de
fondo solo pueden reproducirse uno a la vez. Si reproduces un sonido, el que previamente se había estado
reproduciendo es parado.
El uso y características del sonido 3D son descritos más adelante, en el subcapítulo “Sonidos 3D”. Son sonidos mono
(WAV o MIDI).
Finalmente, si quieres usar otro tipo de sonido, en partículas mp3, estos no pueden ser reproducidos por DirectX. En su
lugar, el reproductor multimedia normal debe ser usado para esto, lo cual lo hace mucho más limitado, ya que solo
puedes reproducir un sonido a la vez, no se le pueden aplicar efectos (ni siquiera cambio de volumen) y el tiempo (p.e.
los sonidos rebobinados son de calidad pobre). También puede haber retardos en reproducirlos. Se recomienda
altamente no usar este tipo de media, ya que hay algunas computadoras que probablemente no estén capacitadas para
reproducirlos).
Puedes encontrar información sobre las funciones de audio en las secciones siguientes:
Existen cinco funciones básicas relacionadas con sonido: 2 para reproducir un sonido, una para ver si un sonido se está
reproduciendo, y dos más para parar el sonido. La mayoría necesita el índice del sonido como argumento. El nombre
del sonido representa su índice, pero también puedes almacenar el índice en una variable, y usarla como argumento.
sound_play(index) Reproduce el sonido indicado una vez. Si el sonido es música de fondo, la actual es
parada.
sound_stop(index) Para el sonido indicado. Si hay varios sonidos con este índice reproduciéndose
simultáneamente, todos son parados.
Después de llamar la función de reproducir un sonido, no llega a los altavoces inmediatamente, lo que quiere decir que
mientras esto no suceda, esta función devolverá false. Parecido es si cuando el sonido es parado y lo escuchas por un
Es posible usar efectos de sonido. en lo particular puedes cambiar el volumen y pan (si el sonido viene del altavoz
izquierdo o derecho). En estos casos el volumen solamente puede ser reducido. Estas funciones no trabajan con
para entradas).
corresponden a un tempo más rápido, menores a un tempo más lento. Debe oscilar entre 0.01 y 100.
A demás de los archivos MIDI y WAV (y mp3) existe un cuarto tipo de archivo que puede ser reproducido: archivos
direct music. Tienen la extensión .sgt. Dichos archivos frecuentemente se refieren a otros archivos describiendo p.e.
banda o género. Para encontrar estos archivos, el sistema de sonido debe saber donde se localizan. Con este propósito,
puedes usar las siguientes funciones para estableces el directorio de búsqueda de archivos. NOTA: Debes agregar los
Efectos de sonido
Los efectos de sonido pueden ser usados para cambiar la manera en que los sonidos normales y música de fondo,
suena. Ten en cuenta que los efectos de sonidos solo aplican en archivos WAV y MIDI, no a los mp3. Esta sección
describe las funciones que existen para usar y cambiar efectos de sonido. Ten también en cuenta que para poder usar
estas funciones necesitas una buena comprensión de cómo el sonido y los sintetizadores trabajan. No se han agregado
explicaciones de los diferentes parámetros que se dan. Busca en la web o en libros para más información.
Para aplicar un efecto de sonido a un sonido en particular, puedes indicarlo cuando definas el sonido como recurso o
se_none
se_chorus
se_echo
se_flanger
se_gargle
se_reverb
se_compressor
se_equalizer
Puedes establecer una combinación de efectos, sumando los valores. Entonces, por ejemplo, puedes usar:
sound_effect_set(snd,se_echo+se_reverb);
para obtener una combinación de los efectos echo y reverb.
Todos los efectos tienen distintas configuraciones, una vez que el efecto ha sido aplicado en un sonido. El orden en esto
es crucial. Primero aplicas el efecto al sonido y después estableces sus parámetros. Una vez que reapliques los efectos
al sonido, la configuración se borra y necesitas establecerla de nuevo. Nótese que todos los parámetros deben oscilar
en un rango en particular, según se indique a continuación. Las siguientes funciones existen para cambiar la
*Al final de cada parámetro, entre paréntesis está el rango que se necesita usar p.e. (0 a 100,pred.50)
pred.=predeterminado
sound_effect_chorus(snd,wetdry,depth,feedback,frequency,wave,delay,pha
se) Establece los parámetros para el efecto de chorus al sonido indicado. Los siguientes parámetros son requeridos:
WETDRY Radio de la señal wet (procesada) a la señal dry (no procesada).(0 a 100,pred.50)
DEPTH Porcentaje con el cual el tiempo de espera es modulado por el oscilador de baja frecuencia, en cientos por punto
de porcentaje. (0 a 100,pred.25)
FEEDBACK Porcentaje de señal de salida para respaldar la entrada del sonido. (-99 a 100,pred.0)
DELAY Tiempo (número) en milisegundos que la entrada debe esperar antes de ser reproducida de nuevo (0 a
20,pred.0)
WETDRY Radio de la señal wet (procesada) a la señal dry (no procesada).(0 a 100,pred.50)
FEEDBACK .Porcentaje de señal de salida para respaldar la entrada del sonido. (-99 a 100,pred.0)
PANDELAY. Indica si el LEFTDELAY y RIGHTDELAY deben intercambiarse con cada echo que sucede.(0 y 1,pred.0)
sound_effect_flanger(snd,wetdry,depth,feedback,frequency,wave,delay,ph
ase) Establece los parámetros para el efecto de flanger al sonido indicado. Los siguientes parámetros son requeridos:
WETDRY Radio de la señal wet (procesada) a la señal dry (no procesada).(0 a 100,pred.50)
DEPTH Porcentaje con el cual el tiempo de espera es modulado por el oscilador de baja frecuencia, en cientos por punto
de porcentaje. (0 a 100,pred.25)
FEEDBACK Porcentaje de señal de salida para respaldar la entrada del sonido. (-99 a 100,pred.0)
DELAY Tiempo (número) en milisegundos que la entrada debe esperar antes de ser reproducida de nuevo (0 a
20,pred.0)
sound_effect_compressor(snd,gain,attack,release,threshold,ratio,delay)
Establece los parámetros para el efecto de compressor al sonido indicado. Los siguientes parámetros son requeridos:
RELEASE Velocidad a la que la entrada es parada después de que la entrada llegue debajo del umbral.(50 a
3000,pred.50)
RATIO Radio de frecuencia (0.001 a 0.999,pred.0.001)
DELAY Tiempo (número) en milisegundos que la entrada debe esperar antes de ser reproducida de nuevo (0 a
20,pred.0)
Sonido 3D
Sonido 3D se refiera al sonido que tiene una posición (y velocidad) con respecto al oyente. A pesar de que es más
prominente usarlo en juegos 3D, también puede ser efectivamente aplicado en juegos 2D. La idea es que el sonido
tenga una posición en el espacio. En todas las funciones, se asume que el oyente está en una posición (0,0,0). El
sistema calcula como el receptor estaría oyendo el sonido, y lo adapta acorde a esto. Este efecto es especialmente
bueno cuando tienes un buen equipo de sonido. De cualquier manera, esto también funciona en altavoces pequeños.
Además de una posición, el sonido también puede tener una velocidad. Esto lleva al muy conocido efecto Doppler, que
están correctamente modelado. Finalmente el sonido puede tener una orientación y, una vez más, el sonido es
Game Maker cuenta con la modalidad de sonido 3D, a través de las funciones que se indican abajo, pero solo funcionan
con recursos de sonido que fueron indicados como 3D. (La desventaja es que los sonidos 3D son mono, no estéreo).
a la posición del oyente en el espacio. Valores en el eje x incrementan de izquierda a derecha, en el eje y de arriba
hacia abajo, y en el eje z de cerca a lejos. Estos valores son medidos en metros. El volumen con el que el sonido se
debe oír depende en esta medida, de la misma manera a como ocurre en el mundo real.
indicado en el espacio. Por favor note que estableciendo la velocidad no significa que la posición cambie. La velocidad
es usada sólo para calcular efectos doppler. Entonces, si quieres mover un sonido debes cambiar tu mismo la posición
del sonido.
cual el sonido está en su máxima amplitud, y la distancia máxima a la cual el sonido ya no se debe oir. Entonces,
cuando la distancia oscila entre 0 y la máxima distancia, el sonido esta en su máxima amplitud. Cuando se oscile entre
la distancia mínima y la máxima, la amplitud decrece lentamente hasta que la distancia máxima es alcanzada, o el
sonido ya no es audible. Por predeterminado (default), la distancia mínima es 1 metro y la máxima 1 billón de metros.
sound_3d_set_sound_cone(snd,x,y,z,anglein,angleout,voloutside
Normalmente el sonido tiene la misma amplitud, a una distancia dada, en todas las direcciones. Puedes establecer un
cono de sonido para cambiar esto y dirigir el sonido. x,y,z especifican la dirección del cono. ANGLEIN especifica el
ángulo interior. Si el oyente esta dentro de este ángulo, puede escuchar el sonido en su volumen normal. ANGLEOUT
especifica el ángulo exterior. Cuando el oyente esta afuera de este ángulo, el volumen es indicado con VOLOUTSIDE.
Para ser precisos, VOLOUTSIDE es un número negativo que indica el número de cientos de decibeles que deben ser
sustraídos del volumen interior. Entre el ángulo interior y exterior, el volumen decrece gradualmente.
Música desde el CD
Existen también funciones para tratar con la reproducción de música desde un CD:
cd_init()Debe llamarse antes de usar las otras funciones, así como cuando se cambia un CD (o simplemente de
tiempo a tiempo).
cd_stop()Deja de reproducir.
cd_pause().Pausa la reproducción.
cd_resume().Continua la reproducción.
cd_set_position(pos). Establece la posición en el CD, en milisegundos.
cd_set_track_position(pos). Establece la posición de la pista actual en milisegundos.
cd_open_door()Abre la caja del Reproductor de CDs.
MCI_command(str) Esta función envía el comando al sistema multimedia de Windows usando el Media Control
Interface (MCI, Interfaz de Control de Medios). Devuelve la cadena de texto de resultado. Puedes usar esta función
para controlar cualquier tipo de hardware multimedia. Para más información sobre este comando, consulta la ayuda de
Windows. Por ejemplo, MCI_command('play cdaudio from 1') pone a sonar el cd (después de
inicializalo correctamente). Esta función es sólo para uso avanzado.
imágenes, etc...para mostrar mensajes y preguntas al jugador, y mostrar la tabla de mayores puntajes.
Ventanas
Muchos juegos tienen ventanas, las cuales pueden mostrar un video, una imagen o algún texto. Frecuentemente son
usadas al principio del juego (como intro), al principio de un nivel, o al final del juego (p.e. los créditos). En Game
Maker, dichas ventanas con texto, imágenes o video pueden mostrarse en cualquier momento del juego, y mientras
versión independiente del juego (stand-alone), no debes olvidar incluir este archive ahí. full indica si se debe mostrar
en pantalla completa. BACKCOL es el color de fondo, y DELAY es el retardo en segundos antes de volver al juego
(aunque el jugador siempre tendrá la posibilidad de volver al juego manualmente, haciendo clic con el Mouse)
show_image(fname,full,delay) Muestra una ventana con una imagen. fname es el nombre del archivo
de imagen (únicamente .bmp, .jpg y .wmf), pero debes poner los archivos en el directorio del juego tu mismo. FULL
indica si deseas mostrarlo en pantalla completa. DELAY es el retardo en segundos antes de volver al juego.
show_video(fname,full,loop) Muestra una ventana de video. FNAME es el nombre del archivo de video
(.avi,.mpg), pero debes poner este archivo en el directorio del juego tu mismo. FULL indica si deseas mostrarlo en
Existen otras funciones para mostrar mensajes, preguntas, un menú con opciones, o un cuadro de diálogo en el cual el
usado como acceso directo a ese botón (p.e. “&Acepto”, si se presiona “A” se simula la presión del botón). Esta función
show_question(str) Muestra una pregunta; devuelve true cuando el usuario elige “yes” y de otra manera,
false.
message_background(back) Establece la imagen de fondo par a los cuadros de diálogo para cualquiera de
las funciones arriba. BACK debe ser uno de los archivos definidos en el juego. Si BACK es parcialmente transparente,
message_alpha(alpha) Establece la traslucidez de los cuadros de diálogo para cualquiera de las funciones
anteriores. ALPHA debe oscilar entre 0 (completamente traslúcido) y 1 (completamente opaco) (solo para WIN2000 o
superior).
message_button(spr) Establece el sprite usado para los botones en los cuadros de diálogo. SPR debe ser un
sprite consistiendo de 3 imágenes, la primera indica el botón cuando no esta presionado y el mouse está alejado, el
Segundo indica el botón cuando el mouse está sobre el pero no presionado, y el tercero cuando del botón es
presionado.
los cuadros de diálogo. STYLE indica el estilo de fuente. (0=normal, 1=negrita, 2=cursiva, 3=negrita y cursiva).
message_mouse_color(col) Establece el color de la fuente para los botones en los cuadros de diálogo
cuando el mouse está sobre ellos.
message_input_color(col) Establece el color para el fondo del campo de entrada en los cuadros de
diálogo.
message_caption(show,str) Establece el título del cuadro de diálogo. SHOW indica si se debería mostrar
mensaje.
show_menu(str,def) Muestra un menú pop-up. STR indica el texto del menú. Este consiste en diferentes
opciones del menú con una barra vertical entre ellas. Por ejemplo, str = ‘menu0|menu1|menu2’. Cuando la primera
opción es seleccionada, se devuelve un 0, para la segunda un 1, etc. Cuando ninguna opción es seleccionada, el valor
con un ';'(punto y coma) entre ellas. '*' (asterisco) significa cualquier string. Por ejemplo: ‘Mapa de
show_error(str,abort) Muestra un mensaje de error estándar (y/o escribe el error al archivo de registros).
ABORT indica si el juego debería cerrarse (true-abortar, false-ignorar).
Tablas de récords
Una ventana “pop-up” especial es la lista de MEJORES PUNTAJES que se mantiene para cada juego. Las siguientes
cuando no hay nadie en un lugar en particular. ESCAPE es el string en el inferior indicando que se debe presionar la
tecla ESC. Puedes usar esto en particular cuando tu juego usa un idioma diferente.
highscore_show_ext(numb,back,border,col1,col2,name,size) Muestra la tabla de
hightscores con diferentes opciones (también puede lograrse usando las funciones anteriores). NUMB es el nuevo
puntaje. Si el puntaje es suficientemente bueno para ser agregado a la lista, el jugador puede introducir un nombre.
Usa -1 para mostrar simplemente la lista actual. BACK es la imagen de fondo a usar, BORDER indica si se debe mostrar
o no el borde. COL1 es el color para la nueva entrada, COL2 para las demás entradas. NAME es el nombre de la fuente
highscore_value(place) Devuelve el puntaje de la persona en el lugar PLACE(1-10). Esto puede ser usado
para dibujar tu propia lista de puntajes.
Recursos
En Game Maker puedes especificar varios tipos de recursos, como sprites, sonidos, fondos, objetos, etc. En este
capítulo encontrarás un número de opciones que actúan sobre estos recursos, para modificarlos u obtener información
Sprites
sprite_get_bbox_right(ind) Devuelve el valor del límite derecho del sprite (bounding box) con el índice
especificado.
sprite_get_bbox_top(ind) Devuelve el valor del límite superior del sprite (bounding box) con el índice
especificado.
sprite_get_bbox_bottom(ind) Devuelve el valor del límite inferior del sprite (bounding box) con el
índice especificado.
sprite_get_bbox_mode(ind) Devuelve el modo usado para la caja de controno del sprite (0=automatic,
1=full image, 2=manual).
sprite_get_precise(ind) Devuelve si el sprite con el índice dado utiliza la colisión precisa (precise
collision checking).
Sonidos
Los sonidos usan muchos recursos y algunos sistemas sólo pueden guardar y hacer sonar un número limitado de ellos.
Si haces un juego muy grande, deberías llevar un control sobre los sonidos que se cargan en la memoria de audio en
cada momento. Podrías desactivar la opción Preload para asegurarte de que los sonidos no se carga hasta que van a
ser usados (aunque este método puede originar cierto retraso la primera vez que se escuche el sonido). Además, los
sonidos no se eliminan de la memoria cuando ya no van a ser utilizados. Para controlar esto puedes usar las siguientes
funciones:
Fondos
Paths
path_get_y(ind,pos) Devuelve la coordenada y en la posición pos del path (pos debe estar comprendida
entre 0 y 1).
path_get_speed(ind,pos) Devuelve la velocidad en la posición pos del path (pos debe estar comprendida
entre 0 y 1).
Scripts
Time lines
Objetos
object_get_parent(ind) Devuelve el índice del objeto pariente del objeto ind (-1 si no tiene pariente).
object_is_ancestor(ind1,ind2) Devuelve si el objeto ind2 es un parent del objeto ind1.
Rooms
Observa que como las habitaciones cambian durante el juego hay otras rutinas para obtener información de la
habitación actual
Es posible crear nuevos recursos durante el juego. También se pueden cambiar recursos existentes. Las posibilidades
se muestran en este capítulo. Pero se debe tener cuidado: la modificación de recursos fácilmente puede llevar a
¡¡¡errores en los juegos!!! Al modificar recursos, se deben seguir las siguientes reglas:
• No cambies recursos que están siendo utilizados. ¡Esto provocará errores! Por ejemplo, no cambies un sprite
• Cuando guardas/salvas el juego mientras se está ejecutando, los recursos agregados y modificados NO son
guardados con el juego. Por lo que, si más tarde se carga el juego, los recursos modificados podrían no estar
disponibles. En general, cuando se manipulan recursos ya no se puede hacer uso del sistema interno de
• Cuando reinicias un juego mientras está en ejecución, los recursos modificados no son reestablecidos a su
forma original. En general, cuando se manipulan recursos ya no se puede hacer uso de la acción o de la
• La creación de recursos durante la ejecución del juego (en particular los sprites y fondos) fácilmente
consume gran cantidad de memoria. Se debe ser extremadamente cuidadoso con esto. Por ejemplo, si se
tiene un un sprite 128x128 con 32 cuadros de animación y decides crear 36 copias rotadas estarás usando
• Se deben eliminar los recursos que ya no son utilizados. De otra forma pronto se ocuparía toda la memoria
del sistema.
En general, no se deben modificar recursos durante el juego. Es mejor crear y cambiar los recursos al inicio del juego o
Sprites
sprite_set_precise(ind,mode) Establece si el sprite con índice ind usa chequeo de colisión precisa
(true o false).
Las siguientes funciones pueden utilizarse para crear nuevos sprites y para eliminarlos.
sprite_duplicate(ind) Crea una copia del sprite con índice ind. Devuelve el índice del nuevo sprite.
sprite_assign(ind,spr) Asigna el sprite spr al al sprite con índice ind. Es decir, crea una copia del sprite.
De esta manera fácilmente puedes asignar un sprite existente a p. ej. un nuevo sprite.
sprite_merge(ind1,ind2) Fusiona las imagines del sprite ind2 en el sprite ind1, agregándolas al final de
este ultimo. Si los tamaños no corresponden los sprites son encogidos. ¡No se elimina el sprite ind2!
sprite_add(fname,imgnumb,precise,transparent,smooth,preload,xorig,yori
g) Agrega a los recursos sprites la imagen del archivo fname. Solo se pueden agregar imágenes bmp, jpg y gif.
Cuando se trata de un bmp o jpg la imagen puede ser una tira que contenga las subimágenes del sprite una junto a la
otra. Se usa imgnumb para indicar el número de subimágenes (1 para una sola). Este argumento no es empleado
con imágenes gif (animadas); se emplea el número de imágenes del archivo gif. precise indica si se usará chequeo
de colisión precisa. transparent indica si la imagen es parcialmente transparente. smooth indica si se alisarán
los bordes. preload indica si se precargará la imagen en la memoria de texturas. xorig y yorig indican la
posición de origen en el sprite. La función devuelve el índice del nuevo sprite. Si ocurre algún error devuelve -1.
sprite_replace(ind,fname,imgnumb,precise,transparent,smooth,preload,xo
rig,yorig) Lo mismo que la anterior pero en este caso se reemplaza al sprite con índice ind. El valor devuelvo
sprite_create_from_screen(x,y,w,h,precise,transparent,smooth,preload,x
orig,yorig) Crea un nuevo sprite copiando de un área indicada de la pantalla. Esto permite crear cualquier
sprite que se desee. Se dibuja la imagen en pantalla empleando las funciones de dibujado y luego se crea un sprite con
ella. (Si no se hace dentro del evento drawing aún se puede hacer de manera que no sea visible si no se refresca la
pantalla). Los demás parámetros son similares a los indicados en las anteriores funciones. La función devuelve el índice
del sprite. Pero se poner algo de cuidado aquí. Aunque se habla de la pantalla, de hecho, lo que importa es el área en
donde se dibuja. No importa el hecho de que haya una ventana en pantalla y que la imagen pudiera estar escalada en
esta ventana.
del sprite con índice ind. x, y, w y h indican las dimensiones del área en pantalla (coordenadas x, y, ancho y alto).
índice ind usando los valores de luminosidad (hue) del sprite spr. Esta acción no se puede deshacer.
Sonidos
Las siguientes rutinas pueden ser utilizadas para crear nuevos sonidos y para eliminarlos.
archive de sonido. kind indica el tipo de sonido (0=normal, 1=de fondo, 2=3D, 3=mmplayer), preload indica si el
sonido debiera ser almacenado inmediatamente en la memoria de audio (true o false). La función devuelve el índice del
nuevo sonido, que puede utilizarse para reproducirlo. (O devolverá -1 si ocurriera algún error, p. ej. que el archivo no
existiera).
crea un nuevo sonido sino que se sustituye el que tenga índice index, liberando el anterior sonido. El valor devuelto por
sound_delete(index) Elimina el sonido index, liberando toda la memoria asociada con él. Ya no es posible
recuperarlo
Fondos
Las siguientes rutinas pueden ser empleadas para crear nuevas imágenes de fondo y para eliminarlas.
background_duplicate(ind) Crea un duplicado del fondo con el índice ind. Devuelve el índice del nuevo
background_assign(ind,back) Asigna el fondo back al fondo ind. Esto es, crea una copia del fondo.
precargará la imagen en la memoria de texturas. La función devuelve el índice del nuevo fondo, el cual se puede usar
para dibujarlo o para asignarlo a la variable background_index[0] para hacerlo visible en la habitación actual. Devuelve
anterior pero en este caso el fondo con índice ind es reemplazado. La función devuelve un valor indicando si tuvo éxito
h=alto) con el color col. Devuelve el índice del nuevo fondo, -1 si ocurre algún error.
indicado (w=ancho, h=alto) coloreado con un gradiente. col1 y col2 indican los dos colores. kind es un número entre 0
y 5 que indica el tipo de gradiente: 0=horizontal, 1=vertical, 2=rectángulo, 3=elipse, 4=doble horizontal, 5=doble
vertical. Esta función devuelve el índice del nuevo fondo, ó -1 si ocurre algún error.
background_create_from_screen(x,y,w,h,transparent,smooth,preload) Crea un
fondo copiando un área indicada de la pantalla (x, y=coordenadas esquina superior izquierda, w=ancho, h=alto). Esta
función permite crear cualquier fondo que se desee. Se dibuja la imagen en pantalla usando las funciones de dibujo y a
continuación se crea un nuevo fondo de ella. (Si no se hace esto en el evento drawing incluso se puede lograr que no
sea visible en pantalla si no se refresca). Los otros parámetros son similares a los de las anteriores funciones. La
función devuelve el índice del nuevo fondo. Se requiere algo de cuidado aquí. Aunque se habla de la pantalla, lo que
importa es la región en la que se dibuja. No importa el hecho de que haya una ventana en pantalla y que la imagen en
del fondo con índice ind usando los valores hue del fondo back. Esta acción no puede deshacerse.
Fuentes
Es posible crear, reemplazar y eliminar fuentes durante el juego usando las siguientes funciones. (No se debe
reemplazar una fuente que está configurada como la actual o en su caso se debe reestablecer la fuente después del
cambio).
Se indica el tamaño (size), si es negrita (bold), cursiva (italic) y el primer y ultimo caracteres que deben ser creados
(first y last).
creada de un sprite. El sprite debe contener una subimagen para cada carácter. first indica el índice del primer carácter
en el sprite. Por ejemplo, se puede usar ord('0') si el sprite solo contiene los dígitos. prop indica si la fuente es
proporcional. En una fuente proporcional, para cada carácter el ancho de la caja de colisión es utilizado como el ancho
del mismo. Finalmente, sep indica la distancia que debe separar a los caracteres horizontalmente. Un valor típico
nueva fuente, indicando el nombre (name), tamaño (size) si es negrita (bold) o cursive (italic) y el primer y último
Paths
Es posible crear trayectorias y agregar puntos a las mismas. Sin embargo, nunca debe modificarse una trayectoria que
está siendo usada por alguna instancia. Se pueden provocar resultados inesperados. Se tienen las siguientes funciones:
(0=straight, 1=smooth).
path_set_closed(ind,closed) Establece si la trayectoria ind debe ser cerrada (true) o abierta (false).
path_assign(ind,path) Asigna la trayectoria path a la trayectoria ind. Por tanto, crea una copia de la
trayectoria. De esta manera se puede fácilmente configurar una trayectoria existente a p. Ej. otra trayectoria nueva.
con factor de velocidad speed. Se debe recordar que un factor de 100 corresponde a la velocidad actual. Valores
path_clear_points(ind) Limpia todos los puntos de la trayectoria ind, volviéndola una trayectoria vacía.
path_rotate(ind,angle) Rota angle grados la trayectoria ind en contra de las manecillas del reloj
(alrededor de su centro).
path_scale(ind,xscale,yscale) Escala la trayectoria ind con los factores indicados (con respecto a su
centro).
Scripts
No se pueden modificar los scripts durante la ejecución del juego. Los scripts son una parte de la lógica del juego. La
modificación de los mismos llevaría a una autoreescritura de código que muy fácilmente llevaría a errores. Hay otras
formas de hacer algo parecido. Si realmente se necesita ejecutar una pieza de código no conocida en tiempo de diseño
(p. ej. Desde un archivo) se pueden hacer uso de las siguientes funciones:
En ocasiones se quiere almacenar en una variable el índice de un script para luego ejecutarlo. Para ello se puede utilizar
la siguiente función
script_execute(scr,arg0,arg1,...) Ejecuta el script con índice scr con los argumentos indicados.
Time lines
Las siguientes rutinas permiten la creación y modificación de las time lines. ¡No se deben alterar las líneas de tiempo
timeline_delete(ind) Elimina la línea de tiempo con índice ind. Debe asegurarse que ninguna instancia
instante step. codestr contiene el código para las acciones. Si el step no existe se crea. Por lo que pueden agregarse
timeline_moment_clear(ind,step) Se puede utilizar esta función para borrar todas las acciones de
Objetos
También los objetos pueden ser manipulados y creados en tiempo de ejecución. NUNCA se debe cambiar o eliminar un
objeto del cual existan instancias. Esto puede provocar efectos inesperados ya que ciertas propiedades son
object_set_sprite(ind,spr) Establece el sprite para el objeto con índice ind. El valor -1 remueve del
object_set_visible(ind,vis) Establece si las instancias creadas del objeto ind deben ser visibles por
object_set_mask(ind,spr) Establece el sprite para la máscara del objeto con índice ind. Para que la
object_set_parent(ind,obj) Establece el padre (obj) para el objeto ind. Si se usa -1 se indica que el
objeto ind no tiene padre. El cambiar el objeto padre cambia el comportamiento de las instancias del objeto.
Las siguientes rutinas son útiles para crear objetos mientras el juego se ejecuta. Como con todas las rutinas de
modificación de recursos, se debe ser muy cuidadoso para que no se creen nuevos objetos todo el tiempo.
object_add() Agrega un nuevo objeto. Devuelve el índice del mismo. Después se puede utilizar este índice en
las rutinas anteriores para configurar ciertas propiedades del objeto y luego se puede utilizar el índice para crear
object_delete(ind) Elimina el objeto con índice ind. Se debe asegurar de que no existan instancias de este
se deben definir eventos para dicho objeto. Solo se pueden agregar acciones de código. Se debe indicar el objeto (ind),
el tipo de evento (evtype), el número de evento (evnumb, se pueden usar las constantes indicadas antes para la
función event_perform()). Por ultimo se proporciona la cadena de código que debe ejecutarse (codestr). Se pueden
La creación de objetos es en particular muy útil cuando se están diseñando scripts o bibliotecas de acciones. Por
ejemplo, un script de inicialización puede crear un objeto para mostrar un texto y otro script puede agregar un objeto
con un texto en particular. De esta forma se tiene un mecanismo simple para desplegar mensajes sin la necesidad de
Rooms
La manipulación de habitaciones en tiempo de ejecución es algo muy riesgoso. Se debe tomar en cuenta que las
habitaciones cambian todo el tiempo debido a lo que ocurre dentro del juego. Esto normalmente solo ocurre para la
habitación en turno y hay muchas rutinas descritas en anteriores secciones para manipular las instancias, los fondos y
los tiles en la habitación actual. Pero los cambios en la habitación active se mantendrán si dicha habitación es
persistente. Entonces, nunca se debieran manipular elementos de la habitación en turno o de cualquier otra habitación
que sea persistente y que ya haya sido visitada antes. Tales cambios en general no serán percibidos pero en ocasiones
podrían ocasionar errores inesperados. Debido al hecho de que las habitaciones están enlazadas de manera complicada
room_set_code(ind,str) Configura el código de inicialización (str) para la habitación con índice ind.
con índice ind si no cuenta con una imagen de fondo. col indica el color y show indica si el color debe mostrarse o no.
room_set_background(ind,bind,vis,fore,back,x,y,htiled,vtiled,hspeed,vs
peed,alpha) Establece el fondo con índice bind (0-7) como fondo para la habitación con índice ind. vis indica si el
fondo será visible y fore si se trata de un fondo de primer plano. back es el índice de la imagen de fondo. x, y indican la
posición de la imagen y htiled y vtiled indican si la imagen debe dividirse en tiles. hspeed y vspeed indican la velocidad
con la que el fondo se mueve y alpha indica un valor de transparencia alfa (1 = sólido y más rápido).
room_set_view(ind,vind,vis,xview,yview,wview,hview,xport,yport,wport,h
port,hborder,vborder,hspeed,vspeed,obj) Establece la vista con índice vind (0-7) para la
habitación con índice ind. vis indica si la vista es visible. xview, yview, wview y hview indican la posición de la vista en
la habitación. xport, yport, wport y hport indican la posición en pantalla. Cuando la vista debe seguir a un objeto
hborder y vborder indican el borde mínimo visible que debe mantenerse alrededor del objeto. hspeed y vspeed indican
la máxima velocidad con la que la vista puede moverse. obj es el índice del objeto o el índice de la instancia.
índice ind.
room_add() Agrega una nueva habitación. Devuelve el índice de la misma. Se debe notar que la habitación no
formará parte del orden de habitaciones. Por lo que la nueva habitación no cuenta con habitaciones previa ni siguiente.
room_duplicate(ind) Agrega una copia de la habitación con índice ind. Devuelve el índice de la nueva
habitación.
room_assign(ind,room) Asigna la habitación room al índice ind. Por tanto, esta función crea una copia de la
habitación.
room_instance_add(ind,x,y,obj) Agrega a la habitación ind una nueva instancia del objeto obj,
height indican la parte del fondo que forma al tile. x, y es la posición del tile en la habitación y depth es la profundidad
del tile.
room_tile_add_ext(ind,back,left,top,width,height,x,y,depth,xscale,
yscale,alpha) Lo mismo que la anterior rutina pero también se puede especificar un factor de escalado en las
direcciones x e y, y una transparencia alpha para el tile.
En juegos más avanzados probablemente querrás leer datos de un archivo que has incluido con el juego. Por ejemplo,
pudieras crear un archivo que indique en qué momento deben ocurrir ciertas cosas. Probablemente también quieras
guardar información para la siguiente vez que se ejecute el juego (por ejemplo, el cuarto actual). En algunas
situaciones puede ser que necesites ejecutar programas externos, o crear nuevos archivos con datos del juego.
Puedes encontrar toda la información sobre estos temas en las secciones siguientes:
Archivos
Es útil utilizar archivos externos en juegos. Por ejemplo, podrías hacer un archivo que describe qué ciertas cosas deben
suceder en qué momento. También puedes querer salvar la información para la próxima vez que se ejecute el juego.
Las funciones siguientes sirven para leer y escribir datos en archivos de texto:
file_text_open_read(fname) Abre el archivo fname para lectura. La función devuelve la id del archivo
que debes utilizar en el resto de funciones. Puedes abrir hasta un máximo de 32 archivos simultáneamente. No olvides
Para manipular archivos del sistema puedes utilizar las siguientes funciones:
file_find_first(mask,attr) Devuelve el nombre del primer archivo que satisfaga las condiciones de la
máscara mask y los atributos attr. Si no existe tal archivo, devuelve una cadena vacía. La máscara puede contener una
ruta y comodines (*), por ejemplo ‘C:\temp\*.doc’. Los atributos indican archivos adicionales que quieras ver. (Por lo
que los archivos normales son siempre devueltos cuando satisfacen la máscara). Puedes agregar las siguientes
constantes
file_find_close() Debe ser llamada después de manipular los archivos para liberar la memoria.
file_attributes(fname,attr) Indica si el archivo fname tiene todos los atributos dados por attr. Usa
una combinación de las constantes indicadas anteriormente.
Las siguientes funciones sirven para cambiar los nombres de archivos. Observa que estas funciones no afectan a los
filename_name(fname) Devuelve el nombre del archivo fname, con la extensión pero sin la ruta.
filename_path(fname) Devuelve la ruta al archivo indicado, incluyendo la última barra de separación.
filename_dir(fname) Devuelve el directorio del archivo, que normalmente suele ser igual que la ruta pero
sin la última barra de separación.
En algunas situaciones puede que quieras leer datos de archivos binarios. Para ello dispones de las siguientes rutinas
de bajo nivel:
file_bin_open(fname,mod) Abre el archivo con el nombre especificado. El parámetro mod indica qué se
puede hacer con el archivo: 0=leer, 1=escribir y 2= leer y escribir. La función devuelve la id del archivo que debe
utilizarse en las demás funciones. Puedes abrir hasta un máximo de 32 archivos simultáneamente, pero no olvides
file_bin_seek(fileid,pos) Mueve la posición en el archivo a la posición indicada por pos. Para añadir
contenido al final de un archivo, usa el valor file_bin_size(fileid) para el parámetro pos.
Si el jugador ha seleccionado modo seguro en sus preferencias, para ciertas rutinas, no se permite especificar la ruta y
sólo puedes acceder a los archivos en la carpeta de la aplicación p. Ej. para escribir en ellos.
game_id* Identificador único para el juego. Puedes usarlo si necesitas un nombre único de archivo.
working_directory* Carpeta de trabajo del juego. (No incluye la diagonal invertida final).
temp_directory* Carpeta temporal creada para el juego. Puedes almacenar archivos temporales aquí. Serán
eliminados cuando el juego finalice.
En ciertas situaciones podrías dar al jugador la posibilidad de introducir argumentos mediante la línea de comandos al
juego que están ejecutando (para por ejemplo activar trucos o modos especiales). Para obtener estos argumentos
parameter_count() Devuelve el número de parámetros de la línea de comandos (nota que el nombre del
programa es uno de ellos).
parameter_string(n) Devuelve los parámetros n de la línea de comandos. El primer parámetro tiene índice 0
y es el nombre del programa.
Por último, puedes leer el valor de las variables de entorno con la siguiente función:
Registro
Si deseas almacenar una pequeña cantidad de información entre cada ejecución del juego hay un mecanismo más
simple que el emplear un archivo. Puedes usar el registro. El registro es una gran base de datos de Windows para
mantener todo tipo de configuraciones para los programas. Una entrada tiene un nombre, y un valor. Puedes usar tanto
registry_write_real(name,x) Crea una entrada en el registro con el nombre name y el valor real x.
registry_read_string(name) Devuelve la cadena almacenada con el nombre name. (El nombre debe
existir, de otra forma se devuelve una cadena vacía).
registry_read_real(name) Devuelve el valor real almacenado en la entrada name. (El nombre debe
existir, de otra forma se devuelve el número 0).
En realidad, los valores en el registro están agrupados en claves. Las rutinas anteriores trabajan con valores dentro de
la clave que está creada especialmente para tu juego. Tu programa puede usar esto para obtener cierta información
sobre el sistema en el que se está ejecutando el juego. También puedes leer valores en otras claves. Puedes escribir
valores en ellas pero ten mucho cuidado. PUEDES FÁCILMENTE DESTRUIR TU SISTEMA de esta forma. (La
escritura no se permite en modo seguro). Nota que las claves también están colocadas en grupos. Las siguientes
el grupo HKEY_CURRENT_USER. Pero si quieres puedes cambiar este grupo raíz de trabajo. Por ejemplo, para obtener
path = registry_read_string_ext('\Environment','TEMP');
Las siguientes funciones existen para moverte por el registro con libertad:
registry_read_real_ext(key,name) Devuelve el valor real con el nombre name en la clave key (el
nombre debe existir, de otra forma se devuelve el número 0).
Para pasar ciertos parámetros a un programa es corriente utilizar los archivos INI. Los archivos INI contienen secciones
[Form]
Arriba=100
Izquierda=100
Titulo=El mejor juego de la historia
[Game]
MaxScore=12324
Este archivo contiene dos secciones: la primera llamada Form y la segunda llamada Game. Primera sección contiene 3
parejas de valores. Las dos primeras tienen un valor real mientras que la tercera es una cadena de texto. Los archivos
INI son muy fáciles de crear y editar. En Game Maker puedes utilizar las siguientes funciones para esto:
ini_open(name) Abre el archivo de INI con el nombre dado ¡El archivo INI se debe almacenar en la misma
carpeta que el juego!
default.
Ejecución de programas
Game Maker también tiene la posibilidad de iniciar programas externos. Hay dos funciones disponibles para esto:
argumentos. Puede esperar hasta que el programa termine (pausando el juego) o continuar con el juego. La función
execute_shell abre un archivo. Puede ser cualquier archivo para cuyo tipo esté definida una asociación, p. Ej. un
archivo html, un archivo de Word, etc. O puede ser un programa. No puede esperar a que se termine la ejecución por
execute_program(prog,arg,wait) Ejecuta el programa prog con los argumentos arg. wait indica si se
debe esperar a que termine la aplicación.
Ambas funciones no funcionarán si el jugador activa el modo seguro en sus preferencias. Puedes comprobar esto
Estructuras de datos
En los juegos normalmente necesitas guardar información. Por ejemplo, necesitas guardar listas de ítems que lleva un
personaje o nombres de lugares que necesita visitar. Puedes usar arrays para esto. Pero si necesitas hacer operaciones
más complicadas, como ordenar los datos o buscar un ítem determinado, necesitas escribir grandes trozos de código en
Para remediar esto, Game Maker dispone de un número de funciones de estructuras de datos. Hasta el momento hay 6
tipos de estructuras de datos diferentes: stacks (pilas), queues (colas), lists (listas), maps (mapas), priority queues
(colas de prioridad) y grids (rejillas). Cada uno de estos tipos está preparado para un uso particular.
Todas las estructuras de datos funcionan generalmente de la misma manera. Creas una estructura de datos con una
función que devuelve la id de esa estructura y luego usas esa id para hacer diferentes operaciones. Cuando has
terminado, puedes destruir la estructura para ahorrar espacio. Puedes usar tantas estructuras a la vez como desees y
Observa que las estructuras de datos no se guardan cuando guardas el juego usando las funciones o acciones
Al comparar valores, por ejemplo al ordenar una lista, Game Maker debe decidir cuándo dos valores son iguales. Al usar
cadenas de texto y valores enteros esto es evidente, pero para valores reales, debido a errores de redondeo, números
iguales pueden convertirse en diferentes. Por ejemplo, (5/3)*3 no será igual a 5. Para evitar esto, puedes especificar la
precisión que deseas usar. Cuando la diferencia entre dos números es menor que esta precisión, los dos números se
consideran iguales. Por defecto se utiliza una precisión de 0.0000001.Puedes cambiar este valor con las funciones
siguientes:
ds_set_precision(prec) Especifica la precisión usada en comparaciones.
Esta precisión se utiliza en las estructuras de datos pero no en el resto de comparaciones en GML!
Pilas
Una pila es una estructura LIFO (último en entrar, primero en salir). Puedes empujar (push) valores en la pila o
quitarlos tirando de ellos (pop). El último valor que se empujó en la lista es el primero al que se accede al tirar de la
lista. Es como si vas poniendo un libro encima de otro: cuando quieras coger un libro, el último que pusiste estará
encima de todos los demás y será el que cojas. Las pilas se utilizan para gestionar interrupciones o cuando se usan
funciones recursivas:
ds_stack_create() Crea una nueva pila. La función devuelve un número entero con la id de la pila para usarla
en las diferentes funciones. Puedes crear varias pilas.
ds_stack_destroy(id) Destruye la pila, liberando la memoria usada. No olvides usar esta función cuando
ya no necesites la pila.
ds_stack_clear(id) Limpia la pila, borrando todos los valores que contiene pero no la destruye.
ds_stack_size(id) Devuelve el número de valores en la pila.
ds_stack_empty(id) Devuelve true si la pila está vacía. Es lo mismo que chequear si el número de valores en
la pila es cero.
ds_stack_top(id) Devuelve el valor al principio de la pila (el primero que se introdujo) pero no lo elimina.
Colas
Una cola es parecido a una pila, pero funciona como una estructura FIFO (primero en entrar, primero en salir). El
primer valor que se mete en la cola es el primero en salir, como una cola en una tienda. El primer cliente en llegar será
el primero en ser atendido y los demás tendrán que esperar su turno en orden. Para trabajar con colas existen las
funciones siguientes (observa que las 5 primeras son equivalentes a las funciones de las pilas: todas las estructuras de
ds_queue_create()Crea una nueva cola. La función devuelve un número entero con la id de la cola para
usarla en las diferentes funciones. Puedes crear varias colas.
ds_queue_destroy(id) Destruye la cola, liberando la memoria usada. No olvides usar esta función cuando
ya no necesites la cola.
ds_queue_clear(id)Limpia la cola, borrando todos los valores que contiene pero no la destruye.
ds_queue_size(id) Devuelve el número de valores en la cola.
ds_queue_empty(id) Devuelve true si la cola está vacía. Es lo mismo que chequear si el número de valores
en la cola es cero.
ds_queue_enqueue(id,val) Introduce el valor en la cola.
ds_queue_head(id) Devuelve el valor al principio de la cola, esto es, el primero que se introdujo, pero no lo
elimina de la cola.
Listas
Una lista guarda una colección de valore en un orden determinado. Puedes añadir valores a la lista a la posición que
desees. Por eso, puedes acceder acceder a los valores usando un índice de su posición en la lista. También puedes
ordenar los elementos de forma ascendente o descendente. Las listas se pueden usar para muchas cosas, por ejemplo,
para guardar valores que cambian. Las listas se han programado usando arrays, pero al estar definidas en código
ds_list_create()Crea una nueva lista. La función devuelve un número entero con la id de la lista para usarla
en las diferentes funciones. Puedes crear varias listas.
ds_list_destroy(id) Destruye la lista, liberando la memoria usada. No olvides usar esta función cuando ya
no necesites la lista.
ds_list_clear(id) Limpia la lista, borrando todos los valores que contiene pero no la destruye.
Mapas
En algunas ocasiones necesitas guardar pares de valores consistentes de una llave (key) y un valor. Por ejemplo, un
personaje puede llevar varios ítems diferentes y puede tener un número diferente de cada uno. En este caso, el ítem
será la llave y la cantidad será el valor. Los mapas manejan estas parejas de valores, ordenándolos por la llave. Puedes
añadir valores al mapa y buscar uno concreto usando las llaves. Como las llaves también están ordenadas, puedes
encontrar los valores correspondientes a la llave siguiente o anterior. A veces es útil usar un mapa para guardar llaves
sin ningún valor asignado. En este caso puedes asignarles a todas las llaves el valor 0.
ds_map_create()Crea un nuevo mapa. La función devuelve un número entero con la id del mapa para usarla en
las diferentes funciones. Puedes crear varios mapas.
ds_map_destroy(id) Destruye el mapa, liberando la memoria usada. No olvides usar esta función cuando ya
no necesites el mapa.
ds_map_clear(id)Limpia el mapa, borrando todos las parejas llave-valor que contiene pero no lo destruye.
ds_map_size(id) Devuelve el número de parejas llave-valor en el mapa.
ds_map_empty(id) Devuelve true si el mapa está vacía. Es lo mismo que chequear si el número de valores en
el mapa es cero.
Colas de prioridad
En una cola de prioridad a cada valor se le asigna una prioridad. Así puedes buscar los valores con mayor o menor
ds_ priority _create()Crea una nueva cola. La función devuelve un número entero con la id de la cola
para usarla en las diferentes funciones. Puedes crear varias colas.
ds_ priority _destroy(id) Destruye la cola, liberando la memoria usada. No olvides usar esta función
cuando ya no necesites la cola.
ds_ priority _clear(id)Limpia la cola, borrando todos los valores que contiene pero no la destruye.
ds_ priority _size(id) Devuelve el número de valores en la cola.
ds_ priority _empty(id) Devuelve true si la cola está vacía. Es lo mismo que chequear si el número de
valores en la cola es cero.
Rejillas
Una rejilla es simplemente un vector (o arreglo) bidimensional. Una rejilla tiene un entero con su altura y anchura. Esta
estructura te permite acceder a los valores indicando el valor de la celda deseada (el primer valor en las direcciones x e
y es 0). Pero también puedes dar valores por regiones, añadirlos y encontrar los valores máximo, mínimo o medio de
una región. Esta estructura es útil para representar por ejemplo un campo de juego. Aunque todo lo que hace esta
estructura se puede conseguir usando vectores, las operaciones por regiones son mucho más rápidas.
ds_grid_destroy(id) Destruye la rejilla, liberando la memoria usada. No olvides usar esta función cuando ya
no necesites la rejilla.
ds_grid_multiply(id,x,y,val) Multiplica la celda por el valor. Sólo se puede usar con números.
ds_grid_set_region(id,x1,y1,x2,y2,val) Todas las celdas de la región especificada toman el
valor especificado.
Creando partículas
Los sistemas de partículas se emplean para crear efectos especiales. Las partículas son pequeños elementos (ya sean
representados por un pequeño sprite, un píxel o una pequeña forma). Tales partículas se mueven de acuerdo a reglas
predefinidas y pueden cambiar su color mientras se mueven. Muchas partículas juntas pueden crear p. Ej. Fuegos
es sencillo de utilizar por lo que mejor lee cuidadosamente esta sección antes de probarlo. Los sistemas de partículas
tienen muchos parámetros y no siempre es fácil entender cómo crear los efectos que se desean. Si lo encuentras
demasiado difícil, Game Maker también dispone de un sistema de efectos para crear explosiones, humo, lluvia e incluso
fuegos artificiales.
Primero que nada hay tipos de partículas. Un tipo de partícula define una clase de partículas en específico. Tales tipos
pueden tener muchos parámetros que describen la forma, el tamaño, color y movimiento de las partículas.
Después tenemos los sistemas de partículas. Puede haber diferentes sistemas de partículas en un juego. Un sistema de
partículas puede tener partículas de diferentes tipos. Un sistema de partículas cuenta con emisores que crean las
partículas, ya sea continuamente o en estallidos. También puede contar con atractores que atraen las partículas.
Finalmente, puede también tener destructores que destruirán las partículas. Una vez que se ha creado una partícula en
Efectos simples
La forma más sencilla de crear partículas es usando el mecanismo de efectos. Los efectos se crean usando partículas,
pero tú no tienes que preocuparte de eso. Simplemente eliges el tipo de efecto, la posición donde debe crearse, su
• ef_explosion
• ef_ring
• ef_ellipse
• ef_firework
• ef_smoke
• ef_smokeup
• ef_star
• ef_spark
• ef_flare
• ef_cloud
• ef_rain
• ef_snow
Algunos sólo necesitarás crearlos una vez (como la explosión) mientras que otros deberás crearlos en cada step (como
el humo o la lluvia). Observa que los efectos de nieve y lluvia siempre se crean en la parte superior de la pantalla así
Aunque esto pueda sonar limitado, se pueden conseguir efectos muy espectaculares. Por ejemplo, creando una
pequeña nube de humo rojo debajo de una nave espacial en cada step se consigue una cola de fuego. Las dos
posición especificada. size es el tamaño del efecto según la convención siguiente: 0 = pequeño, 1 = mediano, 2 =
grande. color indica el color del efecto. El efecto se crea por debajo de la instancia con una profundidad (depth) igual a
100000.
Tipos de partículas
Tipos de partículas
Un tipo de particular define la forma, color y movimiento de una clase de partículas. Sólo necesitas definir un tipo de
partícula una vez durante el juego, una vez hecho esto puede ser usado por cualquier sistema de partículas. Los tipos
de partículas tienen un gran número de características que pueden ser cambiadas para alterar su apariencia o
part_type_create() Crea un Nuevo tipo de particular. Devuelve el índice del tipo. Este índice debe usarse
en todas las llamadas a las funciones siguientes para configurar las propiedades del tipo de partícula, por lo que es
part_type_destroy(ind) Destruye el tipo de particular ind. Llama a esta función cuando ya no se necesite
tus partículas pero debes saber que además hay 15 tipos de sprites ya definidos. Estos sprites son todos de tamaño
64x64 y poseen valores de transparencia que los hacen fundirse de manera excelente con el fondo. Estos sprites se
• pt_shape_pixel (pixel)
• pt_shape_disk (disco)
• pt_shape_square (cuadrado)
• pt_shape_line (línea)
• pt_shape_star (estrella)
• pt_shape_circle (círculo)
• pt_shape_ring (anillo)
• pt_shape_sphere (esfera)
• pt_shape_flare (brillo)
• pt_shape_spark (chispa)
• pt_shape_explosion (explosión)
• pt_shape_cloud (nube)
• pt_shape_smoke (humo)
• pt_shape_snow (nieve)
particular ind. Con la opción animate puedes indicar si el sprite debe ser animado. Con stretch indicas si la animación
debe repetirse durante el tiempo de vida de la partícula. Y con random indicas si se escoge una subimagen aleatoria
Una vez que hayas seeccionado el sprite para el tipo de partícula puedes elegir el tamaño de éste. Un tamaño de 1
significa el tamaño normal del sprite. Un tipo de partícula se puede definir para que todas las partículas tengan el
mismo tamaño o para que tengan tamaños distintos. También puedes especificar un rango de tamaños, si el tamaño
debe variar durante la vida de la partícula y si puede alternarse al crecer o disminuir, creando un efecto de tintineo.
part_type_size(ind,size_min,size_max,size_incr,size_wiggle) Establece los
parámetros de tamaño para el tipo de partícula. Debes indicar el tamaño inicial, el tamaño final, la cantidad que debe
crecer en cada step (usa un valor negativo para que disminuya) y el tintineo (El valor por defecto es 1 y el tamaño no
cambia).
Las partículas también tienen una orientación que se indica en grados en sentido antihorario. Al igual que el tamaño, la
orientación puede crecer, disminuir y puede ser la misma para todas las partículas o diferente para cada una.
part_type_orientation(ind,ang_min,ang_max,ang_incr,ang_wiggle,ang_rela
tive) Aplica la orientación al tipo de partícula. Debes indicar la orientación inicial, final, el incremento o decremento
en cada step, si la orientación tintinea y si los valores son relativos al movimiento actual (1) o absolutos (0). Por
ejemplo, si ponemos todos los valores a 0 menos ang_relative, que lo ponemos a 1, la orientación de la partícula
Color y blending
Las partículas tendrán un color. Hay diferentes maneras de especificar un color para una partícula. La más simple es
especificar solamente un color. Pero también puedes especificar dos ó tres colores que se interpolarán a lo largo de la
vida de la partícula. Por ejemplo, una partícula puede crearse siendo blanca y poco a poco irse convirtiendo en negra.
Otra posibilidad es indicar que el color de cada partícula debe ser diferente y elegido de un rango de colores
especificado.
El color por defecto es blanco. Cuando usas un sprite con sus propios colores esto es lo que normalmente quieres y no
medio y final .
part_type_color_mix(ind,color1,color2) El color será seleccionado de una mezcla aleatoria de
especificar que cada partícula tendrá un color fijo pero escogido de un rango de colores que especificas en modo rgb.
Además del color, también puedes especificar un valor de transparencia. Así puedes conseguir que las partículas
Normalmente las partículas se mezclan con el fondo igual que los sprites. Pero también es posible usar additive
blending, controlando así la forma en que se mezclan. Esto resulta un efecto muy bueno para explosiones.
part_type_blend(ind,additive) Indica si se debe utilizar additive blending (1) or normal blending (0)
Vida y muerte
Las partículas viven durante un tiempo limitado, después, desaparecen. La vida de las partículas se mide en steps.
Puedes indicar su vida, o un rango para cada tipo de partículas. Además, las partículas pueden crear otras partículas en
cada step o cuando mueren. Vigila que el número de partículas total no sea demasiado elevado.
deben ser generadas en cada step para el tipo indicado de particula. Si usas un número negativo para step_number,
deben ser generadas cuando una particular del tipo indicado llega al final de su ciclo de vida. Puedes usar valores
negativos al igual que en la función anterior. La partícula sólo creará otras partículas al llegar al final de su vida y no
part_type_speed(ind,speed_min,speed_max,speed_incr,speed_wiggle) Establece
las propiedades de velocidad para el tipo de partícula. (Todos los valores son 0 por defecto). Especificas un valor
mínimo y máximo para la partícula y cuando ésta sea creada se escogerá un valor aleatorio entre ellos. Puedes
especificar un valor negativo en speed_incr para hacer que la partícula se frene (aunque nunca llegará a tener
velocidad negativa).
propiedades de dirección para el tipo de partícula, en grados antihorarios (0-360). (Todos los valores son 0 por
defecto).
Las partículas tienen vida en sistemas de partículas. Por lo que para tener partículas en tu juego necesitas crear uno o
más sistemas de partículas. Puede haber muchos tipos diferentes de sistemas de partículas. Por ejemplo, si tu juego
tiene varias pelotas y cada pelota debiera tener una cola de partículas, lo más probable es que cada pelota tenga su
sistema de partículas. La manera más fácil para manejar los sistemas de partículas es crear uno y luego crear
partículas en él, usando los tipos partículas que especificaste antes. Pero, como veremos a continuación, los sistemas
de partículas pueden contener emisores que automáticamente producen partículas, atractores que las atraen y
Una vez que las partículas se añaden a un sistema son dibujadas y actualizadas en cada step automáticamente. Para
definir si las partículas se deben dibujar delante o detrás de las demás instancias, los sistemas de partículas poseen una
profundidad.
Los sistemas de partículas no se eliminarán aunque reinicies el juego, para eliminarlos debes usar la función adecuada.
part_system_create() Crea un nuevo sistema de partículas. Devuelve el índice del sistema creado. Este
índice debe emplearse en las llamadas a las funciones siguientes para configurar las propiedades del sistema de
partículas.
part_system_destroy(ind) Destruye el sistema de partículas ind. Llama a esta función si ya no harás uso
part_system_clear(ind) Devuelve el sistema ind a sus valores por defecto, removiendo todas las
partículas. Cuando oldtonew es true las partículas más viejas son dibujadas primero y las nuevas se dibujan sobre ellas
(valor por defecto). De otra forma las partículas más recientes son dibujadas primero. Esto puede resultar en varios
efectos diferentes.
querer que esto sea así. Las siguientes funciones te permiten controlar estos aspectos:
part_system_update(ind) Esta función actualiza el sistema y permite a los emisores crear partículas.
Puedes llamar a esta función cuando no uses actualización automática, o para acelerar un poco el sistema al inicio.
automática. Debes llamar a esta función desde el evento draw de algún objeto.
number partículas del tipo ind en la posición (x,y) dentro del sistema con el color indicado. Esto es útil cuando el tipo de
Emisores
Los emisores crean partículas. Pueden crear un flujo continuo de partículas o pueden lanzar un cierto número de
partículas usando la función adecuada. Un sistema de partículas puede tener un número arbitrario de emisores. Un
• xmin, xmax, ymin, ymax indican la extensión de la región en la cual se generan las partículas.
• shape indica la forma de la región. Puede tener uno de los siguientes valores:
o ps_shape_rectangle
o ps_shape_ellipse
o ps_shape_diamond
o ps_shape_line
• distribution indica la distribución usada para general las partículas. Puede tener uno de los
siguientes valores:
o ps_distr_linear indica una distribución lineal, esto es que en toda la región se tiene la
• number indica el número de partículas generadas en cada step. Si es menor que 0, en cada step se
generará una particular con una probabilidad de –1/number. Por ejemplo con un valor de –5 se generará una
Las siguientes funciones permiten establecer los emisores y permitirles crear partículas. Nota que cada una de ellas
part_emitter_create(ps) Crea un nuevo emisor en el sistema de partículas ps. Devuelve el índice del
emisor. Este índice debe usarse en todas las llamadas a las funciones siguientes para configurar las propiedades del
emisor.
part_emitter_region(ps,ind,xmin,xmax,ymin,ymax,shape,distribution)
Establece la región y distribución del emisor.
partículas de tipo partype desde el emisor ind en cada step. Si indicas un número menor a 0 en cada step una
particular sera generada con una probabilidad de –1/number. Por ejemplo con un valor de –5 se creará en promedio
Atractores
Además de los emisores, un sistema de partículas también puede contener atractores. Un atractor atrae a las partículas
(o las aleja). Un sistema de partículas puede tener múltiples atractores. Aunque se te recomienda solo usar unos pocos
porque alentarán el procesamiento de las partículas. Un atractor tiene las siguientes propiedades:
• force indican la fuerza de atracción del atractor. El cómo actúe la fuerza sobre las partículas depende de
• dist indica la máxima distancia a la cual el atractor tiene efecto. Solo las partículas con una distancia al
fuerza es 0 mientras que en la posición del atractor la fuerza tiene el valor force indicado.
• additive indica si la fuerza se suma a la velocidad y dirección en cada step (true) o si solo se aplica a la
posición de la particular (false). Cuando es true la particular acelerará hacia el atractor mientras que cuando
Las siguientes funciones definen atractores. Nota que todas necesitan como primer argumento el índice del sistema de
part_attractor_create(ps) Crea un nuevo atractor en el sistema de partículas ps. Devuelve el índice del
atractor. Este índice debe emplearse en la llamada a las funciones siguientes para configurar las propiedades del
atractor.
part_attractor_destroy(ps,ind) Destruye el atractor ind dentro del sistema de partículas ps. Llama
part_attractor_destroy_all(ps) Destruye todos los atractores que hayan sido creados en el sistema
de partículas ps.
partículas ps.
part_attractor_clear(ps,ind) Devuelve los valores del atractor a sus valores por defecto.
Destructores
Los destructores destruyen las partículas cuando aparecen dentro de su región. Un sistema de partículas puede tener
• xmin, xmax, ymin, ymax indican la extensión de la región en la cual se destruyen las partículas.
• shape indica la forma de la región. Puede tener uno de los valores siguientes:
o ps_shape_rectangle
o ps_shape_ellipse
o ps_shape_diamond
Las funciones siguientes configuran las propiedades de los destructores. Nota que cada una de ellas necesita como
del destructor. Este índice debe usarse en las llamadas a las funciones siguientes para configurar las propiedades del
destructor.
Llama a esta función cuando ya no hagas uso del destructor para ahorrar espacio.
partículas ps.
destructor.
Deflectores
Los deflectores desvían a las partículas cuando aparecen dentro de su región. Un sistema de partículas puede tener un
• xmin, xmax, ymin, ymax indican la extensión de la región en la cual las partículas son desviadas.
• kind indica el tipo de deflector. Puede tener uno de los siguientes valores:
paredes horizontales
• friction la cantidad de fricción como resultado de un impacto con el deflector. Entre mayor sea este
Se tienen las siguientes funciones para configurar las propiedades del deflector. Nota que todas necesitan como primer
del deflector. Este índice debe emplearse en las llamadas a las funciones siguientes para configurar las propiedades del
deflector.
part_deflector_destroy(ps,ind) Destruye el deflector ind del sistema de partículas ps. Llama a esta
part_deflector_destroy_all(ps) Destruye todos los deflectores que hayan sido creados en el sistema
de partículas ps.
partículas ps.
part_deflector_clear(ps,ind) Devuelve el deflector a sus valores por defecto.
Cambiadores
Los cambiadores cambian ciertas partículas cuando aparecen dentro de su región. Un sistema de partículas puede tener
• xmin, xmax, ymin, ymax indican la extensión de la región en la cual las partículas son
cambiadas.
• shape indica la forma de la región. Puede tener uno de los valores siguientes:
o ps_shape_rectangle
o ps_shape_ellipse
o ps_shape_diamond
• parttype1 indica el tipo de partícula a ser cambiado.
• kind indica el tipo del cambiador. Puede tener uno de los siguientes valores:
color y forma
Las siguientes funciones permiten establecer las propiedades del cambiador. Nota que cada una de ellas necesita como
part_changer_create(ps) Crea un nuevo cambiador en el sistema de partículas ps. Devuelve el índice del
cambiador. Este índice debe usarse en las llamadas a las funciones siguientes para configurar las propiedades del
cambiador.
part_changer_destroy_all(ps) Destruye todos los cambiadores que hayan sido creados en el sistema
de partículas ps.
part_changer_clear(ps,ind) Devuelve las propiedades del cambiador a sus valores por defecto.
part_changer_region(ps,ind,xmin,xmax,ymin,ymax,shape) Establece la región para el
cambiador.
Aquí tienes un ejemplo de un sistema de partículas que crea unos fuegos artificiales. Hace uso de dos tipos de
partículas: uno que forma el cohete y otro que forma los fuegos artificiales en si. El cohete genera las partículas de los
fuegos artificiales cuando se destruye. También tenemos un emisor en el sistema de partículas que emite regularmente
partículas del cohete desde la parte baja de la pantalla. Para hacer que esto funcione necesitas un objeto. En su evento
de creación colocamos el siguiente código que crea los tipos de partículas, un sistema de partículas y el emisor:
{
// Crear el sistema de partículas
ps = part_system_create();
// El cohete
pt2 = part_type_create();
part_type_shape(pt2,pt_shape_sphere);
part_type_size(pt2,0.2,0.2,0,0);
part_type_speed(pt2,10,14,0,0);
part_type_direction(pt2,80,100,0,0);
part_type_color2(pt2,c_white,c_gray);
part_type_life(pt2,30,60);
part_type_gravity(pt2,0.2,270);
part_type_death(pt2,150,pt1); /* Crear el fuego artificial al
morir*/
// Crear el emisor
em = part_emitter_create(ps);
part_emitter_region(ps,em,100,540,480,490,ps_shape_rectangle,ps_distr_
linear);
part_emitter_stream(ps,em,pt2,-4); // Crear 1 cada 4 steps
}
Listo! Asegúrate de que el sistema de partículas (y tal vez los tipos de partículas) se destruyen al cambiar de room o el
Juegos multijugador
Jugar contra la computadora es divertido. Pero jugar contra oponentes humanos puede serlo mucho más. Es
relativamente fácil crear este tipo de juegos porque no tienes que implementar IA compleja para un oponente
manejado por la computadora. Por supuesto que puedes sentar a dos jugadores tras el mismo monitor y emplear
diferentes teclas o
algún otro dispositivo de entrada, pero es mucho más interesante cuando cada jugador está detrás de su propia
computadora. O aún mejor, un jugador se encuentra del otro lado del océano. El Game Maker tiene soporte
multijugador. Por favor toma en cuenta que el crear juegos multijugador efectivos que se sincronicen bien y no tengan
latencia es una
tarea difícil. Este capítulo da una breve descripción de las posibilidades. En el sitio web hay un tutorial con más
información.
Configurando la conexión
Para que dos computadoras se comuniquen necesitarán cierto protocolo de conexión. Como la mayoría de los juegos, el
Game Maker ofrece cuatro diferentes tipos de conexión: IPX, TCP/IP, Módem y Serial. La conexión IPX (para ser más
precisos, se trata de un protocolo) funciona casi completamente transparente. Puede emplearse para jugar juegos con
otras personas en la misma red local. Se necesita que esté instalada en tu computadora para que pueda emplearse. (Si
no funciona, consulta la documentación de Windows. O ve a la opción Red dentro del Panel de Control y agrega el
protocolo IPX). TCP/IP es el protocolo de internet. Puede usarse para jugar con otros jugadores en cualquier lugar
mediante internet, suponiendo que conozcas su dirección IP. Una conexión modem se realiza a través del modem.
Tienes que introducir cierta información del modem (una cadena de inicialización y un número telefónico) para usarla.
Finalmente,
al usar la línea serial (una conexión directa entre las computadoras) necesitas introducir varias opciones de puertos.
Hay cuatro funciones dentro del GML que pueden emplearse para inicializar estas conexiones:
se necesita la dirección para unirse a una sesión (ve a continuación). En una red local no se necesitan direcciones.
(p. Ej. '0201234567'). Sólo se necesita el número telefónico al unirse a una sesión (ve a continuación).
indica el número de bits de parada (0 = 1 bit, 1 = 1.5 bit, 2 = 2 bits). parity indica la paridad (0=ninguna, 1=impar,
2=par, 3=mark). Y flow indica el tipo de control de flujo (0=ninguna, 1=xon/xoff, 2=rts, 3=dtr, 4=rts and dtr). La
función devuelve si la conexión se efectuó con éxito. Una llamada típica a esta función es
mplay_init_serial(1,57600,0,0,4). Si especificas 0 al primer argumento se abre un diálogo para que el usuario pueda
Tu juego debe llamar una de estas funciones una sola vez. Todas las funciones indican si han tenido éxito (si se logra la
conexión). No tiene éxito si el protocolo en particular no está instalado o soportado por tu máquina. Para checar si hay
Cuando empleas una conexión TCP/IP tal vez quieras decirle a la persona con la que deseas jugar cuál es la dirección ip
Cuando te conectas a una red, puede haber múltiples juegos ejecutándose en la misma red. Esos juegos reciben el
nombre de sesiones. Estas diferentes sesiones pueden corresponder a juegos diferentes o al mismo. Un juego debe
identificarse en la red. Afortunadamente, Game Maker hace esto por ti. Lo único que debes saber es que cuando
cambias el id del juego en la ventana de opciones esta identificación cambia. De esta forma puedes evitar que personas
con versiones anteriores de tu juego jueguen contra personas que cuentan con versiones más recientes.
Si quieres iniciar un nuevo juego multijugador necesitas crear una nueva sesión. Para esto puedes emplear la siguiente
rutina:
jugadores permitidos para este juego (usa 0 para un número arbitrario de jugadores). playname es tu nombre como
Una instancia del juego debe crear la sesión. La(s) otra(s) instancia(s) del juego deben unirse a esta sesión. Esto es un
poco más complicado. Primero debes ver las sesiones disponibles y luego elegir a la que te unirás. Hay tres rutinas
mplay_session_find() Busca todas las sesiones que aún aceptan jugadores y devuelve el número de
sesiones encontradas.
Hay una rutina más que puede cambiar el modo de la sesión. Debe llamarse antes de crear una sesión:
mplay_session_mode(move) Indica si se mueve la sesión de host a otra computadora cuando el host actual
cierre. move debe ser true o false (valor por defecto).
Jugadores
Cada instancia del juego que se una a una sesión es un jugador. Como se indicó antes, los jugadores tienen nombres.
mplay_player_find() Busca todos los jugadores en la sesión actual y devuelve el número de jugadores
encontrados.
mplay_player_name(numb) Devuelve el nombre del jugador número numb (0 es el primer jugador, el cual
siempre eres tú). Esta rutina puede sólo ser llamada después de haber llamado a la anterior.
mplay_player_id(numb) Devuelve el id único del jugador número numb (0 es el primer jugador, el cual
siempre eres tú). Esta rutina puede llamarse sólo después de haber llamado la primera. Este id es usado al enviar y
Datos compartidos
La comunicación mediante datos compartidos es probablemente la mejor forma de sincronizar el juego. Todo el proceso
de comunicación no es visible para ti. Hay un juego de 10000 valores que son comunes a todas las entidades del juego.
Cada entidad puede establecer y leer valores. Game Maker se asegura de que cada entidad vea los mismos valores. Un
valor puede ser un número real o una cadena. Sólo hay dos rutinas:
mplay_data_write(ind,val) Escribe el valor val (cadena o real) en la ubicación ind (ind entre 0 y
10000).
mplay_data_read(ind) Devuelve el valor en la ubicación ind (ind entre 0 y 10000). Inicialmente todos los
valores son 0.
Para sincronizar la información en las diferentes máquinas puedes ya sea usar un modo garantizado (guaranteed mode)
que asegura que el cambio llegue a la otra máquina (pero el cual es lento) o un modo no garantizado (non-guaranteed
mplay_data_mode(guar) Indica si se usa o no transmisión garantizada para los datos compartidos. guar
debe ser true (valor por defecto) o false.
Mensajes
El segundo mecanismo de comunicación que el Game Maker soporta es el envío y la recepción de mensajes. Un jugador
puede enviar mensajes a un jugador o a todos los jugadores. Los jugadores pueden ver si han llegado mensajes y
llevar a cabo las acciones adecuadas. Los mensajes pueden enviarse en modo garantizado en el que estás seguro de
que llegarán (pero puede ser lento) o en modo no garantizado, el cual es más rápido.
mensaje y val es el valor (ya sea un número real o una cadena). El mensaje es enviado en modo no garantizado. Si val
identificador del mensaje y val es el valor (ya sea un número real o una cadena). Este es un envío garantizado. Si val
hubo un nuevo mensaje. Si es así, puedes emplear las siguientes rutinas para obtener su contenido:
mplay_message_clear(player) Elimina todos los mensajes en la cola de espera del jugador player (usa
0 para eliminar todos los mensajes).
Debemos hacer algunas observaciones. Primero, si quieres enviar un mensaje sólo a un usuario en particular,
necesitarás conocer el id del jugador. Como se indicó antes puedes obtener este id con la función mplay_player_id().
Este identificador del jugador también es empleado al recibir mensajes de un jugador en particular. Alternativamente,
puedes dar el nombre del jugador como cadena. Si varios jugadores tienen el mismo nombre, el mensaje sólo llegará al
primero.
En segundo lugar, podrías preguntarte porqué cada mensaje tiene un entero identificador. La razón es que esto ayuda
a tu aplicación a enviar diferentes tipos de mensajes. El receptor puede checar el tipo de mensaje usando el id y llevar
a cabo las acciones apropiadas. (Como no está garantizado que los mensajes lleguen, el enviar el id y el valor en
Usando DLLs
En aquellos casos en los que la funcionalidad del GML no cubra tus objetivos, se pueden ampliar las posibilidades
usando plug-ins (agregados o complementos). Un plug-in consiste en un archivo DLL (Dynamic Link Library – Biblioteca
de enlazado dinámico). En este archivo DLL se pueden definir funciones. Tales funciones pueden ser programadas en
cualquier lenguaje que soporte la creación de DLLs (p. Ej. Delphi, C, C++, etc.). Aunque se necesita contar con ciertas
habilidades de programación para poder hacerlo. Las funciones plug-in deben tener un formato específico. Pueden tener
entre 0 y 11 argumentos, cada uno de los cuales puede ser un número real (double en C) o una cadena terminada en
un carácter nulo. (Para más de 4 argumentos, sólo se soporta argumentos reales). Estas funciones deben devolver ya
En Delphi se puede crear una DLL seleccionando New en el menú File y luego eligiendo DLL. A continuación tienes un
ejemplo de una DLL escrita en Delphi que puede ser empleada con Game Maker. (Esto es código de Delphi ¡no GML!)
library MyDLL;
begin
end.
Esta DLL define dos funciones: MyMin toma dos argumentos reales y devuelve el de menor valor, y DoubleString que
duplica una cadena. Cabe recordar que se debe ser cuidadoso con el manejo de la memoria. Esta es la razón por la que
declaré la cadena resultante como global. También nota el uso de la convención de llamada cdecl. Puedes emplear las
convenciones de llamada cdecl o stdcall. Una vez que crees la DLL en Delphi tendrás el archivo MyDLL.DLL. Este archivo
debe ser colocado en la carpeta de tu juego. (O en cualquier otro lugar donde Windows pueda encontrarlo).
Para hacer uso de esta DLL en Game Maker primero necesitas especificar las funciones externas que deseas emplear y
qué tipo de argumentos requieren. Para ello tenemos la siguiente función en el GML:
external_define(dll,name,calltype,restype,argnumb,arg1type,arg2type,
…) Define una función externa. dll es el nombre del archivo dll. name es el nombre de las funciones. calltype es la
convención de llamada empleada. Usa dll_cdecl o dll_stdcall. restype es el tipo del resultado. Usa ty_real o ty_string.
argnumb es el número de argumentos (0-11). Después, para cada argumento debes especificar su tipo. Para ello usa
nuevamente ty_real o ty_string. Cuando hay más de 4 argumentos todos ellos deben ser de tipo ty_real.
Esta función devuelve el id de la función externa que debe emplearse para llamarla. En el ejemplo de arriba, al inicio
{
global.mmm = external_define('MYOWN.DLL','MyMin',dll_cdecl,
ty_real,2,ty_real,ty_real);
global.ddd = external_define('MYOWN.DLL','DoubleString',dll_cdecl,
ty_string,1,ty_string);
}
Ahora en el momento que necesites llamar a las funciones, usas la siguiente función:
Necesitas proporcionar el número correcto de argumentos del tipo correcto (real o string). La función devuelve el
{
aaa = external_call(global.mmm,x,y);
sss = external_call(global.ddd,'Hello');
}
external_free(dll) Libera la DLL con el nombre indicado. Esto es particularmente necesario si el juego
debiera remover la DLL. Mientras la DLL no haya sido liberada, no puede ser removida. Lo mejor es realizar esto p. Ej.
Ahora te preguntarás cómo hacer una función en una DLL que haga algo en el juego. Por ejemplo, podrías querer una
DLL que agregue instancias de objetos a tu juego. La forma más fácil es dejar que la función DLL devuelva una cadena
que contenga una pieza de código GML. Esta cadena que contiene una pieza de GML puede ser ejecutada usando la
Alternativamente puedes dejar que la DLL cree un archivo con un script que puede ser ejecutado (esta función también
Ahora puedes llamar una función externa y ejecutar la cadena resultante, por ejemplo:
{
ccc = external_call(global.ddd,x,y);
execute_string(ccc);
}
En algunos casos especiales pudieras necesitar el handle de ventana (identificador de Windows) de la ventana principal
del juego. Este puede obtenerse con la siguiente función y luego ser pasado a la DLL:
El empleo de DLLs externas es una función extremadamente poderosa. Pero por favor haz uso de ellas sólo si sabes lo
Gráficos 3D
Game Maker es un programa destinado para crear juegos en 2 dimensiones e isométricos. Sin embargo existen algunas
funciones para crear gráficos tridimensionales. Pero antes que inicies con esto hay un par de cosas que debes entender.
• La funcionalidad 3D en Game Maker está limitada a la parte gráfica. No hay más soporte para otras
funciones 3D. Una vez que comiences a usar gráficos 3D posiblemente tendrás problemas con otros aspectos
de Game Maker, como las vistas, orden de profundidad, etc. La funcionalidad es limitada y tiene baja
prioridad de ser extendida. Así que no esperes soporte para objetos de modelos 3D, etc.
• Cuando usas la funcionalidad 3D, son varias cosas las que ya no pueden ser usadas.
o Ya no podrás usar backgrounds ni foregrounds en tus rooms (La razón es que son usados como
mosaicos para llenar la imagen pero con proyecciones de perspectiva esto ya no funciona
correctamente).
o Ya no podrás usar la posición del ratón. El ratón no será transformado a las coordenadas 3D. Aún
puedes obtener la posición del ratón en la pantalla (en la vista) pero tendrás que hacer cálculos con
o Ya no podrás usar tiles. Lo más posible es que los tiles ya no encajen correctamente.
o La comprobación de colisiones todavía usa las posiciones 2-d de las instancias en el cuarto. Así
que no hay detección de colisiones en 3D. En ocasiones aún podrás usar esto (si utilizas el cuarto
como una representación de un mundo plano (peje. para carreras o juegos FPS) pero en otras
• Todas las funciones 3D son a través de código. Debes estar algo familiarizado con el lenguaje GML. Además
debes entender bastante acerca de como funciona Game Maker de lo contrario te meterás en dificultades.
• Necesitas tener conocimiento básico acerca de gráficos 3D. En particular usaré términos como proyecciones
• No hay modelado 3D en Game Maker. Además que no planeo añadir soporte para cargar modelos 3D.
• Es preciso que trabajes con cuidado para mantener una velocidad razonable. Además, esto no está
Rumbo al modo 3D
Rumbo al modo 3D
Si deseas usar el modo 3D antes necesitas poner Game Maker en modo 3D. Después puedes regresar al modo
Nota que todas las funciones relacionadas con 3D inician con d3d_.
Iniciar el modo 3D dará resultado a los siguientes cambios. Primero que nada hidden surface removal es
cambiado a encendido (usando un z-buffer de 16 bits). Esto significa que por cada píxel en la pantalla sólo el
dibujado con el valor z menor (= al valor depth, profundidad) es mostrado. Si las instancias tienen la misma
profundidad es confuso lo que pasará y puedes obtener efectos feos. ¡Asegúrate que las instancias que pueden
En segundo lugar, la proyección ortográfica es remplazada por una perspectiva. Esto significa lo siguiente.
proyección en perspectiva las instancias que poseen mayor profundidad parecerán más pequeñas. Cuando la
profundidad es 0 al viejo tamaño (a menos que cambies la proyección; velo mas adelante). El punto de vista
para la cámara es puesta a una distancia sobre el cuarto. (Esta distancia es igual al ancho del cuarto; la cual da
una proyección por defecto razonable.) Sólo las instancias que estén frente la cámara son dibujadas. Así que
no uses instancias con una profundidad menor a 0 (o al menos no menor que –w donde w (width) es el ancho
En tercer lugar, la coordenada-y vertical es invertida. Mientras que la posición (0,0) está en la esquina superior-
izquierda de la vista, en el modo 3D la posición (0,0) está en la posición inferior-izquierda, lo cual es normal
(false).
Dibujo fácil
Una vez que haz cambiado al modo 3D puedes usar Game Maker como sueles hacerlo (excepto por las
remarcas hechas al principio). Sólo los objetos aparecerán en diferentes tamaños basados en profundidad.
Incluso puedes usar vistas. Una función adicional puede ser usada. Si dibujas cierto número de cosas en una
pieza de código quizá quieras cambiar el valor de profundidad entre las primitivas que dibujes. Para esto usa:
Nota que al momento en que una nueva instancia es dibujada la profundidad es devuelta al valor original de
esa instancia.
Dibujando polígonos en 3D
El problema de dibujar de la vieja manera es que un sprite o polígono siempre se encuentra en un plano-xy,
esto es, todas las esquinas tienen la misma profundidad. Para 3D verdadero debes ser capaz de tener vértices a
(depth). Así que debemos especificar coordenadas como (x,y,z). Para esto existe una versión especial de
propio color y valor alpha. Esto te permite crear primitivas con un suave cambio de color y valores
alfa.
dibuja.
Por ejemplo, para dibujar un tetraedro (pirámide de tres lados) situada en el plano z=0 con su punta en z =
Ahora que si quieres usar esto, lo más posible es que sólo verás un triangulo en la pantalla porque la punta del
tetraedro estaré en frente del punto de vista. Aparte, usando sólo un color, sería difícil de ver las diferentes
caras. Adelante veremos maneras de cambiar el punto de vista. Asignando colores lo cual puede hacerse como
También puedes usar polígonos texturizados en 3D. Funciona de la misma manera como está descrito en las
funciones de dibujado avanzado en la documentación. Pero en esta ocasión necesitas variantes 3D de las
funciones básicas. De una cosa te debes dar cuenta. En una textura la posición (0,0) es la esquina superior-
izquierda. Pero en ocasiones, cuando se usan proyecciones (como se indica adelante), la esquina inferior-
posición (xtext,ytext) en la textura, mezclando con el color y valor alpha especificados anteriormente.
d3d_vertex_texture_color(x,y,z,xtex,ytex,col,alpha) Añade el
vértice (x,y,z) a la primitiva con posición (xtex,ytex) en la textura, mezclando con su propio color y
valor alpha.
dibuja.
Así que, por ejemplo puedes usar el siguiente código para dibujar una imagen de fondo que desaparece a la
distancia
{
var ttt;
ttt = background_get_texture(back);
d3d_primitive_begin_texture(pr_trianglefan,ttt);
d3d_vertex_texture(0,480,0,0,0);
d3d_vertex_texture(640,480,0,1,0);
d3d_vertex_texture(640,480,1000,1,1);
d3d_vertex_texture(0,480,1000,0,1);
d3d_primitive_end();
}
Un triángulo tiene una cara frontal y una trasera. La cara frontal está definida para ser el lado donde los
vértices son definidos en orden contrario al reloj. Normalmente ambos lados son dibujados. Pero si haces una
forma cerrada esto es inútil porque el lado trasero del triángulo nunca puede ser visto. En este caso puedes
encender backface culling. Esto salva cerca de la mitad la cantidad del tiempo dibujando pero te deja con la
culling (false).
Existen varias funciones para dibujar formas básicas, como bloques y paredes. Nota que estas formas también
d3d_draw_block(x1,y1,z1,x2,y2,z2,texid,hrepeat,vrepeat) Dibuja
un bloque del color actual con las esquinas opuestas indicadas usando la textura indicada. Usa -1
para no usar una textura. hrepeat indica que tan seguido la textura debe ser repetida a través
del borde horizontal de cada cara. vrepeat hace lo mismo con el borde vertical.
d3d_draw_cylinder(x1,y1,z1,x2,y2,z2,texid,hrepeat,vrepeat,ce
rrado,steps) Dibuja un cilindro vertical usando el color actual en la caja de colisión indicada
usando la textura indicada. Usa -1 para no usar una textura. hrepeat indica que tan seguido la
textura debe ser repetida a través del borde horizontal de cada cara. vrepeat hace lo mismo con
el borde vertical. closed indica si se debe cerrar la tapa y fondo del cilindro. steps indica cuantos
una pared vertical en el color actual con las puntas dadas usando la textura indicada. Usa -1 para no
usar una textura. hrepeat indica que tan seguido la textura debe ser repetida a través del borde
horizontal de cada cara. vrepeat hace lo mismo con el borde vertical.
d3d_draw_floor(x1,y1,z1,x2,y2,z2,texid,hrepeat,vrepeat) Dibuja
un piso (inclinado) en el color actual con las esquinas dadas usando la textura indicada. hrepeat
indica que tan seguido la textura debe ser repetida a través del borde horizontal de cada cara.
{
var ttt;
ttt = background_get_texture(back);
d3d_draw_block(20,20,20,80,40,200,ttt,1,1);
d3d_draw_block(200,300,-10,240,340,100,ttt,1,1);
}
Viendo el mundo
Por defecto tu vista es a través del eje-z negativo hacia el medio del cuarto. En ocasiones en juegos 3D deseas
poder cambiar la manera en que miras al mundo. Por ejemplo, en un juego de disparos en primera persona
probablemente deseas tener la cámara mirando desde una posición un poco sobre el plano-xy a lo largo del
plano-xy. En términos gráficos estás estableciendo la proyección correcta. Para cambiar la manera en que
d3d_set_projection(xfrom,yfrom,zfrom,xto,yto,zto,xup,yup,zup
) Defines como mirar en el mundo. Especificas el punto desde dónde mirar, el punto a mirar y el
rumbo ascendente.
Esta función requiere alguna explicación. Para establecer la proyección primero necesitas la posición desde la
cual miras. Esto está indicado por los parámetros (xfrom,yfrom,zfrom). En seguida debes especificar la
dirección que miras. Esto se hace dando un segundo punto al cual mirar. Este es el punto (xto,yto,zto).
Finalmente, incluso puedes rotar la cámara alrededor de la línea del punto de vista al punto observado. Para
especificar esto debemos tener un rumbo ascendente, esto es, la dirección ascendente en la cámara. Esto es
dado por los últimos tres parámetros (xup,yup,zup). Permítanme dar un ejemplo. Para mirar a través del plano-
{
d3d_set_projection(100,100,10,200,100,10,0,0,1);
}
Así miras desde el punto (100,100) y 10 sobre el plano en dirección de (200,100). Los puntos del vector up es
la dirección-z que requiere. Para hacer esto ligeramente más complicado, asumamos que tienes una instancia
en tu cuarto que especifica la posición de la cámara. Esta tendrá una posición (x,y) actual y una dirección (y
quizá incluso una velocidad). Ahora puedes especificar esto como tu cámara usando el siguiente código:
{
with (obj_camera)
d3d_set_projection(x,y,10,
x+cos(direction*pi/180),y-sin(direction*pi/180),10,
0,0,1);
}
Esto puede lucir algo complicado. Miramos desde la posición de la cámara (x,y), 10 sobre el piso. Para
determinar un punto en la dirección correcta necesitamos hacer algo de aritmética. Este punto está indicado por
¡Una observación importante! Cuando Game Maker comienza a dibujar un cuarto este pondrá el punto de vista
de regreso a la posición por defecto. Así que la primera cosa que necesitas hacer cuando dibuja la escena es
d3d_set_projection_ext(xfrom,yfrom,zfrom,xto,yto,zto,xup,yup
,zup,angle,aspect,znear,zfar) Una versión extendida de esta función en la cual
además especificas el ángulo definiendo el campo de vista, el radio de aspecto entre el tamaño
Los parámetros adicionales trabajan como sigue. Si especificaste la posición de la cámara, el punto a mirar, y el
vector up, puedes todavía cambiar qué tan unidos están los lentes de la cámara. Esto es conocido como el
campo de vista. Un valor razonable es 45 grados y esto es el valor por defecto tomado. Pero puedes cambiar
esto si gustas. Después puedes especificar la relación de aspecto entre la proyección horizontal y vertical.
Normalmente quieres usar el mismo como la radio de aspecto del cuarto o la vista, p.ej. 640/480. Finalmente
puedes indicar los planos de recorte. Los objetos que son más cercanos que znear a la cámara no son
dibujados. Similar para objetos más lejos que zfar. Puede ser importante poner estos parámetros a valores
razonables porque ellos también influyen en la precisión de las comparaciones de z. Si usted hace el rango
demasiado grande la precisión empeora. Por defecto usamos 1 y 32000. ¡znear debe ser más grande que 0!
A veces necesitas temporalmente una proyección normal ortográfica como la usada cuando no hay 3D. Por
ejemplo, cuando quieres dibujar alguna capa. Para esto puedes usar la función siguiente:
Un uso estándar para esto es dibujar una capa para p.ej. mostrar el puntaje u otros aspectos. Para hacer esto
ponemos una proyección ortográfica. También debemos desactivar hidden surface removal temporalmente
porque queremos que la información sea dibujada independientemente del valor de profundidad actual. El
{
draw_set_color(c_black);
d3d_set_projection_ortho(0,0,room_width,room_height,0);
d3d_set_hidden(false);
draw_text(10,10,'Score: ' + string(score));
d3d_set_hidden(true);
}
Transformaciones
Las transformaciones te permiten cambiar el lugar donde las cosas son dibujadas en el mundo. Por ejemplo, la
función para dibujar bloques sólo puede dibujar bloques paralelos de eje. Por primera opción una
transformación de rotación puedes crear bloques girados. También los sprites siempre son dibujados paralelos
al plano-xy. Estableciendo una transformación puedes cambiar esto. Hay dos tipos de funciones: las funciones
transformación).
d3d_transform_set_rotation_axis(xa,ya,za,ángulo) Pone la
transformación a una rotación alrededor del eje indicado por el vector con la cantidad indicada.
vector indicado.
cantidades indicadas.
rotación alrededor del eje indicado por el vector con la cantidad indicada.
Comprende que la rotación y el escalamiento son con respecto al origen del mundo, no al respecto al objeto
que debe ser dibujado. Si el objeto no está en el origen este también se moverá a un lugar diferente, que no es
lo que queremos. Así para p.ej. hacer girar un objeto sobre su propio eje-x, primero debemos trasladar al
origen, después hacerlo girar, y finalmente trasladarlo de regreso a su posición. Esto es para qué son las
Los ejemplos siguientes podrían explicar esto mejor. Asumiendo que tenemos un sprite spr que queremos
dibujar en la posición (100,100,10). Podemos usar el código siguiente para hacer esto
{
d3d_transform_set_translation(100,100,10);
draw_sprite(spr,0,0,0);
d3d_transform_set_identity();
}
Nota que porque usamos una transformación ahora deberíamos dibujar el sprite en la posición (0,0). (¡Esto
asume que la instancia actual tiene una profundidad de 0! Si no estas seguro, primero pon la profundidad.) Si
usáramos esto en nuestro tirador en primera persona no veríamos el sprite. La razón es que es todavía paralelo
al plano-xy. Queremos hacerlo girar más de 90 grados a lo largo del eje-x (o el eje-y). Entonces tenemos que
añadir una rotación. Recuerda el orden: primero debemos hacer girar el sprite y luego transformarlo. Entonces
{
d3d_transform_set_identity();
d3d_transform_add_rotation_x(90);
d3d_transform_add_translation(100,100,10);
draw_sprite(spr,0,0,0);
d3d_transform_set_identity();
}
En ocasiones quieres guardar temporalmente la transformación actual, por ejemplo para añadir una
transformación adicional y luego restaurar a la anterior (esto a menudo pasa dibujando modelos jerárquicos).
Para este fin puedes empujar la transformación actual sobre un montón y luego sacarlo del montón para
hacerlo la transformación actual de nuevo. Las funciones siguientes existen para esto:
Devuelve si hay espacio sobre el montón para empujarlo allí (si olvidas sacar la transformación en
quita del montón. Devuelve si había tras una transformación sobre el montón.
Niebla
La niebla puede ser usada en juegos de 3D para hacer que los objetos a la distancia luzcan borrosos o incluso
desaparecer. Esto ayuda en la creación de la atmósfera y hace posible el no dibujar los objetos que están a lo
empleo de niebla. color indica el color de la niebla. principio indica la distancia a la cual la niebla debe
comenzar. fin indica la distancia a la cual la niebla es máxima y nada puede ser visto más allá.
Iluminación
Las escenas que dibujas con las funciones anteriores parecen bastante planas porque no hay luz alguna. El
color si las caras son iguales, independiente de su orientación. Para crear escenas que luzcan más realistas
debes permitir la iluminación y poner las luces en los sitios correctos. La creación de escenas correctamente
Usando la iluminación, para cada vértice de un polígono el color es determinado. Después, el color de los
píxeles internos está basado en el color de estos vértices. Hay dos modos que puede ser hecho: El polígono
entero consigue el mismo color, o el color suavemente es interpolado sobre el polígono. Por defecto smooth
Para usar la iluminación obviamente tienes que definir luces. Dos diferentes luces existen: luces direccionales
(como el sol), y luces posiciónales. La luz tiene un color. (Sólo soportamos la luz difusa, no la reflexión
ind es el índice de la luz (usa un pequeño número positivo). (dx,dy,dz) es la dirección de la luz. col es
el color de la luz (a menudo quieres usar c_white. Esta función no enciende la luz.
es el índice de la luz (usa un pequeño número positivo). (x,y,z) es la posición de la luz. rango indica
qué lejos brilla la luz. La intensidad de la luz se disminuirá sobre este rango. col es el color de la luz.
ind.
La manera en que un objeto refleja la luz depende del ángulo entre la dirección de la luz y el normal de la
superficie, es decir el vector que señala lejos de la superficie. De ahí, para crear objetos iluminados no sólo
tienes que proporcionar la posición de los vértices, sino también sus normales. Para estas cuatro funciones
(x,y,z) a la primitiva, con vector normal (nx,ny,nz), y con su propio color y valor alpha.
d3d_vertex_normal_texture(x,y,z,nx,ny,nz,xtex,ytex) Añade el
vértice (x,y) a la primitiva, con vector normal (nx,ny,nz), y con la posición (xtex,ytex) en la textura,
d3d_vertex_normal_texture_color(x,y,z,nx,ny,nz,xtex,ytex,col
,alpha) Añade el vértice (x,y) a la primitiva, con vector normal (nx,ny,nz), y con la posición
Note que para las formas básicas que puedes dibujar las normales automáticamente son puestas
correctamente.
Creando modelos
Cuando necesitas dibujar modelos complejos es muy costoso llamar a todas las funciones diferentes una y otra vez en
cada step. Para evitarte esto, puedes crear modelos. Un modelo consiste de un número de primitivas y formas de
dibujo. Una vez que creas un modelo puedes dibujarlo en diferentes lugares y en diferenes posiciones con una simple
Antes de listar las funciones de modelos es importante resaltar un aspecto: el manejo de texturas. Como ya se ha
explicado antes, las texturas se toman de sprites y backgrounds. Los índices de las texturas pueden ser diferentes en
cada momento, por lo que los modelos no contienen información sobre ellas. Sólo cuando dibujas un modelo debes
especificar información sobre la textura. De esta forma, sólo puedes usar una textura en cada modelo. Si necesitas usar
más texturas puedes combinar varias (y tener mucho cuidado con las coordenadas de cada una) o usar múltiples
modelos. La ventaja de esto es que puedes dibujar sencillamente el mismo modelo con diferentes texturas.
Para crear, cargar, guardar y dibujar modelos se usan las siguientes funciones:
d3d_model_create() Crea un nuevo modelo y devuelve su índice, que debe ser usado en las demás
funciones.
aplicará al modelo. Usa -1 para no usar texturas. Si quieres rotar o escalar el modelo usa las rutinas de transformación
ya hemos visto en los capítulos anteriores, sólo que en lugar de especificar una textura ahora daremos el índice del
modelo.
d3d_model_vertex_texture_color(ind,x,y,z,xtex,ytex,col,alpha) Añade el
(x,y,z) al modelo con el vector normal (nx,ny,nz) y sus propios valores de color y transparencia.
d3d_model_vertex_normal_texture(ind,x,y,z,nx,ny,nz,xtex,ytex) Añade el
vértice (x,y,z) al modelo con el vector normal (nx,ny,nz) y coordenadas de textura (xtex, ytex).
d3d_model_vertex_normal_texture_color(ind,x,y,z,nx,ny,nz,xtex,ytex,col
,alpha) Añade el vértice (x,y,z) al modelo con el vector normal (nx,ny,nz) con valores de color y textura.
Además de primitivas también puedes añadir formas al modelo. Las funciones son análogas a las ya vistas usando el
modelo.
d3d_model_cylinder(ind,x1,y1,z1,x2,y2,z2,hrepeat,vrepeat,closed,steps)
Añade un cilindro al modelo.
d3d_model_cone(ind,x1,y1,z1,x2,y2,z2,hrepeat,vrepeat,closed,steps) Añade
un cono al modelo.
elipsoide al modelo.
Crear script.
Lo primero antes de crear un script hay que entender que es un
script. Un script es un trozo de codigo que nos sirve por ejemplo para
acortar una accion que usaremos en varios objetos para no tener qu
ir todo el rato copiando y pegando el codigo y resumirlo en una sola
linea. Por ejemplo en tu script que se llama draw pones:
draw_sprite(pers,1,x,y) y cuando quieras aplicarlo en vez de poner
draw_sprite pones draw. En este ejemplo no nos sirve de mucho pero
mas adelante vereis como si influye.
Como una tabla de una línea que espera valores.Esta puede contener
los valores que queramos solo que aquí la he hecho de 5 celdas para
ahorrar espacio. A los valores de la array podemos acceder de esta
manera:
Array[1]=7
Array[2]=672
Array[5]=1
7 672 1
Array[1]-=3
Array[3]=-4
4 -4 1
mediana=mean(numero[1],numero[2],numero[3],numero[4],numero
[5],numero[6],numero[7],numero[8],numero[9],numero[10])
draw_text(50,50,string(mediana))
[HP]
hp = 375
maxhp = 500
[nivel]
exp = 5075
nivel = 4
ini_open(fname);
Operaciones de escritura:
Operaciones de lectura:
ini_close();
Bucles
Hola a todos, hoy explicaré los bucles.
Bucle do while.
Bucle for.
Sentencia repeat.
Así, puede decirse que una FSM es un sistema que tiene varios
estados, pasa de uno a otro por unas transiciones y realiza una serie
de acciones. Por ejemplo, una bombilla puede tener dos estados:
encendida y apagada. Para pasar de un estado a otro (transición),
hay que accionar un interruptor. Por último, la bombilla puede
realizar acciones cuando se encuentre en un estado concreto. Por
ejemplo, si está encendida realizará la acción de iluminar una
habitación.
Estados
Los estados son el núcleo de las FSMs. Una FSM puede tener tantos
estados como definamos, pero solamente podrá estar en uno de ellos
a la vez. En el ejemplo anterior de la bombilla, ésta puede estar
encendida o apagada, pero no puede estar encendida y apagada a la
vez. Es decir, los estados de la bombilla serán encendida y
apagada.
Transiciones
Las transiciones llevan a la FSM de un estado a otro. Para que ocurra
una transición, deben cumplirse unas condiciones.
Ejemplo:
En nuestro RPG hay un minero que tiene 2 estados: buscar oro en la
mina y descansar. Cuando está buscando oro el minero se va
cansando. Al llegar a un nivel de fatiga, el minero pasará al estado de
descanso hasta que recupere las fuerzas. Así, tenemos 2 condiciones:
• Si estás cansado->Descansa
• Si te has recuperado->Vuelve a buscar oro
Ejemplo:
El minero del ejemplo anterior tiene ahora 3 estados:
• Buscar oro
• Descansar
• Comer
El paso de un estado a otro se define con esta tabla de transiciones:
Condición\Estado actual Buscar oro Descansar Comer
Cansado Descansar X X
Recuperado X Buscar oro X
Hambriento Comer Comer X
Saciado X X Buscar oro
Ejemplo:
El minero puede realizar la acción encender linterna, si se
encuentra buscando oro en la mina y todo está muy oscuro. Es decir,
si se encuentra en el estado Buscar oro. Sin embargo esta acción no
tendrá sentido si se encuentra en los estados Comer o Descansar.
Ejemplo:
Si el minero está en el estado Buscar oro realizará la acción Cavar
para cavar y buscar oro. Esta acción se realizará regularmente, o sea
que es una acción de estado.
Cuando el minero va a descansar, antes de tumbarse a dormir se
quita las botas para estar más cómodo (acción Descalzarse).
Cuando está durmiendo ronca (acción Roncar) y cuando se despierta
se vuelve a poner las botas sólo si va a volver a buscar más oro
(acción Calzarse). Al despertarse, el minero siempre soltará un gran
bostezo (acción Bostezar). Así:
• La acción Descalzarse es una acción de entrada, ya que la
ejecutaremos siempre una vez al entrar en el estado Descansar.
• La acción Roncares una acción de estado, ya que la
ejecutaremos repetidamente mientras estemos en el estado
Descansar.
• La acción Calzarse es una acción de transición, ya que sólo la
ejecutaremos una vez si pasamos del estado Descansar al estado
Buscar oro.
• La acción Bostezares una acción de salida, ya que la
ejecutaremos siempre una vez al salir del estado Descansar.
Estados globales
Nuestra FSM puede tener unos estados que sean capaces de
interrumpir los demás y ejecutarse inmediatamente. Normalmente
son estados que realizan una acción y luego vuelven al estado que se
estaba ejecutando.
Ejemplo:
Nuestro minero acaba de ganar un nuevo estado global: Ir al baño.
Este estado tiene prioridad absoluta, cuando el minero tenga ganas
de ir al baño (es decir, cuando se cumpla la condición que lanza la
transición a este estado, por ejemplo vejiga llena) dejará lo que
está haciendo e irá al baño de inmediato. Después, volverá a lo que
estaba haciendo, como si nada hubiera pasado.
Consideraciones finales
Las máquinas de estados finitos son muy utilizadas pese a ser un
sistema algo viejo. Las razones principales son las siguientes:
• Son rápidas y fáciles de implementar.
• Son fáciles de depurar para buscar errores
• Usan poco tiempo de procesamiento, debido a su sencillez
• Son intuitivas y faciles de comprender por la mente humana
• Son flexibles, se pueden expandir facilmente y se acoplan muy bien
a otros métodos (redes neuronales, algoritmos genéticos...) para
crear sistemas híbridos
Los juegos profesionales utlizan FSMs en todo momento,
combinándolas además con otros métodos para tareas que requieran
un nivel de inteligencia más desarrollado. Las FSMs se basan en
reglas simples (condiciones if), por ello se suele decir que el sistema
realmente no "piensa". Sin embargo, una FSM correctamente
desarrollada y detallada puede dar esa impresión. Su gran rapidez
hace que sean perfectas para tareas sencillas y repetitivas en las que
no se necesite un alto nivel de inteligencia, como por ejemplo buscar
caminos, realizar tareas repetitivas...Además, para las tareas que
requieran inteligencia más avanzada se pueden integrar
perfectamente con otros métodos.
Un ejemplo clarísimo es el juego Los Sims, en el que prácticamente
todo el comportamiento de los personajes se ha programado con
FSMs.
Descripción general
Vamos a hacer un juego de disparos visto desde arriba en el que el
jugador puede moverse por un mapa enorme buscando la salida
mientras se enfrenta a multitud de enemigos. Será una especie de
Quake pero visto desde arriba.
Paredes
Creamos un objeto y lo llamamos objPared. Creamos un sprite que
será un cuadrado de 32x32 de color negro y no transparente y se lo
asignamos a este objeto.
Jugador
El jugador será una máquina de estados finitos. Hasta ahora
habíamos visto cómo usar la arquitectura de las FSM en personajes
controlados por el ordenador. Sin embargo ahora vamos a usar una
FSM para un personaje controlado por el jugador. Recordad que las
FSM se rigen por condiciones que hacen que el personaje pase de un
estado a otro. Primero, debemos definir sus características.
execute_string(estado+"(1)");
image_angle = direction;//rotar el sprite (s?lo GM6)
switch argument0{
case 0://Entrar
//Aquí podemos hacer que el personaje grite, o crear una explosión...
tiempoMuerte = tiempoMuerteMax;
sprite_index = sprPlayerMuerto;
break;
case 1://Ejecutar
tiempoMuerte -= 1;
if (tiempoMuerte = 0){
if (vidas > 0){
vidas -= 1;
salud = saludMax;
estado = "Vivo";
execute_string(estado+"(0)");
}else game_end();
}
break;
case 2://Salir
break;
case 3://Transición
break;
}
switch argument0{
case 0://Entrar
sprite_index = sprPlayerVivo;
break;
case 1://Ejecutar
if (salud <= 0){
estado = "Muerto";
execute_string(estado+"(0)");
exit;
}
scrAndar(keyboard_check(vk_up), keyboard_check(vk_down),
keyboard_check(vk_left), keyboard_check(vk_right), velocidad);
scrDisparar(keyboard_check(ord('C')));
break;
case 2://Salir
break;
case 3://Transición
break;
}
if (izquierda) direction += 5;
else if (derecha) direction -= 5;
scrAndar(keyboard_check(ord('W')),
keyboard_check(ord('S')),keyboard_check(ord('A')),keyboard_check(
ord('D')), velocidad);
velocidad = 4;
//scrDisparar(disparar)
if (tiempoDisparo = 0){
if (argument0){
with (instance_create(x, y ,objBala)){
speed = 8;
direction = direction;
parent = id;
}
tiempoDisparo = tiempoDisparoMax;
}
}else tiempoDisparo -=1;
tiempoDisparoMax = 15;
tiempoDisparo = 0;
Listo, este script crea una bala que se mueve a una velocidad de 8
píxeles por step en la dirección en la que mire la instancia que llame
al script. Además, le asignamos la id de la instancia a la variable
parent de la bala recién creada. Esto nos servirá para poder sabe
luego quién ha disparado cada bala.
Ya hemos terminado con el jugador, ha sido sencillo, no? ;)
Balas
Lógicamente, para poder disparar necesitamos crear un objeto y
llamarlo objBala. Además, creamos un sprite que será un círculo rojo
de 5x5 y con el centro en (2,2). En este nuevo objeto, vamos al
Evento de Colisión con el objeto objPared y ponemos el siguiente
código:
instance_destroy();
Enemigos (I)
Por fín hemos llegado al meollo del asunto: los enemigos. Como
siempre, empezamos definiendo sus estados:
• Atacar: en este estado el enemigo se acercará al jugador hasta
una distancia en la que empezará a disparar. Si su salud es menor
que la mitad de la salud del personaje mandará un mensaje por radio
a la instancia más cercana para que venga a ayudarle.
• Huir: Si su salud es menor que la cuarta parte de la salud del
jugador intentará huir a un lugar seguro siempre que no está solo.
• Patrullar: el enemigo simplemente irá caminando por el laberinto.
• Ayudar: cuando reciba una petici?n de ayuda irá corriendo a
socorrer a su compañero en apuros.
• EnemigoMuerto: en esta ocasión será un estado global que
podrá interrumpir todos los demás.
/*CARACTERISTICAS DE LA FSM*/
estado = "Patrullar";
estado_global = "";
mensaje = -1;
//scrJugadorVisible()
if (instance_exists(objPlayer)){
var target;
target = instance_nearest(x, y, objPlayer);
if ((collision_line(x, y, target.x, target.y, objPared, false, true) <
0)&& (abs(point_direction(x, y, target.x, target.y)-direction) <= 60))
return true;
else return false;
}else return false;
}
image_angle = direction;
switch argument0{
case 0://Entrar
break;
case 1://Ejecutar
if ((scrJugadorVisible())||(recibirBalazo)){
recibirBalazo = false;
direction = point_direction(x, y, objPlayer.x, objPlayer.y);
estado = "Atacar";
}else if (mensaje = 0){
estado = "Ayudar";
execute_string(estado+"(0)");
}else scrVagar();
break;
case 2://Salir
break;
case 3://Transición
break;
}
//scrVagar()
if (place_meeting(x+2*velocidadNormal*cos(degtorad(direction)), y-
2*velocidadNormal*sin(degtorad(direction)), objPared)){
var i;
for (i=-1;i<=1;i+=2){
if
(!place_meeting(x+2*velocidadNormal*cos(degtorad(direction+i*90)
), y-2*velocidadNormal*sin(degtorad(direction+i*90)), objPared)){
direction += i*90;
break;
}else if (i = 1) direction += 180;
}
}
x += velocidadNormal*cos(degtorad(direction));
y -= velocidadNormal*sin(degtorad(direction));
other.salud -=1;
other.recibirBalazo = true;
instance_destroy();
switch argument0{
case 0://Entrar
break;
case 1://Ejecutar
if (!scrJugadorVisible()) estado = "Patrullar";
else if ((scrDebil(4))&&(scrSolo())){
estado = "Huir";
execute_string(estado+"(0)");
}else{
if (!scrAcercarse(objPlayer)) scrPelear();
if ((scrDebil(2))&&(scrSolo())) scrEnviarMensaje(scrNearestFriend());
}
break;
case 2://Salir
break;
case 3://Transición
break;
//scrDebil(energia)
if (instance_exists(objPlayer)){
}
return false;
Navegación
Creamos un objeto y lo llamamos objControlador. Este objeto se
encargará de calcular los caminos libres que seguirán los enemigos.
Su funcionamiento será el siguiente: en un momento determinado un
enemigo le pedirá que calcule un camino para ir de un lugar a otro.
Este objeto calculará el camino y lo devolverá como una path
asociada a la instancia que le hizo la petición. Ahora, la instancia sólo
tiene que seguir ese path y llegar a su destino de forma segura. Para
realizar todo esto vamos a usar la versión de GM del famoso
algoritmo A*.
Con esto creamos una rejilla que divide el room en celdas del tamaño
de los enemigos y marcamos las celdas ocupadas por una pared
como prohibidas.
Si ponéis este código en el Evento Draw de este objeto podréis ver
la rejilla con las celdas marcadas, que coincidirán con donde habéis
puesto las paredes:
mp_grid_draw(rejilla);
//scrAcercarse(objetivo)
if (instance_exists(argument0)){
}
return false;
giro *= -1;
esquiva = esquivaMax;
}else esquiva -= 1;
if
(place_meeting(x+2*velocidadNormal*cos(degtorad(direction+giro)),
y-2*velocidadNormal*sin(degtorad(direction+giro)),objPared))
esquiva = 0;
else{
x += velocidadNormal*cos(degtorad(direction+giro));
y -= velocidadNormal*sin(degtorad(direction+giro));
}
scrDisparar(true);
giro = 90;
tiempoGiroMax = 150;
tiempoGiro = tiempoGiroMax;
Enemigos (II)
El siguiente estado es Huir. En este estado el enemigo intentará huir
hasta la posición del compañero más cercano:
switch argument0{
case 0://Entrar
break;
case 1://Ejecutar
if (!scrAcercarse(scrNearestFriend())) estado = "Patrullar";
break;
case 2://Salir
break;
case 3://Transici?n
break;
Listo!
Ahora creamos el estado global EnemigoMuerto:
switch argument0
{
case 0://Entrar sprite_index = sprEnemyMuerto;
break;
case 1://Ejecutar
break;
case 2://Salir
break;
case 3://Transición
break;
}
Sistema de mensajes
El sistema de mensajes estará controlado por el objeto
objControlador que ya habíamos creado. En su Evento Create
definimos el sistema:
mensajes = ds_queue_create();
mensaje_id=0
mensaje_remitente=ds_map_create();
mensaje_destino=ds_map_create();
//scrEnviarMensaje(Destino)
var Id, Remitente, Destino;
Id = objControlador.mensaje_id;
Remitente = id;
Destino = argument0;
ds_queue_enqueue(objControlador.mensajes, Id);
//Añadir las características asociadas al mensaje a los mapas de
memoria
ds_map_add(objControlador.mensaje_remitente, Id, Remitente);
ds_map_add(objControlador.mensaje_destino, Id, Destino);
if !ds_queue_empty(mensajes){
var Id, Remitente, Destino;
Id = ds_queue_dequeue(mensajes);
Remitente = ds_map_find_value(mensaje_remitente,Id);
ds_map_delete(mensaje_remitente,Id);
Destino = ds_map_find_value(mensaje_destino,Id);
ds_map_delete(mensaje_destino,Id);
Destino.mensaje = 0;
Destino.remitente = Remitente;
}
scrRecibirMensaje();
switch argument0{
case 0://Entrar
mensaje = -1;
break;
case 1://Ejecutar
if (!scrAcercarse(remitente)){
remitente = 0;
estado = "Patrullar";
}
break;
case 2://Salir
break;
case 3://Transición
break;
}
Creando un nivel
Lo único que nos falta es crear un nivel y ponernos a jugar. Creamos
una room y en Creation code inicializamos el room, creando el
objeto controlador, el objeto del jugador, inicializando las
propiedades de la vista y rodeando el nivel de paredes:
var w, h;
w = sprite_get_width(sprPared);
h = sprite_get_height(sprPared);
instance_create(0, 0, objControlador);
var i;
for (i=0;i
instance_create(i, 0, objPared);
instance_create(i, room_height-h, objPared);
}
for (i=0;i
instance_create(0, i, objPared);
instance_create(room_width-w, i, objPared);
}
Conclusiones
Hemos creado un Quake sencillo en 2D basándonos en la arquitectura
de FSM. Hemos usado un montón de scripts, de forma que los
estados de las entidades que tenían más o menos esta forma:
switch estado{
case 0:
break;
case 1:
if (condicion1){
estado = "NuevoEstado1";
}else if (condicion2){
estado = "NuevoEstado2";
}else{
//acciones
}
break;
}
Descripción
La Central de Policía podrá enviar diferentes tipos de mensajes:
• "Atraco en el banco": un aviso que hará que el policía deje lo
que está haciendo y vaya al banco a parar el atraco.
• "Información sobre un criminal": con este mensaje el policía
recibirá un aviso para ir a la comisaría y recibir información sobre un
criminal, lo que le facilitará su captura. El Policía sólo recibirá este
mensaje si se encuentra patrullando.
Sistema de mensajes
Antes de definir el sistema de mensajería, hay que describir las
características que tendrán los mensajes en sí:
• Id: identificador del mensaje.
• Remitente: quien lo envía (id de la instancia que envía el
mensaje).
• Destino: a quién va dirigido (id de la instancia que recibirá el
mensaje).
• Tipo: tipo de mensaje (0=Aviso de atraco, 1=Recibir información,
2=Avisar de un delito").
Enviar mensajes
Con todo esto, ya podemos crear el script enviarMensaje que
servirá para (quién lo diría? :P) enviar mensajes. Este script será
ejecutado directamente por las instancias del policía y la central:
[code]
//enviarMensaje(Destino, Tipo)
var Id, Remitente, Destino, Tipo;
Id = obj_ControlMensajes.mensaje_id;
Remitente = id;
Destino = argument0;
Tipo = argument1;
var prioridad;
switch Tipo{//Calcular la prioridad del mensaje dependiendo del tipo
case 0:
prioridad = 3;
break;
case 1:
prioridad = 1;
break;
case 2:
prioridad = 2;
break;
}
/Añadir el mensaje a la cola de prioridad
ds_priority_add(obj_ControlMensajes.mensajes, Id, prioridad);
//Añadir las caracter?sticas asociadas al mensaje a los mapas de
memoria
ds_map_add(obj_ControlMensajes.mensaje_remitente, Id,
Remitente);
ds_map_add(obj_ControlMensajes.mensaje_destino, Id, Destino);
ds_map_add(obj_ControlMensajes.mensaje_tipo, Id, Tipo);
//Mostrar el mensaje
show_message("Mensaje
enviado:#"+obj_ControlMensajes.mensaje_texto[Tipo]);
[/code]
Observad que para enviar un mensaje sólo necesitamos pasarle 2
argumentos al script: Destino y Tipo. Esto es debido a que el valor
de la Id del mensaje se coge de la variable
obj_ControlMensajes.mensaje_id y por tanto no hace falta pasársela
como argumento al script ya que éste podrá acceder a ella
directamente. Como sólo hará 1 única instancia del objeto
obj_ControlMensajes no habrá lugar a errores. A su vez, el
Remitente será la id de la instancia que envía el mensaje, es decir,
la id de la instancia que ejecute el script enviar_mensaje(). Por lo
tanto, esta variable también será accesible directamente desde el
script.
El siguiente paso será (cómo no? :P) crear el sistema para recibir y
leer los mensajes.
if (estado_global != ""){
execute_string(estado_global+"(1)");
}else{
cansancio += 1;
if (cansancio >= cansancio_max){?
estado_global = "IrACasaYDormir";?
execute_string(estado_global+"(0)");?
}else{
if (mensaje=0){
estado = "IrAlBancoYDetenerAtraco";
execute_string(estado+"(0)");
mensaje = -1;
}
execute_string(estado+"(1)");
}
}
[/code]
El policía podía recibir 2 tipos de mensajes:
• 0: Atraco en el banco
1: Información sobre un criminal
La Central de policía
La Central de policía será algo muy sencillo: básicamente mandará
mensajes al policía aleatoriamente y enviará unidades cuando el
policía se lo pida.
Y el script Vigilar:
[code]
switch argument0{
case 0://Entrar
break;
case 1://Ejecutar
if (mensaje = 2){
estado = "EnviarUnidad";
mensaje = -1;
}else{
if (tiempo_envio>1){
tiempo_envio -= 1;
}else{
enviarMensaje(obj_policia,floor(random(2)));
tiempo_envio = 15+floor(random(15));
}
}
break;
case 2://Salir
break;
case 3://Transición
break;
}
[/code]
Mejoras y ampliaciones
• En el ejemplo que hemos creado los mensajes se envían y reciben
instant?neamente. Podéis añadir mensajes con retardo, de forma que
el destinatario no los reciba inmediatamente sino que se guarden en
la cola durante un tiempo (sería una nueva característica de los
mensajes y requeriría de otro mapa de memoria). Así podríais ver
cómo funciona el sistema cuando hay varios mensajes circulando a la
vez.
• Crear varias instancias de los policías y hacer que se envíen
mensajes entre ellos. Por ejemplo, hacer que pidan ayuda al policía
con mayor agresividad.
• Ahora que ya tenemos un objeto controlador
(obj_ControlMensajes) del sólo existirá un instancia podemos
quitar el código para salir del juego pulsando Escape del evento step
del policía y ponerlo en el evento step de este objeto.
Consideraciones finales
Los sistemas de mensajes son una caracter?sica muy importante en
los juegos. Seguramente no habíais oído hablar antes de ellos, pero
seguro que os suenan si los llamo por su otro nombre: Eventos. Así
es, los juegos profesionales utilizan este sistema para crear y
controlar los eventos del juego, como en GM. Cualquier objeto que
use el sistema de mensajes podrá comunicarse con los demás. Con
unos ejemplos ver?is muy clara la importancia de esto:
• Cuando un personaje intenta abrir una puerta, le manda un
mensaje a ésta diciéndole "Abrete!". La puerta recibe el mensaje y
puede abrirse o responder con otro mensaje diciendo que está
cerrada y que para abrirla necesita una llave.
• Un jugador empuja una caja. La caja recibe el mensaje de que está
siendo empujada y debe moverse.
• Una bala golpea a un enemigo. La bala manda un mensaje al
enemigo diciendo que debe morir y éste cambia su animación, emite
un grito...
• Un jugador de un equipo de fútbol avanza con el balón. Puede
mandar un mensaje a los delanteros para que se desmarquen, otro a
los defensas para que cubran su posición y otro a un compañero para
avisarle de que le va a pasar el balón.
Planificación
Antes de nada, hay que definir primero los estados de nuestro
policía:
• BuscarYAtraparCriminales: en este estado el policía buscará
criminales. Si encuentra uno, intentará atraparlo.
• EntregarCriminal: cuando detenga a un criminal, el policía lo
llevará a la comisaría.
• IrACasaYDormir: ir a casa cuando está cansado a echar un
sueñecillo. Este será un estado global, capaz de interrumpir
cualquier otro estado para ejecutarse.
• IrAlBarYBeber: ir al bar a tomar unas merecidas copas.
Muy bien, ahora sólo nos falta hacer que cada estado se ejecute. Para
ello, en el Evento Step ponemos lo siguiente:
[code]
if (estado_global != ""){
?execute_string(estado_global+"(1)");
}else{
?execute_string(estado+"(1)");
}
[/code]
Veremos este código con más detalle cuando creemos la lógica de los
estados, pero de modo genreal podemos decir que hace lo siguiente:
Si hay un estado global (if (estado_global != "")) se ejecuta. Si no,
ejecutamos el estado actual.
Para ello, tenemos que crear unos scripts con los nombres de los
estados. Dentro de estos scripts definiremos las acciones que
realizará cada uno.
Acciones
Como ya vimos en el tutorial básico sobre FSMs, las acciones
dependen del estado actual. También vimos que había varios tipos de
acciones (de entrada, de salida, de transición y de ejecución). Por
ello, cada estado se comportará de forma diferente al entrar en él,
salir de él, pasar a otro estado o ejecutarse normalmente.
Creamos un script llamado BuscarYAtraparCriminales y ponemos
lo siguiente:
[code]
switch argument0{
case 0://Entrar
break;
case 1://Ejecutar
break;
case 2://Salir
break;
case 3://Transición
break;
}
[/code]
Este script tendrá 1 argumento, que controlará las acciones que se
ejecuten. Así:
• Si argument0 = 0 se ejecutarán las acciones de entrada.
• Si argument0 = 1 se ejecutarán las acciones de ejecución.
• Si argument0 = 3 se ejecutarán las acciones de transición.
En este estado no habrá acciones de salida.
Describimos las acciones que se realizarán en el estado
BuscarYAtraparCriminales:
• Al entrar en el estado el policía nos dirá cuantas detenciones le
faltan por hacer para cubrir su cupo.
• Al ejecutar el estado el policía intentará detener a los criminales.
Si lo consigue, los llevará a la comisaría, si no, aumentará su
agresividad.
• Al salir del estado el policía no hará nada, es decir, no habrá
acciones de salida.
• En este estado no habrá acciones de transición.
Ahora que ya hemos definido las acciones, las metemos en el script:
[code]
switch argument0{
case 0://Entrar
show_message("Me faltan "+string(detenciones_max-
detenciones)+" detenciones");
break;
case 1://Ejecutar
agresividad_criminal = floor((random(10)));
if (agresividad >= agresividad_criminal){
show_message("He detenido a un criminal");
detenciones += 1;
//CAMBIAMOS DE ESTADO
estado = "EntregarCriminal";
execute_string(estado+"(0)");//Ejecutar las acciones de
entrada al nuevo estado
}else{
show_message("Se me ha escapado un criminal. El tenia
agresividad "+string(agresividad_criminal)+" y yo solo
"+string(agresividad));
agresividad += 1;
}
break;
case 2://Salir
break;
case 3://Transición
break;
}
[/code]
Casi hemos terminado, sólo nos queda hacer que el policía se vaya
cansando y que lance el estado global IrACasaYDormir cuando está
demasiado cansado. Volvemos al Evento Step y modificamos el
código que teníamos:
[code]
/*SI MANTENEMOS PULSADO ESCAPE SALIMOS DEL JUEGO*/
if (keyboard_check(vk_escape)){
game_end();
}
if (estado_global != ""){
execute_string(estado_global+"(1)");
}else{
cansancio += 1;
if (cansancio >= cansancio_max){
estado_global = "IrACasaYDormir";
execute_string(estado_global+"(0)");
}else{
execute_string(estado+"(1)");
}
}
[/code]
Ampliaciones
Podéis practicar con este ejemplo, a?adiendo cosas como:
• Una nueva característica llamada lugar que indique dónde se
encuentra el policía en cada momento. Así, esta variable podrá tener
los valores "casa", "comisaria", "calle" y "bar". Hacer que el
policía diga cuándo va de un sitio a otro (tendráis que usar las
acciones de entrada y salida de cada estado). Por ejemplo, en la
acción de salida del estado BuscarYAtraparCriminales el policía
dirá "Me voy a la comisaria" (si es que va a la comisaría ;) ) y en la
acción de entrada del estado EntregarCriminal dirá algo como "Ya
estoy en la comisaria".
• Nuevos estados, como Comer, Investigar (para encontrar
información sobre dónde se encuentran los criminales)...Tendráis que
definir nuevas características para controlar estos estados, por
ejemplo hambre para controlar el estado Comer.
• Comportamiento avanzado: por ejemplo, añadiendo el estado
Investigar podéis poner una nueva característica llamada
inteligencia. Si el policía tiene más inteligencia que agresividad tal
vez decida investigar un poco antes de intentar detener criminales. Si
fracasa al intentar detener un criminal podéis hacer que su
inteligencia se reduzca.
• Hacer que la agresividad se reduzca después de descansar y que la
inteligencia aumente. También podéis hacer que el policía descanse
un tiempo aleatorio, así no será capaz de recuperarse del todo, la
agresividad no se reducirá siempre lo mismo ni la inteligencia crecerá
lo mismo...de esta forma las características del policía cambiarán
constantemente y cada vez se comportará de una manera diferente!
Consideraciones finales
De una manera sencilla hemos creado una IA, bastante simple pero
muy robusta. Además, se puede ampliar muy facilmente añadiendo
nuevos estados y acciones.
En la próxima entrega introduciremos la Central de Policía, capaz de
enviar mensajes al policía dándole información sobre los
criminales...por ejemplo, podremos despertar al policía con un aviso
urgente para que vaya a detener el robo de un banco!
Listas.
veremos qué son las listas, cómo se usan y porqué resultan una
herramienta tan poderosa.
Definición
Extraído del Manual Online de CGM:
A priori se puede decir que una lista es como un array: una colección
de valores que se guardan en orden. Sin embargo las listas poseen
unas funciones que hacen que sean una de las estructuras más
utilizadas para el manejo de grandes cantidades de datos de manera
eficiente.
Para poder usar una lista, primero debemos crearla con la función
ds_list_create(). Esta función nos devuelve la id de la nueva lista
creada, que tendremos que usar para llamar a las demás funciones
de la lista. Para conocer las propiedades de la lista tenemos 2
funciones:
ds_list_size(idLista) Devuelve el número de valores en la lista.
ds_list_empty(idLista) Devuelve true si la lista está vacía.
Para saber si la lista está vacía tenemos 2 opciones: llamar a la
función ds_list_empty(idLista) o chequear si su tamaño es igual a 0:
//Este código
if (ds_list_empty(idLista){...}
Con estas funciones ya podemos crear una lista, ver su tamaño, ver
si está vacía y eliminarla. Ahora veremos las funciones para usar
realmente las listas.
Funciones
ds_list_add(idLista,val) Inserta el valor al final de la lista.
ds_list_insert(idLista,pos,val) Inserta el valor en la posición pos. La
primera posición es 0 y la última es igual al tamaño de la lista menos
1.
ds_list_replace(idLista,pos,val) Reemplaza el valor en la posición pos
por val.
ds_list_delete(idLista,pos) Elimina el valor en la posición pos.
ds_list_find_index(idLista,val) Devuelve la posición en la lista del
valor val. Si no encuentra el valor en la lista devuelve -1.
ds_list_find_value(idLista,pos) Devuelve el valor en la posición pos.
ds_list_sort(idLista,ascend) Ordena los valores de la lista. Si ascend
es true o 1 los ordena de forma ascendente (de menor a mayor), en
caso contrario los ordena de manera descendente (de mayor a
menor).
Como véis, no hace falta llevar una cuenta de cuántos puntos hemos
metido o quitado.
Por último, unimos todos los puntos con una línea y los dibujamos:
Evento Draw
if (ds_list_size(listaPuntosX) ] 1){//si hay dos ó más puntos en la
lista
draw_set_color(c_black);
draw_primitive_begin(pr_linestrip);
var i;
for (i=0; i[ds_list_size(listaPuntosX); i+=1){
draw_vertex(ds_list_find_value(listaPuntosX,
i),ds_list_find_value(listaPuntosY,i));
}
draw_primitive_end();
}
draw_set_color(c_red);
var i;
for (i=0;i[ds_list_size(listaPuntosX); i+=1){//dibujar los puntos
draw_circle(ds_list_find_value(listaPuntosX,
i),ds_list_find_value(listaPuntosY,i), 2, false);
}
Ya está, poned este objeto en una room vacía y cread arte XD.
La principal ventaja que vemos en este ejemplo de las listas sobre los
arrays es que no hace falta que llevemos la cuenta de los puntos que
metemos en la lista. Así mismo, tampoco necesitamos chequear nada
cuando quitamos puntos. Si lo hubiéramos hecho con arrays,
tendríamos que haber chequeado antes de borrar un punto de la lista
que el array tenía algún elemento o nos huberia salido un error de
índice negativo en el array.
Colas.
veremos otra estructura de memoria que podemos usar con GM: las
colas. Es recomendable aunque no necesario haber leído antes el
artículo sobre listas.
Definición
Funciones
ds_queue_enqueue(id,val) Introduce el valor en la cola.
ds_queue_dequeue(id) Devuelve el último valor de la cola (el último
en introducirse) y lo elimina de la cola.
ds_queue_head(id) Devuelve el valor al principio de la cola, esto es,
el primero que se introdujo, pero no lo elimina de la cola.
ds_queue_tail(id) Devuelve el último valor de la cola pero no lo
elimina.
¿Entonces para qué sirve esto? Las colas se utilizan cuando hay que
calcular algo en cada step y luego utilizar los resultados 1 sola vez.
Evento Draw:
-Dibujar un triángulo azul apuntando al objetivo
direction = point _direction(x, y, objetivo.x, objetivo.y);
var d, dir;
d = 16;
dir = degtorad(direction);
draw_set_color(c_blue);
draw_primitive_begin(pr_trianglestrip);
draw_vertex(x+(2*d)*cos(dir)+0*sin(dir), y+0*cos(dir)-
(2*d)*sin(dir));
draw_vertex(x+(-d/2)*cos(dir)+(-d)*sin(dir), y+(-d)*cos(dir)-(-
d/2)*sin(dir));
draw_vertex(x+(-d/2)*cos(dir)+d*sin(dir), y+d*cos(dir)-(-
d/2)*sin(dir));
draw_primitive_end();
Las colas son muy rápidas y usan muy poca memoria si se utilizan
correctamente, ya que tienen la capacidad de acceder y eliminar los
valores que guardan al momento. Y para recorrerlas ni siquiera
necesitamos acceder al índice de cada elemento!
Colas de prioridad.
Definición
Es decir, las colas de prioridad funcionan igual que las colas normales
pero además poseen una nueva característica llamada prioridad que
nos permite acceder a los valores de la cola de otra forma.
Funciones
ds_priority_add(id,val,prio) Añade el valor con la prioridad
especificada a la cola.
ds_priority_change_priority(id,val,prio) Cambia la prioridad del valor
especificado al nuevo valor.
ds_priority_find_priority(id,val) Devuelve la prioridad del valor
especificado.
ds_priority_delete_value(id,val) Elimina el valor (con su prioridad) de
la cola de prioridad.
ds_priority_delete_min(id) Devuelve el valor con la menor prioridad y
lo elimina de la cola.
ds_priority_find_min(id) Devuelve el valor con la menor prioridad
pero no lo elimina de la cola.
ds_priority_delete_max(id) Devuelve el valor con la mayor prioridad
y lo elimina de la cola.
ds_priority_find_max(id) Devuelve el valor con la mayor prioridad
pero no lo elimina de la cola.
Evento Create:
-Crear la cola de prioridad
-Inicializar variables
enemigos = ds_priority_create();
objetivo = 0;
objetivo = ds_priority_delete_min(enemigos);
Mapas de Memorias.
Los mapas de memoria son una estructura que nos puede facilitar
muchísimo la escritura de códigos complicados.
Definición
Funciones
ds_map_add(id,key,val) Añade la pareja llave (key)-valor (val) al
mapa.
ds_map_replace(id,key,val) Reemplaza el valor correspondiente a la
llave con un nuevo valor.
ds_map_delete(id,key) Elimina la pareja llave-valor especificada del
mapa. Si hay varias parejas con la misma llave sólo 1 es eliminada.
ds_map_exists(id,key) Devuelve true si la llave existe en el mapa.
ds_map_find_value(id,key) Devuelve el valor correspondiente a la
llave.
ds_map_find_previous(id,key) Devuelve la mayor llave que sea
menor que la indicada.
ds_map_find_next(id,key) Devuelve la menor llave que sea mayor
que la indicada.
ds_map_find_first(id) Devuelve la menor llave del mapa.
ds_map_find_last(id) Devuelve la mayor llave del mapa.
Evento Draw:
-Dibujar al jugador
draw_set_color(c_blue);
draw_circle(x, y, 16, false);
/*VELOCIDADES*/
velocidades = ds_map_create();
ds_map_add(velocidades, "normal", 4);
ds_map_add(velocidades, "doble", 8);
ds_map_add(velocidades, "triple", 16);
Evento Draw:
-Dibujar la bala según su tipo
draw_set_color(ds_map_find_value(colores, tipo));
draw_circle(x, y, 4, false);
DLL.
external_define
(dll,name,calltype,restype,argnumb,arg1type,arg2type, …)
Define una función externa. dll es el nombre del archivo dll. name es
el nombre de las funciones. calltype es la convención de llamada
empleada. Usa dll_cdecl o dll_stdcall. restype es el tipo del
resultado. Usa ty_real o ty_string. argnumb es el número de
argumentos (0-11). Después, para cada argumento debes
especificar su tipo. Para ello usa nuevamente ty_real o ty_string.
Cuando hay más de 4 argumentos todos ellos deben ser de tipo
ty_real.
external_call(id,arg1,arg2,…)
Llama a la función externa con el id y los argumentos dados.
Necesitas proporcionar el número correcto de argumentos del tipo
correcto (real o string). La función devuelve el resultado de la función
externa.
Historia:
No es muy complicada. Pacman, un comecocos (monstruillo que
come cocos) recorre unos escenarios de estructura laber?ntica
engullendo todos los cocos que encuentra a su paso. El objetivo de
nuestro amigo es comerse todos los cocos para pasar de nivel. Pero
en el escenario hay otros monstruillos, los fantasmas, a los que no les
hace ninguna gracia que Pacman se zampe los cocos de su territorio.
Los fantasmas intentaran impedirle a Pacman que se coma los cocos
y lo perseguir?n sin tregua por toda la pantalla para com?rselo. Pero
Pacman podr? defenderse: Repartidos por la pantalla hay un peque?o
n?mero de supercocos que s?per vitaminizan a Pacman por un
peque?o espacio de tiempo y que lo hacen invulnerable y con la
capacidad de comerse a los fantasmas.
APARTADO GR?FICO
- Unos objetos que no son otros que los cocos, los supercocos y
las frutas que recolecta el comecocos por el escenario.
- Unos muros que forman los pasillos del laberinto por donde se
mueven los personajes.
APARTADO SONORO
APARTADO DE PROGRAMACI?N
- El Comecocos
- Los Fantasmas
- Los cocos
- Los supercocos
- La fruta
Tiene que quedar clara la diferencia entre los sprites y los objetos.
Los Sprites son las im?genes (animadas) que no tienen ning?n
comportamiento. Los objetos tienen normalmente un sprite para
representarlas pero los objetos tienen un comportamiento.
Guardar como. Guarda el archivo del dise?o del juego bajo el nombre
que le queramos dar.
Crear Ejecutable. Una vez que tu juego este listo deber?s distribuirlo
en forma de ejecutable para que otros puedan jugarlo.
A?ADIENDO SPRITES
Para a?adir un sprite, elije a?adir Sprite del men? a?adir, o utiliza el
bot?n correspondiente en la barra de herramientas. Te aparecer?
una ventana.
En esta ventana podr?s indicar el nombre del sprite. Todos los
sprites (y el resto de los elementos que intervienen en nuestro juego)
Los objetos son los elementos del juego que hacen cosas. La
mayor?a del tiempo tienen un sprite como representaci?n gr?fica de
modo que se pueden ver. Tienen comportamiento porque pueden
reaccionar a ciertos acontecimientos. Todas las cosas que ves en el
juego (a excepci?n del fondo) son objetos. Los personajes, los
monstruos, las bolas, las paredes, son todos objetos. Tambi?n puede
haber ciertos objetos que no se ven pero que controlan ciertos
aspectos del juego. Para Agregar un objeto a tu juego, elije agregar
objeto del men? de agregar (Add). Aparecer? la siguiente pantalla:
A la izquierda hay informaci?n de car?cter general sobre el objeto.
En el centro hay la lista de los eventos que pueden suceder al objeto.
En la derecha hay las diversas acciones que el objeto puede realizar.
Lo primero que tienes que hacer es darle al objeto un nombre.
Despu?s elegir el sprite para el objeto. Luego indicaras si el objeto es
visible (en la mayor?a de los casos si lo es) y si es s?lido (como una
pared).
haciendo click sobre ellos con el bot?n derecho del rat?n. Puedes
arrastrar objetos por la pantalla si mantienes pulsado el bot?n
“Control” del teclado m?s el bot?n izquierdo del rat?n. Desde la
pesta?a “settings”, adem?s de fijar la anchura y la altura de la
pantalla podemos fijar la velocidad del juego. ?ste es el n?mero de
frames por segundo. Cuanta m?s alta es la velocidad, mas suave es
el movimiento. Tambi?n deberemos fijar un nombre y una descripci?n
para cada una de las pantallas que creemos.
Sigue las indicaciones del profe.
Procederemos a crear las pantallas de nuestro juego.
EVENTOS
Ahora puedes agregar acciones a otro evento. Haz clic con el bot?n
izquierdo del rat?n sobre el evento adecuado para seleccionarlo y
arrastra la acci?n a la lista.
Puedes cambiar el orden de las acciones en la lista arrastrando los
iconos. Si mantienes presionada la tecla mientras arrastras
una acci?n, crear?s una copia de dicha acci?n. Puedes inclusive
arrastrar acciones entre diferentes listas de diferentes objetos.
Cuando haces clic con el bot?n derecho sobre una acci?n, se muestra
un men? desde el cual puedes eliminar la acci?n (tambi?n puedes
hacer esto usando la tecla ), o copiar y pegar acciones.
Cuando mantienes el cursor del rat?n sobre una acci?n, se muestra
una descripci?n m?s detallada sobre la misma.
Para eliminar el evento seleccionado y todas sus acciones, presiona el
bot?n Delete. (Los eventos sin acciones son eliminados
autom?ticamente cuando cierras la ventana por lo que no hay
necesidad de que lo hagas tu mismo). Si deseas asignar las acciones
a un evento diferente (porque por ejemplo, has decidido emplear una
tecla diferente para las acciones) presiona el bot?n Change y
selecciona el nuevo evento. (?El nuevo evento no debe haber sido
empleado antes!).
Como se mencion? arriba, para agregar un evento, presiona el bot?n
Add Event. Se muestra la siguiente ventana:
Aqu? seleccionas el evento que deseas agregar.
ACCIONES
STEP
Step>If the number of instances is a value
Indicamos que si el numero del objeto coco es igual a 0
Inicio de bloque
Ejecutar sonido de “nivel acabado”
Sleep 2000 miliseconds (esperar 2 segundos).
Si existe una siguiente room….
Ir a la siguiente room…
Aqu? podemos definir una transici?n: Un efecto que se produce al
cambiar de pantalla.
De lo contrario…
(De lo contrario, de no haber mas rooms significaria que el jugador
ha terminado el juego, por lo tanto…)
Inicio de un bloque.
Ense?ar la tabla de records.
Reiniciar el juego.
Final del bloque 1.
Final del bloque 2.
(con speed 1)
(Relative)
COLLISION EVENT WITH OBJECT FANTASMA_ASUSTADO
OUTSIDE ROOM
CREATE
STEP
(32*32)
OUTSIDE ROOM
FANTASMA ASUSTADO
ALARM 0
BONUS
CREATE
ALARM 0
ALARM 1
[b]SCRIPTS[/b]
OUTSIDE_WRAP
{
if (x < 0 && hspeed <0> room_width && hspeed > 0) x = -
sprite_width + sprite_xoffset;
if (y < 0 && vspeed <0> room_height && vspeed > 0) y = -
sprite_height + sprite_yoffset;
}
ADAPT_DIRECTION
{
if (hspeed == 0)
{
if (random(3)<1 && place_free(x-4,y))
{ hspeed = -4; vspeed = 0;}
if (random(3)<1 && place_free(x+4,y))
{ hspeed = 4; vspeed = 0;}
}
else
{
if (random(3)<1 && place_free(x,y-4))
{ hspeed = 0; vspeed = -4;}
if (random(3)<1 && place_free(x,y+4))
{ hspeed = 0; vspeed = 4;}
}
}
Glosario.
A
• alpha Transparencia. Es un parámetro que se utiliza en las funciones de dibujo avanzadas y que indica el
nivel de transparencia de lo que se está dibujando: 0 es totalmente transparente y 1 es totalmente opaco.
• argumento Los argumentos son los datos que se le pasan a una función para que ésta opere con ellos.
Por ejemplo, una función que sume dos números necesitará que le digas que números tiene que sumar. Esos
B
• background Fondo. Es la imagen que se pone de fondo al room para que sea el escenario. Puedes
agregar varias imágenes distintas y hacer que cada una se mueva a diferente velocidad cuando el personaje
avance, haciendo que las imágenes más cercanas se muevan más rápido que las más lejanas. Así consigues
• boolean Booleano. Es un tipo de variable que sólo puede tener dos valores: true o false. Es decir, si una
variable booleana no es true entonces es false.
D
• depth Profundidad. Es una propiedad de las instancias que sirve para decidir cuál debe dibujarse por
encima de las demás cuando chocan, cuál se sitúa primero en el room...Puede ser positiva, negativa ó 0 y
F
• false Falso. En Game Maker false=0, es decir, es lo mismo poner false que poner 0.
• fps Frames por segundo. No es lo mismo que la room speed. La room speed es el valor ideal al que
queremos que el juego se actualice. Sin embargo, si ponemos muchas acciones en el evento step el juego
puede ralentizarse, o si usamos unos sprites de mucha calidad puede que el juego necesite mucha
memoria...esto provoca que el juego se ralentice y en lugar de dibujar todos los frames que queremos en
cada segundo dibuje menos. Los fps son la medida real de la velocidad del juego en cada segundo. Te
interesará que los fps estén siempre lo más cercano posible a la room speed.
• frame Para generar la ilusión de animación, lo que se hace es dibujar la pantalla muchas veces (por
ejemplo 60 veces por segundo) haciendo que su contenido cambie poco a poco. Así, al ver esos cambios de
manera seguida parece que la imagen se esté moviendo. Cada instante en el que se dibuja la pantalla se
denomina frame.
• frame rate Es la velocidad con la que se pasa de un frame a otro. Cuanto mayor sea, más fluidas se
verán las animaciones en la pantalla, pero se requerirá más potencia.
G
• gif Es un tipo de archivo de gráficos comprimido y animado. Ideal para sprites.
H
• height Altura. Es un argumento muy común en las funciones de gráficos.
• highscore El marcador más alto, el récord de puntuación obtenido en el juego. Puedes mostrar una lista
con las puntuaciones más altas (highscore list) con la acción correspondiente.
I
• image blending Mezclado de imágenes. Cuando dos imágenes se superponen (por ejemplo, cuando
chocan dos sprites) el programa debe decidir qué es lo que va a dibujar. Si la instancia de uno de los sprites
tiene menor profundidad que el otro se dibujará éste por encima. Pero si tiene mayor depth se dibujará por
debajo. Si uno de los sprites es parcialmente transparente, se verá parte del otro. Si el modo de mezcla está
en suma se sumarán los colores de los dos sprites en la zona en la que chocan (por ejemplo para causar un
efecto de brillo), pero si está en modo resta se restarán. Como ves, en la parte de los sprites que choca se
decide qué hay que hacer según el modo de mezcla que esté activo (esto también afecta a los backgrounds).
L
• left Izquierda. Es un argumento muy común en las funciones que deben especificar una posición para
indicar dónde debe situarse el borde izquierdo.
M
• midi Es un formato de sonido de muy pequeño tamaño. Los midis tienen menos calidad que los wav o
mp3. Sin embargo su tamaño es mucho menor, lo que hace que no consuman casi memoria. Esto hace que
los midis sean ideales para las músicas de fondo del juego.
P
• path Camino o trayectoria. Game Maker te permite dibujar una trayectoria para que luego la siga una
instancia. Así puedes hacer que un enemigo se mueva siguiendo este camino, o hacer que en un juego de
• png Un tipo de archivo de gráficos de tamaño muy reducido creado para ser transmitido por Internet (png
significa "portable network graphics" o "gráficos transportables por la red").
• pop-up Son los mensajes o ventanas que aparecen de repente y sirven para dar o pedir información al
jugador. Por ejemplo, una ventana que se abra y le pregunte al jugador cómo se llama.
R
• rar Es un tipo de archivo comprimido. Los archivos tipo rar consiguen una gran compresión aunque este
formato no está tan extendido como el zip (ver zip).
• recurso Es lo que utiliza el juego. En Game Maker los recursos se dividen en las siguientes categorías:
sprites, backgrounds, sonidos y música, paths, scripts, time lines, fuentes, objetos y rooms.
• room Habitación o cuarto. En el room es donde se sitúan todos los objetos y donde todo el juego tiene
lugar. Un juego puede tener varias rooms y cada una puede ser un nivel distinto, una pantalla de menú, de
ayuda...
• room speed Es el frame rate que queremos que tenga un cuarto y se mide en frames por segundo.
Por ejemplo, una room speed igual a 30 significa que en cada segundo se dibujan 30 frames.
S
• score Marcador. En el juego, puedes hacer que el marcador se incremente cuando el jugador recoja
• screenshot Es una foto de la pantalla en un momento determinado. Los screenshots son muy útiles
para promocionar tu juego o para usarlos en tablas de récords.
• script Es un trozo de código que puede recibir argumentos y devolver un valor. Los scripts se utilizan
• scroll Es el movimiento del fondo. Game Maker te permite añadir varios fondos distintos y asignar un
movimiento a cada uno, para dar la impresión de que el decorado avanza cuando el personaje se mueve.
• sprite Un sprite es la imagen que se asocia a un objeto y que se dibuja en la pantalla. Los sprites
suelen tener varias imágenes, de forma que si en cada momento se dibuja una imagen distinta parece que se
mueve. El formato más utilizado para los sprites es el de los archivos gif.
• sprite sheet Una sprite sheet (o strip) es una imagen no animada de gran tamaño en la que
aparecen muchas imágenes de un personaje (o varios) mostrando todos los frames de sus animaciones. El
editor de sprites de Game Maker permite crear sprites animados fácilmente a partir de una sprite sheet.
• step Paso. Los juegos que crees con Game Maker se ejecutan por pasos, de forma que si haces que se
pase de un paso a otro rápidamente dará la impresión de que todo se ejecuta continuamente (Ver frame).
T
• tile Para hacer un fondo, a veces es bueno utilizar imágenes pequeñas que, puestas una al lado de otra,
forman un dibujo. Esta técnica se denomina tiling. Por ejemplo: divides el room en casillas del mismo
tamaño y en cada una pones una imagen más pequeña (en una un árbol, en otra una casa,...) y haces que se
repitan para llenar todo el room. Así puedes conseguir fondos muy originales y que consumen muy poca
memoria.
• time line Línea de tiempo. En una time line puedes añadir acciones en momentos concretos, de forma
que estas acciones se ejecutarán en el orden que tú decidas y justo cuando quieras.
• top Arriba. Es un argumento muy común en las funciones que deben especificar una posición para indicar
dónde debe situarse el borde superior.
• true Verdadero. En Game Maker true=1, es decir, es lo mismo poner true que poner 1.
w
• wav Son archivos de sonido de gran calidad, pero que por lo tanto ocupan mucho tamaño y consumen
bastante memoria. Los archivos wav se suelen utilizar para los efectos de sonido (explosiones, gritos,
disparos...).
• width Anchura. Es un argumento muy común en las funciones de gráficos.
Z
• zip Es un tipo de archivo comprimido muy corriente. Al comprimir tu juego, conseguirás que ocupe menos
espacio y así podrás distribuirlo más fácilmente por la red.