You are on page 1of 213

Programando con Game Maker

Si no tienes el programa bajo de www.yoyogames.com

Estructura general del GML.


Como habrás leído antes, el Game Maker contiene un lenguaje de programación interno. Este lenguaje te da mucha

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

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

expresiones, existen los siguientes operadores binarios (en orden de prioridad):

• &&, ||: 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

• *, /, div, mod: multiplicación, división, división entera y módulo.

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

• ~: cambio de signo de bit

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

funcionan para las cadenas y el + concatena cadenas.

Ejemplo

Aquí hay un ejemplo con algunas asignaciones


{
x = 23;
color = $FFAA00;
str = 'hola mundo';
y += 5;
x *= y;
x = y << 2;
x = 23*((2+4) / sin(y));
str = 'hola' + " mundo";
b = (x < 5) && !(x==2 || x==4);
}
Variables extra

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 <nombrevariable1>,<nombrevariable2>,<nombrevariable3>, ...

Por ejemplo, puedes escribir:

{
var xx,yy;
xx = x+10;
yy = y+10;
instance_create(xx,yy,pelota);
}

Accediendo a variables en otras instancias

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.

Así por ejemplo, puedes escribir:

pelota.speed = 0;

Esto cambiará la velocidad de todas las instancias del objeto pelota. Hay ciertos “objetos” especiales.

• self: La instancia actual para la que estamos ejecutando la acción


• other: La otra instancia involucrada en un evento de colisión
• all: Todas las instancias
• noone: Ninguna instancia (tal vez te parezca raro pero puede ser útil como veremos más adelante)
• global: : No es precisamente una instancia, sino un contenedor que almacena variables globales
Por ejemplo, puedes usar las siguientes sentencias:

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

primera y su valor x es asignado al valor global.

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

Una sentencia If tiene esta forma

if (<expresión>) <sentencia >

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

corchetes a las sentencias en la sentencia if. Por lo que mejor usa

if (<expresión>)
{
<Sentencia >
}
else
{
<Sentencia >
}

Ejemplo

El siguiente programa mueve el objeto hacia el medio de la pantalla.

{
if (x<200) {x += 4} else {x -= 4};
}

Sentencia Repeat

Una sentencia repeat tiene esta forma

repeat (<expresión>) <sentencia >

La sentencia es repetida el numero de veces indicado por el valor redondeado de la expresión.

Ejemplo

El siguiente programa crea 5 pelotas en posiciones aleatorias.

{
repeat (5) instance_create(random(400),random(400),pelota);
}

Sentencia While

Una sentencia While tiene esta forma

while (<expresión>) <sentencia>


Mientras la expresión sea verdadera, la sentencia (que puede también ser un bloque) es ejecutada. Ten cuidado con tus

ciclos while. Puedes fácilmente hacer que se repitan eternamente, en cuyo caso el juego se bloqueará y ya no

responderá a los comandos del usuario.

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

mover un objeto a una posición aleatoria).

{
while (!place_free(x,y))
{
x = random(room_width);
y = random(room_height);
}
}

Sentencia Do

La sentencia Do tiene esta forma:

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

una posición aleatoria)

{
do
{
x = random(room_width);
y = random(room_height);
}
until (place_free(x,y))
}
Sentencia For

Una sentencia For tiene esta forma:

for (<sentencia1> ; <expresión> ;<sentencia2>) <sentencia3>

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

expresión sea falsa.

Puede sonar complicado. Debes interpretarlo de la manera siguiente. La primera sentencia inicializa el ciclo for. La

expresión prueba si el ciclo debiera terminar. La

sentencia2 es la sentencia de paso hacia la evaluación del siguiente ciclo.

El uso más común es para llevar un contador hasta cierto valor.

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

ejecución simplemente continúa con el código para la siguiente sentencia case.

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

La sentencia Break tiene esta forma:

break

Si se emplea en un ciclo for, while, repeat, en una sentencia switch o with, finaliza el ciclo o sentencia. Si es empleada

fuera de una de estas sentencias finaliza el programa no el juego).

Sentencia Continue

La sentencia Continue tiene esta forma

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

La sentencia Exit tiene esta forma:

exit

Simplemente termina la ejecución del programa/script actual. (¡No termina la ejecución del juego! Para ello necesitas la

función game_end(); ver más abajo)


Funciones

Una función tiene la siguiente estructura: nombre de la función, seguido por uno o varios argumentos entre paréntesis,

separados por comas (también puede no incluir ningún argumento).

<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

pueden ser empleadas en expresiones. Otras simplemente ejecutan órdenes.

Nota que es imposible usar una función como el lado izquierda de una asignación. Por ejemplo, no puedes escribir

instante_nearest(x,y,obj).speed = 0. En lugar, debes 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

argumentos). Pueden usar también argument[0], etc.

Los scripts también pueden devolver un valor, por lo que pueden ser empleados en expresiones. Para ello debes

emplear la sentencia return:

return <expresión>

¡La ejecución del script termina en la sentencia return!

Ejemplo

Aquí esta la definición de un script que calcula el cuadrado del argumento:

{
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,

escribe el nombre del script con sus argumentos entre paréntesis.

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

las pelotas tienen la misma coordenada y. La sentencia

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

with (<expresión>) <sentencia>

<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

mover todas las pelotas 8 píxeles hacia abajo, puedes escribir

with (pelota) y += 8;

Si deseas ejecutar múltiples declaraciones, colócalas entre corchetes. Por ejemplo, para mover todas las pelotas a una

posición aleatoria, puedes usar

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

todas las pelotas usas

with (pelota) instance_destroy();

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

una multi-línea de comentarios colocando el texto entre /* y */.

Funciones y variables en GML

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

sólo de lectura, es decir, no se puede cambiar su valor.


Los nombres de variables con [0...n] después de ellos son arrays. Se da el intervalo posible de sus índices.

Haciendo cálculos

Game Maker contiene un gran número de funciones para realizar determinadas tareas. Aquí tienes una lista completa

de estas funciones.

Esta sección está dividida en los temas:

Constantes

Game Maker incluye las siguientes constantes matemáticas:

true equivale a 1.
false equivale a 0.

pi equivale a 3.1415...

Funciones de valores reales

Estas son las funciones disponibles para trabajar con números reales:

random(x) Devuelve un valor entre 0 y X. El valor devuelto es siempre menor que X.


choose(val1,val2,val3,...) Devuelve uno de argumentos de forma aleatoria. La función acepta un
máximo de 16 argumentos.

abs(x) Devuelve el valor absoluto de X.


sign(x) Devuelve el signo de X (-1, 0 o 1).
round(x) Devuelve el valor de X redondeado al valor entero más cercano.

floor(x) Devuelve el valor de X redondeado hacia abajo.


ceil(x) Devuelve el valor de X redondeado hacia arriba.
frac(x) Devuelve la parte fraccional de X, que es la parte situada tras el punto decimal.

sqrt(x) Devuelve la raíz cuadrada de X. El valor no debe ser negativo.

sqr(x) Devuelve el cuadrado de X.


power(x,n) Devuelve X elevado a la potencia N.
exp(x) Devuelve E elevado a X.

ln(x) Devuelve el logaritmo neperiano (natural) de X.


log2(x) Devuelve el logaritmo en base 2 de X.
log10(x) Devuelve el logaritmo en base 10 de X.
logn(n,x) Devuelve el logaritmo en base N de X.
sin(x) Devuelve el seno de X (X en radianes).
cos(x) Devuelve el coseno de X (X en radianes).
tan(x) Devuelve la tangente de X (X en radianes).
arcsin(x) Devuelve el arcoseno de X.
arccos(x) Devuelve el arcocoseno de X.
arctan(x) Devuelve la arcotangente de X.
arctan2(y,x) Calcula la arcotangente de (Y/X), y devuelve un ángulo en el cuadrante correcto.

degtorad(x) Convierte grados a radianes.


radtodeg(x) Convierte radianes a grados.
min(val1,val2,val3,...) Devuelve el menor de los valores. La función soporta 16 argumentos. Deben ser
todos números reales o cadenas de texto.

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.

mean(val1,val2,val3,...) Devuelve el promedio de los valores. La función soporta 16 argumentos.


Deben ser todos números reales.

median(val1,val2,val3,...) Devuelve el valor intermedio de los argumentos introducidos. (Cuando el


número de argumentos es parejo, el menor de los dos valores intermedios, es el que devuelve la función. La función

soporta 16 argumentos. Deben ser todos números reales.

point_distance(x1,y1,x2,y2) Devuelve la distancia existente entre el punto situado en (x1,y1) y el


situado en (x2,y2).

point_direction(x1,y1,x2,y2) Devuelve la dirección desde el punto (x1,y1) hacia el punto (x2,y2) en


grados.

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.

is_real(x) Averigua cuando X es un valor real. (Diferenciándolo de una cadena de texto).

is_string(x) Averigua cuando X es una cadena de texto. (Diferenciándolo de un número real).

Funciones de cadenas de texto

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

decimales o una parte exponencial.

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_length(str) Devuelve el número de caracteres de la cadena.


string_pos(substr,str) Devuelve la posición de SUBSTR en STR (0 No encontrado).
string_copy(str,index,count) Devuelve una subcadena de STR, partiendo de la posición INDEX y de
una longitud definida por COUNT.

string_char_at(str,index) Devuelve el carácter situado en la posición INDEX de la cadena STR.

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_insert(substr,str,index) Devuelve una copia de la cadena STR con la subcadena SUBSTR


añadida en la posición INDEX.

string_replace(str,substr,newstr) Devuelve una copia de STR con la primera ocurrencia de


SUBSTR reemplazada por NEWSTR.

string_replace_all(str,substr,newstr) Devuelve una copia de STR con todas las ocurrencias


encontradas de SUBSTR reemplazadas por la subcadena NEWSTR.

string_count(substr,str) Devuelve el número de ocurrencias de la subcadena SUBSTR existentes en


STR.

string_lower(str) Devuelve una copia en minúsculas de la cadena STR.


string_upper(str) Devuelve una copia en mayúsculas de la cadena STR.
string_repeat(str,count) Devuelve una cadena con un número de copias de la cadena STR definido 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.

Trabajando con el tiempo y la fecha

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_current_datetime()Devuelve fecha y hora actual.


date_current_date()Devuelve fecha actual ignorando la hora
.date_current_time()Devuelve hora actual ignorando la fecha.

date_create_datetime(year,month,day,hour,minute,second) Crea un valor fecha-hora


correspondiente a la fecha y hora indicados.

date_create_date(year,month,day) Crea un valor fecha-hora correspondiente a la fecha indicada.


date_create_time(hour,minute,second) Crea un valor fecha-hora correspondiente a la hora
indicada.

date_valid_datetime(year,month,day,hour,minute,second) Muestra si la hora y fecha


indicados son válidos.

date_valid_date(year,month,day) Muestra si la fecha indicada es válida.


date_valid_time(hour,minute,second) Muestra si la hora indicada es válida.
date_inc_year(date,amount) Devuelve una nueva fecha N años después de la fecha indicada. N debe ser
un número entero.

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_week(date,amount) Devuelve una nueva fecha N semanas 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

ser un número entero.

date_inc_minute(date,amount) Devuelve una nueva fecha N minutos después de la fecha indicada. N


debe ser un número entero.

date_inc_second(date,amount) Devuelve una nueva fecha N segundos después de la fecha indicada. N


debe ser un número entero.

date_get_year(date) Devuelve el año actual.


date_get_month(date) Devuelve el mes actual.
date_get_week(date) Devuelve la semana actual.
date_get_day(date) Devuelve el día actual.
date_get_hour(date) Devuelve la hora actual.
date_get_minute(date) Devuelve el minuto actual.
date_get_second(date) Devuelve el segundo actual.
date_get_weekday(date) Devuelve el día de la semana actual.
date_get_day_of_year(date) Devuelve el día del año especificado.
date_get_hour_of_year(date) Devuelve la hora del año especificado.
date_get_minute_of_year(date) Devuelve el minuto del año especificado.
date_get_second_of_year(date) Devuelve el segundo del año especificado.
date_year_span(date1,date2) Devuelve el número de años que hay entre las dos fechas. Reporta los
años incompletos como una fracción.

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_compare_datetime(date1,date2) Compara los dos valores fecha-hora. Devuelve -1, 0, ó 1


dependiendo en si la primera fecha es anterior, igual, o posterior que la segunda.

date_compare_date(date1,date2) Compara los dos valores fecha-hora tomando en cuenta sólo la


parte de la fecha. Devuelve -1, 0, ó 1 dependiendo en si la primera es anterior, igual, o posterior que la segunda.

date_compare_time(date1,date2) Compara los dos valores fecha-hora tomando en cuenta sólo la


parte de la hora. Devuelve -1, 0, ó 1 dependiendo en si la primera es anterior, igual, o posterior que la segunda.

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.

date_date_string(date) Devuelve una cadena indicando la fecha definida en el formato predeterminado


para el sistema.

date_time_string(date) Devuelve una cadena indicando la hora definida en el formato predeterminado


para el sistema.

date_days_in_month(date) Devuelve el número de días que hay en el mes indicado.


date_days_in_year(date) Devuelve el número de días que hay en el año indicado.
date_leap_year(date) Define si el año indicado es un año bisiesto.
date_is_today(date) Define si la fecha indicada es la actual.

Game play
Hay una gran cantidad de variables y funciones que puedes emplear para definir el game play (jugabilidad). Estas en

particular influyen en el movimiento y creación de instancias, el timing, y el manejo de los eventos.

La información sobre el game play se puede encontrar en las siguientes secciones:

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

convierte a valores de hspeed y vspeed). También tenemos la fricción y la gravedad, y la dirección de la


gravedad.

Finalmente, tenemos la función motion_add(dir,speed) para agregar movimiento al actual.

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.

xprevious Su posición x anterior.


yprevious Su posición y previa.

xstart Su posición x inicial en el cuarto.

ystart Su posición y inicial en el cuarto.


hspeed Componente horizontal de la velocidad.

vspeed Componente vertical de la velocidad.


direction Su dirección actual (0-360, contra las manecillas del reloj, 0 = a la derecha).
speed Su velocidad actual (píxeles por step).
friction Fricción actual (píxeles por step).
gravity Cantidad actual de gravedad (píxeles por paso).
gravity_direction Dirección de la gravedad (270 es hacia abajo).
motion_set(dir,speed) Establece el movimiento a la velocidad speed y la dirección dir.
motion_add(dir,speed) Agrega el movimiento al movimiento actual (como una suma vectorial).

Existe un gran número de funciones para ayudarte a definir el movimiento:

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.

place_meeting(x,y,obj) Devuelve si la instancia colocada en la posición (x,y) se encuentra con un el


objeto obj. obj puede ser un objeto en cuyo caso la función devuelve verdadero si se encuentra con una instancia de

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_snap(hsnap,vsnap) Alinea la instancia, como la acción correspondiente.


move_wrap(hor,vert,margin) Teleporta la instancia cuando sale del room al lado opuesto. hor indica si
debe teleportarse horizontalmente y vert indica si debe teleprotarse verticalmente. margin indica cuánto debe
salir el origen de la instancia del room para teleportarse (es decir, un margen alrededor del room). Esta función se usa

normalmente el evento Outside.

move_towards_point(x,y,sp) Mueve la instancia con velocidad sp hacia el punto (x,y).


move_bounce_solid(adv) Rebotar contra objetos sólidos, como la acción correspondiente. adv indica si se
emplea rebote avanzado, que toma en cuenta las paredes inclinadas.

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.

move_outside_solid(dir,maxdist) Mueve la instancia en la dirección dir hasta que no esté al alcance


de un objeto sólido. Si no hay collision en la posición actual, no se mueve la instancia. Puedes especificar la distancia

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.

position_empty(x,y) Indica si no hay nada en la posición (x,y).


position_meeting(x,y,obj) Indica si en la posición (x,y) hay una instancia obj. obj puede ser un objeto,
una id de una instancia, o las palabras clave self, other o all.

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_start(path,speed,endaction,absolute) Comienza un path para la instancia actual.


path es el nombre del path que deseas iniciar. speed es la velocidad con la que la instancia debe moverse por el

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)

2: continuar desde la posición inicial

3: recorrer el path al revés (cambia el signo de la velocidad)

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_end() Termina el path para la instancia actual.


path_index* Índice del path que la instancia sigue. No se puede cambiar directamente, debes utilizar la función
path_start(path,speed,endaction,absolute).
path_position Posición en el path actual. 0 es el principio del path y 1 es el final. Los valores deben estar entre
0 y 1.

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.

Planificación del movimiento

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:

mp_linear_step(x,y,stepsize,checkall) Esta función hace que la instancia de un paso hacia la


posición (x,y). La longitud del paso se indica con el parámetro stepsize. Si la instancia ya ha llegado a esa
posición no se moverá. Si checkall es true la instancia se parará cuando choque con una instancia de cualquier
objeto. Si es false, sólo se parará al chocar con un objeto sólido. Esta función no propone un camino alternativo,

simplemente se parará si encuentra un obstáculo. La función devuelve si se ha alcanzado el destino.

mp_linear_step_object(x,y,stepsize,obj) Igual que la anterior, pero esta vez sólo se tienen en


cuenta las instancias del objeto obj. obj puede ser un objeto o una id de una instancia particular.
mp_potential_step(x,y,stepsize,checkall) Igual que las anteriores, pero en este caso la
instancia intentará esquivar los obstáculos que encuentre. Cuando la instancia se choque con un obstáculo cambiará su

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.

mp_potential_step_object(x,y,stepsize,obj) ) Igual que la anterior, pero esta vez sólo se


tienen en cuenta las instancias del objeto obj. obj puede ser un objeto o una id de una instancia particular.
mp_potential_settings(maxrot,rotstep,ahead,onspot) La función anterior hace su
trabajo usando un número de parámetros que pueden ser cambiados con esta función. El método funciona como sigue:

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

versión registrada de Game Maker.


Las dos primeras funciones usan el acercamiento por movimiento lineal y potencial que se usan en las funciones

anteriores.

mp_linear_path(path,xg,yg,stepsize,checkall) Calcula un path en línea recta para la


instancia desde su posición hasta (xg,yg) usando el paso especificado en stepsize. Usa pasos como en la función
mp_linear_step(). 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 devuelve si se ha encontrado un

path. Si no consigue encontrar un camino, la función devolverá un path hasta la posición donde la instancia quedó

bloqueada.

mp_linear_path_object(path,xg,yg,stepsize,obj) Igual que la anterior, pero esta vez sólo


se tienen en cuenta las instancias del objeto obj. obj puede ser un objeto o una id de una instancia particular.
mp_potential_path(path,xg,yg,stepsize,factor,checkall) Esta función calcula un
camino para instancia desde su posición actual y orientación hasta (xg,yg) usando el paso especificado en

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.

mp_potential_path_object(path,xg,yg,stepsize,factor,obj) Igual que la anterior,


pero esta vez sólo se tienen en cuenta las instancias del objeto obj. obj puede ser un objeto o una id de una
instancia particular.

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

hacer que lo siga.


Este sistema es muy potente (se usa en muchos juegos profesionales) pero requiere que lo planifiques con cuidado.

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

estos parámetros afectan de manera muy notable a la eficiencia del método.

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.

Las funciones para el método de rejilla son:

mp_grid_create(left,top,hcells,vcells,cellwidth,cellheight) Esta función crea


la rejilla. Devuelve un índice que debe ser usado en las demás funciones. Puedes crear y mantener varias rejillas al

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_clear_all(id) Marca todas las celdas como libres.


mp_grid_clear_cell(id,h,v) Marca la celda indicada como libre (la primera celda es la 0,0).
mp_grid_clear_rectangle(id,left,top,right,bottom) Marca como libres todas las celdas
que intersectan el rectángulo definido en coordenadas absolutas del cuarto.

mp_grid_add_cell(id,h,v) Marca ls celdas indicadas como prohibidas.


mp_grid_add_rectangle(id,left,top,right,bottom) Marca todas las celdas que intersectan
el rectángulo como prohibidas.

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).

mp_grid_path(id,path,xstart,ystart,xgoal,ygoal,allowdiag) Calcula el path a


través de la rejilla. 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). 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

independiente de la instancia actual).

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

hay colisión devuelven un valor negativo.

collision_point(x,y,obj,prec,notme) Comprueba si hay una colisión en el punto (x,y) con


instancias del objeto obj.

collision_rectangle(x1,y1,x2,y2,obj,prec,notme) Comprueba si hay una colisión entre el

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.

collision_circle(xc,yc,radius,obj,prec,notme) Comprueba si hay una colisión entre la

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.

collision_ellipse(x1,y1,x2,y2,obj,prec,notme) Comprueba si hay una colisión entre la

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

instancia posee las siguientes variables:

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.

solid Indica si la instancia es sólida o no.


persistent Indica si la instancia es persistente y reaparecerá al moverse a otro cuarto. A veces puedes querer
volver a ponerlo a 0 (por ejemplo, al volver al primer cuarto).
Al trabajar con instancias hay un problema: no es fácil identificar una instancia concreta. No tienen un nombre. Cuando

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

instancia. Afortunadamente, las siguientes variables te ayudan a localizar la id de una instancia:

instance_count* Número de instancias que existen en el cuarto.


instance_id[0..n-1]* La id de la instancia número n.

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.

Puedes hacerlo con el siguiente código:

{
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

éste porque se eliminarán inmediatamente y te saltarás instancias existentes).

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.

Las siguientes funciones se usan para crear y destruir instancias:

instance_create(x,y,obj) Crea una instancia de obj en la posición (x,y). La función devuelve la id de la


nueva instancia creada.

instance_copy(performevent) Crea una copia de la instancia actual. El argumento indica si se debe


ejecutar el evento create en la nueva instancia. La función devuelve la id de la nueva copia.

instance_destroy() Destruye la instancia actual.


instance_change(obj,perf) Cambia la instancia a una del tipo obj. perf indica si se deben ejecutar
los eventos de destrucción y creación.

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

debes entender cómo funcionan.

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.

Las rutinas disponibles son las siguientes:

instance_deactivate_all(notme) Desactiva todas las instancias del cuarto. Si notme es true la


instancia actual no es desactivada (normalmente es lo que se desea).

instance_deactivate_object(obj) Desactiva todas las instancias en el cuarto del objeto especificado.


También puedes indicar all para desactivar todas las instancias o la id de una instancia concreta para desactivarla.
instance_deactivate_region(left,top,width,height,inside,notme) Desactiva
todas las instancias en la región indicada (es decir, todas aquellas cuya caja de contorno está parcial o completamente

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

código en el evento step del personaje del jugador:

{
instance_activate_all();
instance_deactivate_region(view_xview[0],view_yview[0],
view_wview[0],view_hview[0],false,true);
}

Normalmente es mejor usar una región ligeramente mayor que la vista.


Timing

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

disponibles (sólo la primera puede ser cambiada):

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:

sleep(numb) Pausa el juego durante numb milisegundos.

Como debes saber, cada instancia tiene 12 diferentes alarmas que puedes configurar. Para cambiar los valores (u

obtener los valores) de las diferentes alarmas usa la siguiente variable:

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_index Índice de la time line asociada con la instancia. Puedes


establecerlo a una time line en particular para usarla. Ponlo en –1 para dejar de usar la time line para la instancia.

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,

por lo que no se saltará ninguna acción.

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();
}
}

Las siguientes variables y funciones se relacionan con los cuartos (rooms).

room Índice del cuarto actual; puede cambiarse para ir a un cuarto diferente, pero mejor usa las rutinas listadas
abajo.

room_first* Índice del primer cuarto en el juego.

room_last* Índice del ultimo cuarto en el juego.

room_goto(numb) Ir al cuarto con indice numb.

room_goto_previous()Ir al cuarto anterior.

room_goto_next()Ir al siguiente cuarto.


room_restart() Reiniciar el cuarto actual.

room_previous(numb)Devuelve el índice del cuarto anterior a numb (-1 = ninguno) pero no va a él.

room_next(numb) Devuelve el índice del cuarto posterior a numb (-1 =ninguno).

game_end() Finaliza el juego.

game_restart() Reinicia el juego.

Los cuartos tienen varias propiedades adicionales:

room_width* Ancho del cuarto en píxeles.

room_height* Alto del cuarto en píxeles.

room_caption Título de la ventana del cuarto.

room_persistent Indica si el cuarto es persistente.

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).

game_save(string) Guarda el juego al archivo con nombre string.

game_load(string) Carga el juego del archivo con nombre string.

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

las partículas, los contenidos de las estructuras de datos ni la configuración multijugador.

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

muestra el score tú mismo.


score El marcador actual.
lives El número de vidas.
health La energía actual (0-100).
show_score Indica si se muestra el marcador en el título de la ventana.
show_lives Indica si se muestra el número de vidas en el título de la ventana.
show_health Indica si se muestra la energía en el título de la ventana.
caption_score El título empleado para el marcador.
caption_lives El título empleado para el número de vidas.
caption_health El título para la energía.

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

las instancias del objeto dado.

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:

event_type* El tipo del evento que se está ejecutando.


event_number* El número del evento que se está ejecutando.
event_object* El índice del objeto para el cual se está ejecutando el evento actual.
event_action* El índice de la acción que está siendo ejecutada (0 es la primera en el evento, etc.)

Otras variables y funciones

Aquí puedes ver algunas variables y funciones que se refieren a los errores.

error_occurred Indica si ha ocurrido un error


error_last Cadena de texto que indica el último mensaje de error
show_debug_message(str) Muestra la cadena str en modo debug

Las siguientes funciones te permiten saber si ciertas variables existen, darles un valor o leerlo. En todas ellas el nombre

de la variable se pasa como una cadena de texto:

variable_global_exists(name) Devuelve si la variable global con el nombre especificado existe.


variable_local_exists(name) Devuelve si la variable local con el nombre especificado existe para la
instancia actual.

variable_global_get(name) Devuelve el valor de la variable global indicada.


variable_global_array_get(name,ind) Devuelve el valor de índice ind del array global con el
nombre indicado.

variable_global_array2_get(name,ind1,ind2) Devuelve el valor de índice ind1, ind2 del array


bidimensional global con el nombre indicado.

variable_local_get(name) Devuelve el valor de la variable local indicada para la instancia actual.


variable_local_array_get(name,ind) Devuelve el valor de índice ind del array locall con el nombre
indicado.
variable_local_array2_get(name,ind1,ind2) Devuelve el valor de índice ind1, ind2 del array
bidimensional global con el nombre indicado.

variable_global_set(name,value) Otorga el valor indicado a la variable global especificada.


variable_global_array_set(name,ind,value) Otorga el valor indicado al elemento ind del array
global especificado.

variable_global_array2_set(name,ind1,ind2,value) Otorga el valor indicado al elemento


ind 1, ind2 del array bidimensional global especificado.

variable_local_set(name,value) Otorga el valor indicado a la variable local especificada.


variable_local_array_set(name,ind,value) Otorga el valor indicado al elemento ind del array
local especificado.

variable_local_array2_set(name,ind1,ind2,value) Otorga el valor indicado al elemento


ind 1, ind2 del array bidimensional local especificado.

Por ejemplo, puedes escribir:

{
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

variables como cadenas de texto.

Puedes cambiar la prioridad del programa usando la función :

set_program_priority(priority) Cambia la prioridad del programa. Debes indicar un valor


comprendido entre -3 y +3. Usando -3 el programa se ejecutará sólo si no hay otro proceso que requiera tiempo de

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

posible. Se cuidadoso y no olvides guardar tu juego antes de ejecutarlo.


Interacción con el usuario

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.

La información sobre este tema se encuentra en las secciones:

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

este fin, contamos con las siguientes funciones:

keyboard_set_map(key1,key2) Mapea la tecla con el código de tecla key 1 a key2.


keyboard_get_map(key) Devuelve el mapeado actual para una tecla
keyboard_unset_map() Restablece todas sus teclas a su mapa original.

Para chequear si una tecla o botón del ratón en particular han sido presionados puedes emplear las siguientes

funciones. Esto es útil particularmente cuando se presionan varias teclas simultáneamente.

keyboard_check(key) Indica si la tecla con el código de tecla particular está presionado.


keyboard_check_pressed(key) Indica si la tecla con el código de tecla particular fue presionado desde el
último step.

keyboard_check_released(key) Indica si la tecla con el código de tecla particular dejó de presionarse


desde el último step.

keyboard_check_direct(key) Indica si la tecla con el código de tecla es presionada chequeando el


hardware directamente. El resultado es independiente de la aplicación enfocada. Esta función permite algunos chequeos

más. En este caso puedes emplear los códigos vk_lshift, vk_lcontrol, vk_lalt, vk_rshift, vk_rcontrol y vk_ralt para

checar si se presiona la tecla shift, control o alt, ya sea izquierda o derecha

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_key_press(key) Simula presionar la tecla con el código de tecla


keyboard_key_release(key) Simulates a release of the key with the indicated keycode.

Las siguientes constantes para los códigos de tecla son:

vk_nokey Código de tecla que representa que no hay teclas presionadas


vk_anykey Código de tecla que representa que cualquier tecla ha sido presionada.
vk_left Código para tecla de la flecha izquierda
vk_right Código para tecla de la flecha derecha
vk_up Código para tecla de la flecha arriba
vk_down Código para tecla de la flecha abajo
vk_enter Tecla Enter o Intro
vk_escape Tecla Escape
vk_space Tecla Espacio
vk_shift Tecla Shift
vk_control Tecla Control
vk_alt Tecla Alt
vk_backspace Tecla Backspace o Retroceso
vk_tab Tecla Tab
vk_home Tecla Inicio
vk_end Tecla Fin
vk_delete Tecla Suprimir
vk_insert Tecla Insertar
vk_pageup Tecla Re Pag
vk_pagedown Tecla Av Pag
vk_pause Tecla Pausa/Inter
vk_printscreen Tecla Impr Pant/Pet Sis
vk_f1 ... vk_f12 Códigos de tecla para las las teclas funcionales F1 hasta F12
vk_numpad0 ... vk_numpad9 Teclas numéricas en el teclado numérico
vk_multiply Tecla de multiplicación en el teclado numérico
vk_divide Tecla de división en el teclado numérico
vk_add Tecla de suma en el teclado numérico
vk_subtract Tecla de substracción en el teclado numérico
vk_decimal Tecla de punto decimal en el teclado numérico
Para las letras usa por ejemplo ord('A'). (Letras Mayúsculas). Para los dígitos usa por ejemplo ord('5') para
obtener la tecla <5> (no en el teclado numérico). Las siguientes constantes solo sirven para

keyboard_check_direct:

vk_lshift Tecla Shift de la Izquierda


vk_lcontrol Tecla Control de la Izquierda
vk_lalt Tecla Alt de la Izquierda
vk_rshift Tecla Shift de la Derecha
vk_rcontrol Tecla Control de la Derecha
vk_ralt Tecla Alt de la Derecha

Por ejemplo, asumiendo que tienes un objeto que el usuario puede controlar con las teclas del cursor puedes colocar el

siguiente código en el evento step del objeto:

{
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.

Hay algunas funciones adicionales relacionadas con la interacción con el 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.

io_clear() ‘Limpia’ todos los estados del teclado y del ratón.


io_handle() Maneja la entrada y salida por parte del usuario, actualizando los estados del teclado y del ratón.
keyboard_wait() Espera hasta que el usuario presione una tecla del teclado.

El ratón

Para más interacción, las siguientes variables y funciones existes:

mouse_x* Coordenada X del ratón. No puede cambiarse.


mouse_y* Coordenada Y del ratón. No puede cambiarse.
mouse_button Botón del ratón presionado actualmente. Como valores puedes emplear mb_none (ningún
botón), mb_any (cualquier botón), mb_left (botón izquierdo), mb_middle (botón central) o mb_right
(botón derecho).

mouse_lastbutton último botón presionado del ratón.

Para chequear si un botón particular del ratón se presionó puedes usar estas funciones. Esto es muy útil cuando

muchas teclas se presionan simultáneamente.

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.

Hay funciones adicionales relacionadas con la interacción con el ratón:

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.

io_clear() ‘Limpia’ todos los estados del teclado y del ratón.


io_handle() Maneja la entrada y salida por parte del usuario, actualizando los estados del teclado y del ratón.
mouse_wait()Espera hasta que el usuario presione un botón en el ratón.

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.

joystick_exists(id) Indica si el joystick id (1 o 2) existe.


joystick_name(id) Devuelve el nombre del joystick.
joystick_axes(id) Devuelve el número de ejes del joystick.
joystick_buttons(id) Devuelve el número de botones del joystick.
joystick_has_pov(id) Indica si el joystick tiene capacidades point-of-view.
joystick_direction(id) Devuelve el código (vk_numpad1 a vk_numpad9) correspondiente a la dirección
del joystick id (1 o 2).

joystick_check_button(id,numb) Indica si el botón del joystick id es presionado (numb está en el


intervalo 1-32).

joystick_xpos(id) Devuelve la posición (-1 a 1) del eje-x del joystick id.


joystick_ypos(id) Devuelve la posición y del joystick id.
joystick_zpos(id) Devuelve la posición z del joystick id (si es que cuenta con eje z).
joystick_rpos(id) Devuelve la posición del timón del joystick id (del cuarto eje).
joystick_upos(id) Devuelve la posición u del joystick id (del quinto eje).
joystick_vpos(id) Devuelve la posición v del joystick id (del sexto eje).
joystick_pov(id) Devuelve la posición del point-of-view del joystick id. Este es un ángulo entre 0 y 360
grados. 0 es adelante, 90 a la derecha, 180 atrás y 270 a la izquierda. Cuando no se especifica la dirección del point-of-

view devuelve –1.

Gráficos del juego

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.

La información sobre la parte gráfica se encuentra en las secciones siguientes:

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 no cambias el índice de la subimagen actual.

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

siguiente código en el evento step:

{
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

tendrán una profundidad baja (valores negativos).

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).

bbox_right* Lado derecho de la caja de contorno utilizada por la imagen de la instancia


bbox_top* parte superior de la caja de contorno utilizada por la imagen de la instancia.
bbox_bottom* parte inferior de la caja de contorno utilizada por la imagen de la instancia.

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

un rango de 0 a 7; este número indica el fondo al que se aplicarán los cambios):

background_color Color de fondo para el cuarto.


background_showcolor Indica si se debe limpiar la pantalla con el color de fondo.
background_visible[0..7] Indica si el fondo indicado es visible o no. Por ejemplo, para indicar que el
fondo número 3 no debe mostrarse, deberíamos hacerlo así: background_visible[3]=false;

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_index[0..7] Imagen de fondo asignada al fondo indicado.


background_x[0..7] Posición x del fondo.
background_y[0...7] Posición y del fondo.
background_width[0...7]* Anchura de la imagen del fondo.
background_height[0...7]* Altura de la imagen del fondo.
background_htiled[0..7] Indica si el fondo debe repetirse horizontalmente para llenar toda la pantalla.
background_vtiled[0..7] Indica si el fondo debe repetirse verticalmente para llenar toda la pantalla.
background_xscale[0..7] Factor de escalado horizontal 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_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!

background_alpha[0..7] Factor de transparencia usado al dibujar el fondo. 1 es el valor normal (imagen


totalmente opaca) y 0 es totalmente transparente (utiliza valores intermedios para dibujar fondos parcialmente

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...

draw_sprite(sprite,subimg,x,y) Dibuja la subimagen subimg (-1 = subimagen actual) del sprite


con índice sprite con su origen en la posición (x,y) sin teñirlo de ningún color y sin usar transparencia.

draw_sprite_stretched(sprite,subimg,x,y,w,h) Dibuja la subimagen subimg del 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.

draw_sprite_part(sprite,subimg,left,top,width,height,x,y) Dibuja la parte del


sprite indicada con su esquina superior izquierda en (x,y). La parte del sprite que queremos dibujar se indica con

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.

draw_background_stretched(back,x,y,w,h) Dibuja el fondo escalado de forma que ocupe la


región indicada.

draw_background_tiled(back,x,y) Dibuja el fondo repitiéndolo en horizontal y vertical de forma que


llene toda la pantalla.

draw_background_part(back,left,top,width,height,x,y) Dibuja el trozo indicado del


fondo con su esquina superior-izquierda en la posición (x,y).

Las funciones siguientes son versiones extendidas de las funciones anteriores. Estas funciones sólo están

disponibles en la versión registrada de Game Maker.

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_stretched_ext(sprite,subimg,x,y,w,h,color,alpha) Dibuja el sprite


escalado de forma que ocupe la región indicada: esquina superior izquierda en (x,y), anchura w y altura h. color
indica el color de teñido y alpha el factor de transparencia.
draw_sprite_tiled_ext(sprite,subimg,x,y,xscale,yscale,color,alpha) Dibuja
el sprite repetido de forma que cubra toda la pantalla con factores de escala, color de teñido y factor de transparencia.

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

sprite (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 y no sobre su origen.

draw_background_ext(back,x,y,xscale,yscale,rot,color,alpha) Dibuja el fondo

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_tiled_ext(back,x,y,xscale,yscale,color,alpha) Dibuja el fondo


repetido de forma que ocupe todo el cuarto con factores de escala, 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

indicado del fondo.

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

funciones siguientes sirven para dibujar las formas básicas:


draw_clear(col) Pinta todo el cuarto del color especificado.
draw_clear_alpha(col,alpha) Pinta todo el cuarto del color especificado y con el factor de transparencia
indicado (muy útil para superficies).

draw_point(x,y) Dibuja un punto en (x,y) en el color de dibujo actual.


draw_line(x1,y1,x2,y2) Dibuja una línea desde (x1,y1) hasta (x2,y2).
draw_rectangle(x1,y1,x2,y2,outline) Dibuja un rectángulo. outline indica si sólo debe
dibujarse el borde (true) o si el rectángulo debe estar relleno (false).

draw_roundrect(x1,y1,x2,y2,outline) Dibuja un rectángulo redondeado. outline indica si sólo


debe dibujarse el borde (true) o si el rectángulo debe estar relleno (false).

draw_triangle(x1,y1,x2,y2,x3,y3,outline) Dibuja un triángulo. outline indica si sólo debe


dibujarse el borde (true) o si debe estar relleno (false).

draw_circle(x,y,r,outline) Dibuja un círculo con su centro en (x,y) y radio r. outline indica si


sólo debe dibujarse el borde (true) o si debe estar relleno (false).

draw_ellipse(x1,y1,x2,y2,outline) Dibuja una elipse. outline indica si sólo debe dibujarse el


borde (true) o si debe estar rellena (false).

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_button(x1,y1,x2,y2,up) Dibuja un botón. up indica si está pulsado (0) o no (1).


draw_path(path,x,y,absolute) Con esta función puedes dibujar el path indicado en el cuarto con su
comienzo en la posición (x,y). Si absolute es true el path se dibuja en la posición en la que fue definido y los
valores de x e y son ignorados.

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

con estas funciones:

draw_set_color(col) Indica el color que debe usarse para dibujar primitivas.


draw_set_alpha(alpha) Indica el factor de transparencia que debe usarse para dibujar primitivas. Debe
estar comprendido en el rango 0-1. 0 significa totalmente transparente y 1 totalmente opaco.

draw_get_color() Devuelve el color general de dibujo utilizado para dibujar primitivas.


draw_get_alpha()Devuelve el factor de transparencia general de dibujo utilizado para dibujar primitivas.
Hay un gran rango de colores predefinidos:

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:

make_color_rgb(red,green,blue) Devuelve un color con los componentes indicados de rojo, verde y


azul. Los valores indicados deben estar entre 0 y 255 (ambos inclusive).

make_color_hsv(hue,saturation,value) Devuelve un color con los componentes indicados de


brillo, saturación y valor. Los valores indicados deben estar entre 0 y 255 (ambos inclusive).

color_get_red(col) Devuelve el componente de rojo del color.


color_get_green(col) Devuelve el componente de verde del color.
color_get_blue(col) Devuelve el componente de azul del color.
color_get_hue(col) Devuelve el componente de brillo del color.
color_get_saturation(col) Devuelve el componente de saturación del color.
color_get_value(col) Devuelve el componente de valor del color.
merge_color(col1,col2,amount) Devuelve un color resultante de mezclar los dos colores indicados. La
mezcla se determina por el parámetro amount: un valor igual a 0 corresponde a col1, un valor de 1 corresponde a
col2 y un valor intermedio hace que se mezclen los dos colores.

También puedes utilizar las siguientes funciones misceláneas:


draw_getpixel(x,y) Devuelve el color del píxel en la posición (x,y) del cuarto. Esta función no es muy rápida,
así que úsala con cuidado.

screen_save(fname) Salva una imagen bmp de la pantalla en el archive especificado. Útil para crear
screenshots.

screen_save_part(fname,x,y,w,h) Salva la parte de la pantalla indicada a un archive bmp.

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

vertical respecto a esa posición.

Para dibujar texto disponemos de las funciones siguientes:

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

draw_text(x,y,string) Dibuja el texto indicado en la posición (x,y) usando el color y el factor de


transparencia de dibujo generales. Los símbolo #, chr(13) o chr(10) (tecla ENTER o de salto de línea) son interpretados

como caracteres de salto de línea. De esta forma puedes dibujar textos de varias líneas (Usa \# para dibujar el símbolo

#).

draw_text_ext(x,y,string,sep,w) Similar a la función anterior pero ahora puedes especificar 2 cosas


más. Primero de todo, sep indica la distancia de separación entre las líneas del texto si es que tiene varias líneas. Usa
-1 para obtener la distancia por defecto. Por último, w indica la anchura del texto en píxeles. Las líneas más largas que
esta distancia se dividen en nuevas líneas cuando encuentren un espacio en el texto o el símbolo -. Utiliza el valor -1 si

no quieres que el texto se divida automáticamente en varias líneas.

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

funciones sólo están disponibles en la versión registrada de Game Maker!

draw_text_transformed(x,y,string,xscale,yscale,angle) Dibuja el texto indicado en la


posición (x,y) con factores de escala y rotado angle grados en sentido antihorario.
draw_text_ext_transformed(x,y,string,sep,w,xscale,yscale,angle) Combina las
funciones draw_text_ext() y draw_text_transformed(). De esta forma es posible dibujar textos
multilíneas rotados o escalados.

draw_text_color(x,y,string,c1,c2,c3,c4,alpha) Dibuja el texto indicado aplicando a cada


esquina el color indicado en el orden arriba-izquierda, arriba-derecha, abajo-derecha, abajo-izquierda y con factor de

transparencia alpha (0-1).


draw_text_ext_color(x,y,string,sep,w,c1,c2,c3,c4,alpha) Similar a
draw_text_ext() pero con vértices coloreados.
draw_text_transformed_color(x,y,string,xscale,yscale,angle,c1,c2,c3,c4
,alpha) Similar a draw_text_transformed()pero con vértices coloreados.
draw_text_ext_transformed_color(x,y,string,sep,w,xscale,yscale,angle,c
1,c2,c3,c4,alpha) Similar a draw_text_ext_transformed()pero con vértices coloreados.
Funciones avanzadas de dibujo

Esta funcionalidad sólo está disponible en la versión registrada de Game Maker

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.

draw_point_color(x,y,col1) Dibuja un punto en la posición (x,y) con el color indicado.


draw_line_color(x1,y1,x2,y2,col1,col2) Dibuja una línea desde (x1,y1) hasta (x2,y2),
interpolando el color entre col1 y col2.
draw_rectangle_color(x1,y1,x2,y2,col1,col2,col3,col4,outline) Dibuja un
rectángulo. Los cuatro colores indican los colores de los vértices superior-izquierdo, superior-derecho, inferior-derecho

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_roundrect_color(x1,y1,x2,y2,col1,col2,outline) Dibuja un rectángulo


redondeado. col1 es el color en el centro del rectángulo y col2 en el borde. 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).

draw_circle_color(x,y,r,col1,col2,outline) Dibuja un círculo en (x,y) con radio r. col1


es el color en el centro y col2 en el borde. outline indica si sólo debe dibujarse el contorno del círculo (true) o si
éste debe estar relleno (false).

draw_ellipse_color(x1,y1,x2,y2,col1,col2,outline) Dibuja una elipse. col1 es el


color en el centro y col2 en el borde. outline indica si sólo debe dibujarse el contorno de la elipse (true) o si éste
debe estar rellena (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.

Las siguientes funciones sirven para tratar 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

apropiada, por ejemplo draw_clear(color).

surface_free(id) Libera la memoria utilizada por la superficie.


surface_exists(id) Devuelve si la superficie con la id especificada existe.

Estas funciones nos dan información sobre la superficie:

surface_get_width(id) Devuelve la anchura de la superficie.


surface_get_height(id) Devuelve la altura de la superficie.
surface_get_texture(id) Devuelve la textura correspondiente a la superficie. Esto se puede usar para
dibujar objetos texturizados con la imagen de la superficie.

Estas dos funciones manejan el mecanismo de dibujo:

surface_set_target(id) Indica que la superficie con la id correspondiente es el objetivo de dibujo. Esto


significa que todas las funciones de dibujo que se llamen actuarán sobre la superficie. Simplemente resetea la

proyección para que cubra sólo a la superficie.

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

dibujar sobre la superficie!

Las funciones siguientes nos permiten manejar las superficies:

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.

Para dibujar superficies hay muchas posibilidades:

draw_surface(id,x,y) Dibuja la superficie en la posición (x,y).


draw_surface_stretched(id,x,y,w,h) Dibuja la superficie en la posición (x,y) y escalada de forma
que tenga la anchura y altura indicadas.

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(id,left,top,width,height,x,y) Dibuja la parte indicada de la superficie


en la posición (x,y).

draw_surface_ext(id,x,y,xscale,yscale,rot,color,alpha) Dibuja la superficie en la


posición (x,y), con factores de escala, rotación, transparencia y tiñiéndola con el color indicado (utiliza c_white si no

quieres teñir la superficie).

draw_surface_stretched_ext(id,x,y,w,h,color,alpha) Dibuja la superficie en la posición


(x,y) escalada para que ocupe la región indicada con factor de transparencia y tiñiéndola del color especificado.

draw_surface_tiled_ext(id,x,y,xscale,yscale,color,alpha) Dibuja la superficie


repitiéndola para que ocupe todo el cuarto pero con factores de escala, transparencia y tiñiéndola del color indicado.

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

superior derecha, esquina inferior derecha y esquina inferior izquierda.


Por último, existen dos funciones para copiar superficies:

surface_copy(destination,x,y,source) Copia la superfcie en la posición (x,y) en la superficie


indicada por destination.
surface_copy_part(destination,x,y,source,xs,ys,ws,hs) Copia la parte indicada de la
superfcie en la posición (x,y) en la superficie indicada por destination.

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.

Al usar superficies debes tener algunas cosas en cuenta:

• Nunca debes cambiar el objetivo de dibujo mientras dibujas estás dibujando sobre la pantalla. Es decir, nunca

uses las funciones surface_set_target() ni surface_reset_target() en el evento draw. Esto causará

graves problemas en la proyección.

• 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

las superficies se destruirán automáticamente.

• 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.

• Las superficies no se guardarán al guardar un juego.

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:

• background. El fondo del que se crea la tile.


• left, top, width, height. La parte del fondo usada en la tile (izquierda, arriba, anchura y
altura).

• x,y. Posición de la esquina superior izquierda del tile en el room.


• depth. Profundidad del tile. Puedes escoger la profundidad que quieras, haciendo que las tiles aparezcan
entre varias instancias distintas.

• visible. Indica si es visible.


• xscale, yscale. Factores de escalado horixontal y vertical (el tamaño normal es 1).
• blend. Color de teñido usado al dibujar el tile.
• alpha. Factor de transparencia utilizado al dibujar el tile.

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.

Las funciones siguientes sirven para manejar tiles:

tile_add(background,left,top,width,height,x,y,depth) Añade una nueva tile al cuarto


con las propiedades indicadas. Esta función devuelve la id de la nueva tile que se puede usar más tarde en el resto de

funciones.

tile_delete(id) Elimina el tile con la id especificada.


tile_exists(id) Devuelve si existe una tile con la id especificada.

Las funciones siguientes nos dan información sobre las tiles:

tile_get_x(id) Devuelve la posición x de la tile con la id especificada.


tile_get_y(id) Devuelve la posición y de la tile con la id especificada.
tile_get_left(id) Devuelve el valor de la propiedad left (izquierda) de la tile con la id especificada.

tile_get_top(id) Devuelve el valor de la propiedad top (arriba) de la tile con la id especificada.


tile_get_width(id) Devuelve la anchura de la tile con la id especificada.
tile_get_height(id) Devuelve la altura de la tile con la id especificada.
tile_get_depth(id) Devuelve la profundidad de la tile con la id especificada.
tile_get_visible(id) Devuelve si la tile con la id especificada es visible o no.
tile_get_xscale(id) Devuelve el factor horizontal de escalado de la tile con la id especificada.
tile_get_yscale(id) Devuelve el factor vertical de escalado de la tile con la id especificada.
tile_get_background(id) Devuelve el fondo de la tile con la id especificada.
tile_get_blend(id) Devuelve el color de teñido de la tile con la id especificada.
tile_get_alpha(id) Devuelve el factor de transparencia de la tile con la id especificada.

Las funciones siguientes sirven para manipular las propiedades de las tiles:

tile_set_position(id,x,y) Coloca la tile con la id especificada en la posición x,y.


tile_set_region(id,left,top,width,height) Cambia la región del tile con la id especificada
sobre su fondo.

tile_set_background(id,background) Cambia el fondo de la tile con la id especificada.


tile_set_visible(id,visible) Cambia la visibilidad de la tile con la id especificada.
tile_set_depth(id,depth) Cambia la profundidad de la tile con la id especificada.
tile_set_scale(id,xscale,yscale) Cambia los factores de escala de la tile con la id especificada.
tile_set_blend(id,color) Cambia el color de teñido de la tile con la id especificada. Sólo disponible en
la versión registrada!

tile_set_alpha(id,alpha) Cambia la transparencia de la tile con la id especificada.

Las funciones siguientes manejan capas de tiles, es decir, grupos de tiles que tienen la misma profundidad:

tile_layer_hide(depth) Oculta todas las tiles con la profundidad indicada.


tile_layer_show(depth) Muestra todas las tiles con la profundidad indicada.
tile_layer_delete(depth) Elimina todas las tiles con la profundidad indicada..
tile_layer_shift(depth,x,y) Mueve todas las tiles con la profundidad indicada siguiendo el vector
(x,y). Esta función se puede usar para capas de tiles móviles.

tile_layer_find(depth,x,y) Devuelve la id de la tile con la profundidad indicada que se encuentra en la


posición (x,y). Si no se encuentra ninguna tile la función devuelve -1. Cuando se encuentran varias tiles en esa posición

con la misma profundidad se devuelve la primera.

tile_layer_delete_at(depth,x,y) Elimina la tile con la profundidad indicada que se encuentra en la


posición (x,y). Si se encentran varias tiles en esa posición y con la profundidad indicada se eliminan todas.

tile_layer_depth(depth,newdepth) Cambia la profundidad de todas las tiles con la profundidad


indicada a la nueva profundidad. Es decir, con esta función puedes mover capas enteras de tiles a otra 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

cambiar el modo sólo están disponibles en la versión registrada de Game Maker.

display_get_width() Devuelve la anchura del display en píxeles.


display_get_height()Devuelve la altura del display en píxeles.
display_get_colordepth()Devuelve la profundidad de color en bits.
display_get_frequency()Devuelve la frecuencia de refresco del monitor.
display_set_size(w,h) Cambia la altura y anchura del display a los nuevos valores indicados. La función
devuelve true si los cambios se aplicaron con éxito y false en caso contrario (Debes tener en cuenta que sólo algunas

combinaciones están permitidas, por ejemplo 800x600, 1024x768 ó 1280x1024).

display_set_colordepth(coldepth) Cambia la profundidad de color a la especificada. En general sólo


se permite usar profundidad de color de 16 ó 32 bits. La función devuelve true si los cambios se aplicaron con éxito y

false en caso contrario.

display_set_frequency(frequency) Cambia la frecuencia de refresco del monitor a la especificada.


Sólo unas pocas frecuencias están permitidas. Por ejemplo, puedes ajustar este valor a 60 y usar un room_speed del

mismo valor para lograr animaciones fluidas de 60 frames por segundo. La función devuelve true si los cambios se

aplicaron con éxito y false en caso contrario.

display_set_all(w,h,frequency,coldepth) Cambia todos los valores a la vez. Para valores que

no quieras cambiar utiliza -1. La función devuelve true si los cambios se aplicaron con éxito y false en caso contrario.

display_test_all(w,h,frequency,coldepth) Comprueba si los valores especificados están

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:

display_mouse_get_x() Devuelve la coordenada x del puntero en el display.


display_mouse_get_y()Devuelve la coordenada x del puntero en el display.
display_mouse_set(x,y) Cambia la posición del puntero a los valores indicados.

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

durante el juego. Para ello puedes usar las funciones siguientes:


window_set_visible(visible) Hace que la ventana se vuelva visible o invisible. Normalmente la
ventana es visible durante todo el juego. Cuando la ventana sea invisible el programa no ejecutará ni recibirá los

eventos del teclado.

window_get_visible() Devuelve si la ventana es visible.


window_set_fullscreen(full) Hace que la ventana ocupe toda la pantalla (modo pantalla completa) o
no.

window_get_fullscreen() Devuelve si la ventana ocupa toda la pantalla.


window_set_showborder(show) Indica si el borde alrededor de la ventana debe mostrarse (si la ventana
está a pantalla completa el borde no se muestra).

window_get_showborder() Devuelve si el borde de la ventana es visible cuando ésta no está a pantalla


completa.

window_set_showicons(show) Indica si deben mostrarse los iconos de la ventana (minimizar, maximizar


y cerrar). Si la ventana está a pantalla completa no se muestran.

window_get_showicons() Devuelve si los iconos de la ventana son visibles.


window_set_stayontop(stay) Indica si la ventana de be mostrarse siempre por encima de otras
ventanas que puedan existir.

window_get_stayontop() Devuelve si la ventana se mantiene siempre por encima de las demás.


window_set_sizeable(sizeable) Indica si el jugador puede cambiar el tamaño de la ventana. El
jugador sólo podrá hacer esto si el borde de la ventana es visible y la ventana no está a pantalla completa.

window_get_sizeable() Devuelve si el jugador puede cambiar el tamaño de la ventana.


window_set_caption(caption) Indica el título de la ventana. Normalmente esto se especifica al definir el
room y se cambia usando la variable room_caption. Por ello, esta función no es útil a no ser que dibujes tú mismo el

room en lugar de dejar que Game Maker lo haga automáticamente. El título de la ventana sólo es visible cuando ésta

tiene borde visible y no está a pantalla completa.

window_get_caption() Devuelve el título de la ventana.


window_set_cursor(curs) Indica el puntero que se usará en la ventana. Puedes indicar una de las
siguientes constantes:

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).

window_get_cursor() Devuelve el cursor utilizado en la ventana.


window_set_color(color) Indica el color de la parte de la ventana que no se usa para mostrar el room.
window_get_color() Devuelve el color de la ventana.
window_set_region_scale(scale,adaptwindow) Si la ventana es mayor que el cuarto actual el
cuarto se muestra centrado en la ventana. Con esta función es posible indicar si queremos que el cuarto sea escalado

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

del cuarto) es positivo.

window_get_region_scale() Devuelve el factor de escala de la región de dibujo.

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

ventana vuelva al modo normal.

window_set_position(x,y) Indica la posición de la ventana.


window_set_size(w,h) Indica el nuevo tamaño de la ventana. Observa que si el tamaño indicado es menor
que la región de dibujo se mantendrá lo suficientemente grande como para que la región entre por completo dentro de

é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).

window_center() Centra la ventana en la pantalla.


window_default() Da a la ventana los valores por defecto de tamaño y posición (centrada).
window_get_x() Devuelve la coordenada x de la ventana.
window_get_y()Devuelve la coordenada y de la ventana.
window_get_width()Devuelve la anchura de la ventana.
window_get_height()Devuelve la altura de la ventana.
Puede que nunca necesites utilizar las funciones de posicionamiento de l ventana ya que Game Maker lo realizará

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:

window_mouse_get_x() Devuelve la coordenada x del puntero en la ventana.


window_mouse_get_y()Devuelve la coordenada y del puntero en la ventana.
window_mouse_set(x,y) Indica la nueva posición del puntero en la ventana.

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

concreto siempre permanezca visible en una vista.

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

supone que la vista principal es la vista 0):

{
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.

view_enabled Indica si las vistas están activadas en el cuarto actual.


view_current* La vista que actualmente se está dibujando (0...7). Usa esta variable sólo en el evento draw. Por
ejemplo, puedes chequear el valor de esta variable para dibujar algo sólo en unas vistas y no en otras. El valor de esta

variable no se puede cambiar.

view_visible[0..7] Devuelve si la vista indicada es visible en la pantalla


view_xview[0..7] Posición X de la vista en el cuarto.
view_yview[0..7] Posición Y de la vista en el cuarto.
view_wview[0..7] Anchura de la vista en el cuarto.
view_hview[0..7] Altura de la vista en el cuarto.
view_xport[0..7] Posición X del puerto en la región de dibujo.
view_yport[0..7] Posición Y del puerto en la región de dibujo.
view_wport[0..7] Anchura del puerto en la región de dibujo.
view_hport[0..7] Altura del puerto en la región de dibujo.
view_angle[0..7] Ángulo de rotación usado para la vista en el cuarto (grados en sentido antihorario).
view_hborder[0..7] Tamaño en píxeles del borde horizontal alrededor del objeto a seguir por la vista.
view_vborder[0..7] Tamaño en píxeles del borde vertical alrededor del objeto a seguir por la vista.
view_hspeed[0..7] Velocidad horizontal máxima de la vista.
view_vspeed[0..7] Velocidad vertical máxima de la vista.
view_object[0..7] Objeto cuya instancia debe permanecer visible en la vista. Si hay varias instancias de este
objeto se usará la primera. Puedes asignar una id de una instancia a esta variable para indicar una instancia concreta

que quieras seguir.

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

adaptar tú mismo el tamaño de la pantalla con las siguientes funciones:

window_set_region_size(w,h,adaptwindow) Indica la anchura y altura de la región de dibujo en


la ventana. adaptwindow indica si el tamaño de la ventana debe adaptarse si la región de dibujo no cabe dentro de

ella. La ventana siempre se adaptará si usas escalado fijo (Consulta la función window_set_region_scale() en el

capítulo La ventana).

window_get_region_width() Devuelve la anchura actual de la región de dibujo.


window_get_region_height()Devuelve la altura actual de la región de dibujo.

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_view_mouse_get_x(id) Devuelve la coordenada x del ratón respecto a la vista indicada.


window_view_mouse_get_y(id) Devuelve la coordenada y del ratón respecto a la vista indicada.
window_view_mouse_set(id,x,y) Indica la posición del ratón respecto a la vista indicada.
window_views_mouse_get_x()Devuelve la coordenada x del ratón respecto a la vista en la que se
encuentra (es lo mismo que mouse_x).

window_views_mouse_get_y()Devuelve la coordenada y del ratón respecto a la vista en la que se


encuentra (es lo mismo que mouse_y).

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.

transition_kind Indica la transición al próximo frame:


0 = sin transición

1 = Crear desde la izquierda

2 = Crear desde la derecha

3 = Crear desde arriba

4 = Crear desde abajo

5 = Crear desde el centro

6 = Cambiar desde la izquierda

7 = Cambiar desde la derecha

8 = Cambiar desde arriba

9 = Cambiar desde abajo

10 = Mezclar desde la izquierda

11 = Mezclar desde la derecha

12 = Mezclar desde arriba

13 = Mezclar desde abajo

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

superficies y partículas puedes crear transiciones realmente asombrosas!

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

operación. Existen dos funciones para hacer esto:

screen_redraw() Redibuja la pantalla ejecutando todos los eventos de dibujo.


screen_refresh() Refresca la pantalla usando la imagen del cuarto actual sin ejecutar los eventos de dibujo.
Para entender la segunda función es necesario ver cómo trabaja internamente el mecanismo de dibujo. Internamente

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

que el jugador pulse una tecla:

{
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:

set_automatic_draw(value) Indica si el cuarto se debe dibujar automáticamente (true por defecto) o no


(false).

Finalmente, hay una función que te permite sincronizar el dibujo con la frecuencia de refresco del monitor:

set_synchronization(value) Indica si se debe 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

deben ser reproducidos a través de un reproductor multimedia.

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

por podérseles aplicar cualquier tipo de efectos en ellas.

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:

Funciones básicas de sonido

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_loop(index) Reproduce el sonido indicado, rebobinándolo continuamente. 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.

sound_stop_all() Para todos los sonidos.


sound_isplaying(index) Devuelve si (una copia de) el sonido indicado se esta reproduciendo. Nótese que
esta función devuelve true cuando el sonido realmente esta reproduciéndose a través de los altavoces (línea de salida).

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

tiempo más (p.e. por el eco), la función devolverá true.

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

archivos que se reproducen a través del reproductor multimedia.

sound_volume(index,value) Cambia el volumen del sonido indicado (0 = bajo, 1 = alto,)


sound_global_volume(value) Cambia el volumen global de todos los sonidos (0=bajo, 1=alto)
sound_fade(index,value,time) Cambia el volumen del sonido indicado a el nuevo value (0=bajo,
1=alto) durante el tiempo indicado (en milisegundos). Esto puede ser usado para ascender o descender el sonido (p.e.

para entradas).

sound_pan(index,value) Cambia el pan del sonido indicado (-1=izquierda, 0,centro, 1=derecha).

sound_background_tempo(factor) Cambia el tempo de la música de fondo (si es un archivo midi).


factor indica el factor a multiplicar por el tempo. Un valor de 1 corresponde al tempo normal. Valores mayores

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

archivos tu mismo. Game Maker no incluye automáticamente dichos archivos adicionales.

sound_set_search_directory(dir) Establece el directorio en donde se deben buscar archivos direct


music. El string DIR no debe incluir la diagonal al final.

Efectos de sonido

Esta funcionalidad esta disponible únicamente en la versión registrada de Game Maker.

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

puedes usar la siguiente función:

sound_effect_set(snd,effect) Establece una (combinación de) efecto(s) de sonido para el sonido


indicado.

EFFECT puede ser cualquiera de estos valores:

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

configuración de los efectos de sonido:

*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)

FREQUENCY Frecuencia del LFO (0 a 10,pred.0)

WAVE Forma de la onda del LFO (0- triangular, 1-onda,pred.1)

DELAY Tiempo (número) en milisegundos que la entrada debe esperar antes de ser reproducida de nuevo (0 a

20,pred.0)

PHASE Fase diferencial entre LFOs izquierdos o derechos.(0 a 4,pred.2)


sound_effect_echo(snd,wetdry,feedback,leftdelay,rightdelay,pandelay)
Establece los parámetros para el efecto de echo 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)

FEEDBACK .Porcentaje de señal de salida para respaldar la entrada del sonido. (-99 a 100,pred.0)

LEFTDELAY. Retraso para el canal izquierdo, en milisegundos. (1 a 2000,pred.333)

RIGHTDELAY. Retraso para el canal derecho, en milisegundos. (1 a 2000,pred.333)

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)

FREQUENCY Frecuencia del LFO (0 a 10,pred.0)

WAVE Forma de la onda del LFO (0- triangular, 1-onda,pred.1)

DELAY Tiempo (número) en milisegundos que la entrada debe esperar antes de ser reproducida de nuevo (0 a

20,pred.0)

PHASE Fase diferencial entre LFOs izquierdos o derechos.(0 a 4,pred.2)

sound_effect_gargle(snd,rate,wave) Establece los parámetros para el efecto de gargle al sonido


indicado. Los siguientes parámetros son requeridos:

RATE Variable de modulación,en Hertz. (1 a 1000, pred.1)


WAVE Forma de la onda del LFO (0- triangular, 1-onda,pred.1)

sound_effect_reverb(snd,gain,mix,time,ratio) Establece los parámetros para el efecto de


reverb al sonido indicado. Los siguientes parámetros son requeridos:

GAIN Señal ganada por la entrada, en decibels (dB) (-96 a 0,pred.0)


MIX Mezcla de la repetición, en dB. (-96 a 0,pred.0)
TIME Tiempo de la repetición, en milisegundos (0.001 a 3000,pred.1000)
RATIO Radio de frecuencia (0.001 a 0.999,pred.0.001)

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:

GAIN Señal de salida ganada después de la compresión (-60 a 60, pred.0)


ATTACK Tiempo antes de que la compresión termine (0.01 a 500,0.01)

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)

sound_effect_equalizer(snd,center,bandwidth,gain) Establece los parámetros para el


efecto de equalizer al sonido indicado. Los siguientes parámetros son requeridos:

center Fecuencia central, en Hertz (80 a 16000)


bandwidth Banda ancha, en semitonos (1 a 36)
gain Ganancia. (-15 a 15)

Sonido 3D

Esta funcionalidad esta disponible únicamente en la versión registrada de Game Maker.

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

adaptado por consiguiente.

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).

sound_3d_set_sound_position(snd,x,y,z) Establece la posición al sonido indicado con respecto

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.

sound_3d_set_sound_velocity(snd,x,y,z) Establece la velocidad al sonido indicado con el vector

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.

sound_3d_set_sound_distance(snd,mindist,maxdist) Establece la distancia mínima a la

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

Esta funcionalidad esta disponible únicamente en la versión registrada de Game Maker.

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_present()Devuelve si hay un cd en el dispositivo CD predeterminado.


cd_number()Devuelve el numero de pistas en el CD.
cd_playing()Devuelve si el CD se está reproduciendo.
cd_paused().Devuelve si el CD esta pausado o parado.
cd_track()Devuelve el número de la pista que se está reproduciendo (1=la primera)
cd_length().Devuelve la longitud total del CD en milisegundos.
cd_track_length(n). Devuelve la longitud de una pista n del CD en milisegundos
cd_position().Devuelve la posición actual en el CD en milisegundos.
cd_track_position().Devuelve la posición actual de la pista reproducida, en milisegundos.
cd_play(first,last). Le indica al CD desde que pista a que pista debe reproducir. Si deseas que reproduzca
todo el CD, indica 1 y 1000 como argumentos.

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.

cd_close_door()Cierra la caja del Reproductor de CDs.

Hay una función general para acceder la funcionalidad multimedia de Windows:

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.

Ventanas, highscores, y otros pop-ups


En esta sección vamos a aprender algunas funciones que pueden ser usadas para mostrar ventanas con videos,

imágenes, etc...para mostrar mensajes y preguntas al jugador, y mostrar la tabla de mayores puntajes.

Esta sección está dividida en los temas:

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

esto sucede, el juego es temporalmente pausado. Estas son las funciones:

show_text(fname,full,backcol,delay) Muestra una ventana con texto. fname es el nombre del


archive de texto (.txt o .rtf). Debes poner este archivo en el directorio del juego tu mismo. También, cuando crees una

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

pantalla completa. DELAY es el retardo en segundos antes de volver al juego.

show_info() Muestra la información del juego.


load_info(fname) Carga la información del juego con el nombre de archive FNAME, el cual debería ser un
archivo RTF. Esto hace posible mostrar diferentes archivos de ayuda, en diferentes momentos.

Preguntas y mensajes pop-up

Existen otras funciones para mostrar mensajes, preguntas, un menú con opciones, o un cuadro de diálogo en el cual el

jugador puede insertar un número, un string, indicar un color o un nombre de archivo:

show_message(str) Muestra un cuadro de dialogo con un string STR como mensaje.


show_message_ext(str,but1,but2,but3) Muestra un cuadro de diálogo con el string STR como
mensaje, más un máximo de 3 botones. But1,but2 y but3 contienen el texto de cada botón. Un string vacío significa
que el botón no se mostrará. En los textos puedes usar el símbolo “&” para indicar que el siguiente carácter debe ser

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

devuelve el número del botón presionado (0 si el usuario presiona la tecla ESC).

show_question(str) Muestra una pregunta; devuelve true cuando el usuario elige “yes” y de otra manera,
false.

get_integer(str,def) Pregunta un número al jugador por medio de un cuadro de diálogo. STR es el


mensaje, DEF es el número predeterminado que se mostrará.

get_string(str,def) Pregunta un string al jugador en un cuadro de diálogo. STR es el mensaje, DEF es el


valor predeterminado.

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,

también lo será el cuadro de diálogo (solo para Windows 2000 o superior).

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.

message_text_font(name,size,color,style) Establece la fuente para el texto de los cuadros de


diálogo (esta debe ser una fuente normal de Windows, no una de las fuentes de recurso que usas en tu juego!) STYLE

indica el estilo de la fuente (0=normal, 1=negrita, 2=cursiva, 3=negrita y cursiva).

message_button_font(name,size,color,style) Establece la fuente para los botones en los


cuadros de diálogo. STYLE indica el estilo de fuente. (0=normal, 1=negrita, 2=cursiva, 3=negrita y cursiva).

message_input_font(name,size,color,style) Establece la fuente para el campo de entrada en

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

el borde (1) o no (0) y STR indica el título cuando el borde si es mostrado.

message_position(x,y) Establece la posición para los cuadros de diálogo en la pantalla.


message_size(w,h) Arregla el tamaño de los cuadros de diálogo en la pantalla. Si eliges 0 para el ancho(w), el
ancho de la imagen es usado. Si eliges 0 para el alto (h), el alto se calcula basándose en el número de líneas del

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

predeterminado def es devuelto.

show_menu_pos(x,y,str,def) Muestra un menú popup, como en la función anterior, pero en la posición


x, y en la pantalla.

get_color(defcol) Le pide al jugador un color. DEFCOL es el color predeterminado. Si el jugador presiona


CANCEL el valor -1 es devuelto.

get_open_filename(filter,fname) Le pide al jugador un archive para abrir, con el filtro (FILTER)


dado. El filtro tiene la forma ‘nombre1|máscara1|nombre2|máscara2’ . Una máscara contiene diferentes opciones

con un ';'(punto y coma) entre ellas. '*' (asterisco) significa cualquier string. Por ejemplo: ‘Mapa de

bits|*bmp;*.wmf’.Si el jugador presiona Cancel, un string vacío es devuelto.

get_save_filename(filter,fname) Pide un archivo para guardarlo, con el filtro dado. Si el jugador


presiona Cancel, un string vacío es devuelto.

get_directory(dname) Pide un directorio. DNAME es el nombre predeterminado. Si el usuario presiona


Cancel, un string vacío es devuelto.

get_directory_alt(capt,root) Una forma alternativa de preguntar por un directorio. CAPT es el titulo


a mostrar. ROOT es la raíz del diagrama del directorio a mostrar. Usa un string vacío para mostrar el diagrama

completo. Si el usuario presiona Cancel, un string vacío es devuelto.

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

funciones existen para esto:

highscore_show(numb) Muestra la tabla de highscores. NUMB es un nuevo puntaje. Si el puntaje es


suficientemente bueno para ser agregado a la lista, el jugador puede introducir un nombre. Usa -1 para únicamente

mostrar la lista actual.

highscore_set_background(back) Establece la imagen de fondo. BACK debe ser el índice de uno de


los recursos de fondo.

highscore_set_border(show) Establece si la forma de highscore debe mostrar un borde o no.


highscore_set_font(name,size,style) Establece la fuente usada para el texto en la tabla. (Esta es
una fuente normal de Windows, no una de los recursos de fuente). Debes especificar un nombre y un estilo (0=normal,

1=negrita, 2=cursiva, 3=negrita y cursiva).

highscore_set_colors(back,new,other)Establece los colores usados para el fondo, el de una


nueva entrada en la tabla, y el de las demás entradas. entradas.

highscore_set_strings(caption,nobody,escape) Cambia los diferentes strings


predeterminados usados al mostrar la tabla de highscores. CAPTION es el título de la forma. NOBODY es el string usado

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

a usar, y SIZE es el tamaño de la fuente.

highscore_clear() Limpia la tabla de puntajes.


highscore_add(str,numb) Agrega al jugador con el nombre STR y puntaje NUMB a la lista.
highscore_add_current() Agrega el puntaje actual a la lista de hightsocres. También se le pide al jugador
que proporcione un nombre.

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.

highscore_name(place) Devuelve el nombre de la persona con el lugar PLACE(1-10).


draw_highscore(x1,y1,x2,y2) Dibuja la tabla de puntajes en la room, con la caja dada (x1,y1,x2,y2),
usando la fuente actual.

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

de ellos en tiempo real.

Los recursos se dividen en los siguientes tipos:

Sprites

Las funciones siguientes te dan información sobre un sprite:

sprite_exists(ind) Devuelve si el sprite con el índice (ind) especificado existe.


sprite_get_name(ind) Devuelve el nombre del sprite con el ind especificado.
sprite_get_number(ind) Devuelve el número de subimágenes del sprite con el índice dado.
sprite_get_width(ind) Devuelve el ancho del sprite con el índice especificado.
sprite_get_height(ind) Devuelve la altura del sprite con el índice dado.
sprite_get_transparent(ind) Devuelve si el sprite con el índice especificado utiliza transparencia.
sprite_get_smooth(ind) Devuelve si el sprite tiene los bordes suavizados.
sprite_get_preload(ind) Devuelve si el sprite debe ser cargado al principio del juego.
sprite_get_xoffset(ind) Devuelve el x-offset (punto de origen en x) del sprite con el índice especificado.
sprite_get_yoffset(ind) Devuelve el y-offset (punto de origen en y) del sprite con el índice especificado.
sprite_get_bbox_left(ind) Devuelve el valor del límite izquierdo del sprite (bounding box) con el índice
especificado.

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

Las funciones siguientes te dan información sobre los sonidos:

sound_exists(ind) Devuelve si un sonido con el índice dado existe.


sound_get_name(ind) Devuelve el nombre del sonido con el índice dado.
sound_get_kind(ind) Devuelve el tipo de sonido del sonido especificado (0=normal, 1=background, 2=3d,
3=mmplayer).

sound_get_preload(ind) Devuelve si el sonido especificado debe cargarse al principio del juego.

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:

sound_discard(index) Elimina el sonido indicado de la memoria de audio.


sound_restore(index) Carga el sonido indicado en la memoria de audio para que se pueda utilizar cuando
se necesite inmediatamente

Fondos

Las siguientes funciones te darán información acerca de un fondo :

background_exists(ind) Devuelve si el background (fondo) con el índice dado existe.


background_get_name(ind) Devuelve el nombre del fondo con el índice indicado.
background_get_width(ind) Devuelve el ancho del fondo con el índice indicado.
background_get_height(ind) Devuelve la altura del fondo con el índice especificado.
background_get_transparent(ind) Devuelve si el fondo con el índice indicado es transparente.
background_get_smooth(ind) Devuelve si el fondo tiene los bordes suavizados.
background_get_preload(ind) Devuelve si el fondo debe ser cargado al principio del juego.
Fuentes

Las funciones siguientes te dan información sobre las fuentes:

font_exists(ind) Devuelve si la fuente con el índice especificado existe.


font_get_name(ind) Devuelve el nombre de la fuente con el índice especificado.
font_get_fontname(ind) Devuelve el nombre de fuente (arial, verdana,...) de la fuente con el índice
especificado.

font_get_bold(ind) Devuelve si la fuente con el índice especificado está en negrita.


font_get_italic(ind) Devuelve si la fuente con el índice especificado es cursiva.
font_get_first(ind) Devuelve el primer carácter de la fuente con el índice especificado.
font_get_last(ind) Devuelve el último carácter de la fuente con el índice especificado.

Paths

Las funciones siguientes te proporcionarán información sobre un path:

path_exists(ind) Devuelve si el path con el índice dado existe.


path_get_name(ind) Devuelve el nombre del path con el índice dado.
path_get_length(ind) Devuelve la longitud del path con el índice indicado.
path_get_kind(ind) Devuelve el tipo de conexiones del path con el índice especificado (0=recto, 1=curvo).
path_get_closed(ind) Devuelve si el path es cerrado o no.
path_get_precision(ind) Devuelve la precisión utilizado para paths redondeadas.
path_get_number(ind) Devuelve el número de puntos del path.
path_get_point_x(ind,n) Devuelve la coordenada x del punto n del path. El primer punto es el 0.
path_get_point_y(ind,n) Devuelve la coordenada y del punto n del path. El primer punto es el 0.
path_get_point_speed(ind,n) Devuelve la velocidad del punto n del path. El primer punto es el 0.
path_get_x(ind,pos) Devuelve la coordenada x en la posición pos del path (pos debe estar comprendida
entre 0 y 1).

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

Las siguientes opciones te darán información acerca de un script:

script_exists(ind) Devuelve si un script con el índice indicado existe.


script_get_name(ind) Devuelve el nombre del script con el índice indicado.
script_get_text(ind) Devuelve la cadena de texto del script con el índice dado

Time lines

Las funciones siguientes te dan información sobre las time lines:


timeline_exists(ind) Devuelve si la time line con el índice especificado existe.
timeline_get_name(ind) Devuelve el nombre de la time line con el índice especificado.

Objetos

Las siguientes funciones proporcionarán información acerca de un objeto:

object_exists(ind) Devuelve si el objeto con el índice dado existe.


object_get_name(ind) Devuelve el nombre del objeto con el índice dado.
object_get_sprite(ind) Devuelve el índice del sprite por defecto del objeto con el índice especificado.
object_get_solid(ind) Devuelve si el objeto con el índice dado es sólido por defecto.
object_get_visible(ind) Devuelve si el objeto con el índice dado es visible por defecto.
object_get_depth(ind) Devuelve la profundidad del objeto con el índice dado.
object_get_persistent(ind) Devuelve si el objeto con el índice señalado es persistente.
object_get_mask(ind) Devuelve el índice de la máscara del objeto con el índice dado (-1 si no tiene
máscara especial).

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

Las siguientes funciones darán información acerca de una habitación:

room_exists(ind) Devuelve si el cuarto con el índice señalado existe.


room_get_name(ind) Devuelve el nombre de la habitación con el índice dado.

Observa que como las habitaciones cambian durante el juego hay otras rutinas para obtener información de la

habitación actual

Modificando los recursos


Estas funciones sólo están disponibles en la versión registrada de Game Maker.

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

que está siendo utilizado por alguna instancia.

• 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

salvado y carga de juegos.

• 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

función para reiniciar el juego.


• La manipulación de recursos puede ser muy lenta. Por ejemplo, el cambiar sprites o fondos es relativamente

lento. Por lo que no es cambiarlos durante la ejecución del juego.

• 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

36x32x128x128x4 = ¡36 MB de memoria!

• 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

quizá al inicio de una habitación.

Las funciones para modificar recursos se dividen en las siguientes secciones:

Sprites

Las siguientes funciones permiten modificar las propiedades de los sprites:

sprite_set_offset(ind,xoff,yoff) Establece el offset del sprite.

sprite_set_bbox_mode(ind,mode) Establece a mode el tipo de caja de colisión del sprite (0 =

automática, 1 = imagen completa, 2 = manual).

sprite_set_bbox(ind,left,top,right,bottom) Configura la caja de colisión del sprite con

índice ind. Funciona solo cuando es manual el modo de caja de colisión.

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.

Devuelve -1 si se presenta algún error.

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

por la función indica si tuvo éxito la operación.

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.

sprite_add_from_screen(ind,x,y,w,h) Agrega un área de la pantalla como la siguiente subimagen

del sprite con índice ind. x, y, w y h indican las dimensiones del área en pantalla (coordenadas x, y, ancho y alto).

sprite_delete(ind) Elimina el sprite, liberando la memoria utilizada.

Se cuenta con la siguiente rutina para cambiar la apariencia de un sprite.

sprite_set_alpha_from_sprite(ind,spr) Cambia los valores alfa (transparencia) del sprite con

í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.

sound_add(fname,kind,preload) Agrega un recurso de sonido al juego. Fname es el nombre del

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).

sound_replace(index,fname,kind,loadonuse) Lo mismo que la anterior pero esta vez no se

crea un nuevo sonido sino que se sustituye el que tenga índice index, liberando el anterior sonido. El valor devuelto por

esta función indica si tuvo éxito la operación.

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

fondo. Cuando se presenta algún error se devuelve -1.

background_assign(ind,back) Asigna el fondo back al fondo ind. Esto es, crea una copia del fondo.

background_add(fname,transparent,smooth,preload) Agrega la imagen almacenada en el


archivo fname al juego de recursos background. Solo se pueden manejar imágenes bmp y jpg. 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. 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

-1 cuando ocurre algún error.

background_replace(ind,fname,transparent,smooth,preload) Lo mismo que la

anterior pero en este caso el fondo con índice ind es reemplazado. La función devuelve un valor indicando si tuvo éxito

la operación. Cuando el fondo reemplazado es visible en la habitación será reemplazado.

background_create_color(w,h,col,preload) Crea un nuevo fondo del tamaño dado (w=ancho,

h=alto) con el color col. Devuelve el índice del nuevo fondo, -1 si ocurre algún error.

background_create_gradient(w,h,col1,col2,kind,preload) Crea un fondo del tamaño

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

ella pudiera estar escalada.

background_delete(ind) Elimina el fondo, liberando la memoria utilizada.

La siguiente rutina permite cambiar la apariencia de un fondo.

background_set_alpha_from_background(ind,back) Cambia los valores alfa (transparencia)

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).

font_add(name,size,bold,italic,first,last) Agrega una nueva fuente y devuelve su índice.

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).

font_add_sprite(spr,first,prop,sep) Agrega una nueva fuente y devuelve su índice. La fuente es

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

debiera estar entre 2 y 8 dependiendo del tamaño de la fuente.

font_replace(ind,name,size,bold,italic,first,last) Reemplaza la fuente ind con una

nueva fuente, indicando el nombre (name), tamaño (size) si es negrita (bold) o cursive (italic) y el primer y último

carácter que deben crearse.

font_replace_sprite(ind,spr,first,prop,sep) Reemplaza la fuente ind con una nueva

fuente basada en el sprite spr.

font_delete(ind) Elimina la fuente con índice ind, liberando la memoria utilizada.

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:

path_set_kind(ind,val) Establece el tipo de conexiones de la trayectoria ind (0=recta, 1=suave)

(0=straight, 1=smooth).

path_set_closed(ind,closed) Establece si la trayectoria ind debe ser cerrada (true) o abierta (false).

path_set_precision(ind,prec) Establece la precisión con la que se calcula la suavidad de la trayectoria

(prec debe estar entre 1 y 8).

path_add() Agrega una nueva trayectoria vacía. Devuelve el índice de la trayectoria.

path_delete(ind) Elimina la trayectoria con índice ind.

path_duplicate(ind) Crea un duplicado de la trayectoria ind. Devuelve el índice de la nueva trayectoria.

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.

path_add_point(ind,x,y,speed) Agrega un punto a la trayectoria con índice ind, en la posición (x,y) y

con factor de velocidad speed. Se debe recordar que un factor de 100 corresponde a la velocidad actual. Valores

inferiores indican una reducción de la velocidad y valores superiores un incremento de la misma.

path_insert_point(ind,n,x,y,speed) Inserta un punto en la trayectoria con índice ind antes del

punto n, en la posición (x,y) y con factor de velocidad speed.

path_change_point(ind,n,x,y,speed) Cambia el punto n de la trayectoria ind a la posición (x,y) y

con factor de velocidad speed.

path_delete_point(ind,n) Elimina el punto n de la trayectoria con índice ind.

path_clear_points(ind) Limpia todos los puntos de la trayectoria ind, volviéndola una trayectoria vacía.

path_reverse(ind) Invierte la trayectoria ind.

path_mirror(ind) Voltea horizontalmente la trayectoria ind (con respecto a su centro).

path_flip(ind) Voltea verticalmente la trayectoria ind (con respecto a su centro).

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).

path_shift(ind,xshift,yshift) Mueve la trayectoria con los valores indicados.

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:

execute_string(str) Ejecuta el fragmento de código en la cadena str.

execute_file(fname) Ejecuta el código dentro del archive fname.

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

que estén en uso!

timeline_add() Agrega una nueva línea de tiempo. Devuelve el índice de la misma.

timeline_delete(ind) Elimina la línea de tiempo con índice ind. Debe asegurarse que ninguna instancia

emplee dicha línea de tiempo en ninguna habitación.

timeline_moment_add(ind,step,codestr) Agrega una acción de código a la línea de tiempo en el

instante step. codestr contiene el código para las acciones. Si el step no existe se crea. Por lo que pueden agregarse

múltiples acciones de código para el mismo instante.

timeline_moment_clear(ind,step) Se puede utilizar esta función para borrar todas las acciones de

una línea de tiempo (ind) en un instante (step) en particular

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

almacenadas en la instancia y, por tanto, al cambiarlas en el objeto no se tendrá el efecto deseado.

object_set_sprite(ind,spr) Establece el sprite para el objeto con índice ind. El valor -1 remueve del

objeto el sprite actual.

object_set_solid(ind,solid) Establece si al crear instancias del objeto ind deberán considerarse

sólidas (true o false).

object_set_visible(ind,vis) Establece si las instancias creadas del objeto ind deben ser visibles por

defecto (true o false).

object_set_depth(ind,depth) Establece la profundidad (depth) por defecto de las instancias creadas


del objeto ind.

object_set_persistent(ind,pers) Establece si las instancias creadas del objeto deben ser

persistentes por defecto (true o false).

object_set_mask(ind,spr) Establece el sprite para la máscara del objeto con índice ind. Para que la

máscara sea el sprite del objeto se puede usar -1.

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

instancias del objeto.

object_delete(ind) Elimina el objeto con índice ind. Se debe asegurar de que no existan instancias de este

objeto en ninguna de las habitaciones.

object_event_add(ind,evtype,evnumb,codestr) Para asignarle un comportamiento a un objeto

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

agregar múltiples acciones a cada evento.

object_event_clear(ind,evtype,evnumb) Se puede emplear esta función para borrar todas las

acciones para un evento en particular.

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

crear objetos usando la interfaz estándar

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

no hay ninguna rutina que permita eliminar una habitación.


Las siguientes funciones están disponibles

room_set_width(ind,w) Establece el ancho (w) para la habitación con índice ind.

room_set_height(ind,h) Establece la altura (h) para la habitación con índice ind.

room_set_caption(ind,str) Establece el título (caption) para la habitación con índice ind.

room_set_persistent(ind,val) Establece si la habitación con índice ind será persistente o no (val).

room_set_code(ind,str) Configura el código de inicialización (str) para la habitación con índice ind.

room_set_background_color(ind,col,show) Configura las propiedades de color para la habitación

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.

room_set_view_enabled(ind,val) Establece si las vistas deben habilitarse para la habitación con

í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.

Si se desea moverse a una habitación agregada se debe indicar el índice de la misma.

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,

colocándola en la posición x, y. Devuelve el índice de la instancia.

room_instance_clear(ind) Elimina todas las instancias dentro de la habitación ind.

room_tile_add(ind,back,left,top,width,height,x,y,depth) Agrega un nuevo tile a la


habitación en la posición indicada. Devuelve el índice del tile. back es el fondo del cual se toma el tile. Left, top, width y

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.

room_tile_clear(ind) Elimina todos los tiles de la habitación indicada.

Archivos, registro, y ejecución de programas

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

cerrar los archivos una vez que no los necesites.

file_text_open_write(fname) Abre el archivo fname para escritura, creándolo si no existe. La función


devuelve la id del archivo que debes usar en las demás funciones.

file_text_open_append(fname) Abre el archivo fname para agregar datos al final, creándolo si no


existe. La función devuelve la id del archivo que debes usar en las demás funciones.

file_text_close(fileid) Cierra el archivo indicado por fileid (¡No olvides llamarla!).


file_text_write_string(fileid,str) Escribe la cadena str al archivo indicado por fileid.
file_text_write_real(fileid,x) Escribe el valor real x en el archivo indicado por fileid.
file_text_writeln(fileid) Escribe un carácter de nueva línea en el archivo.
file_text_read_string(fileid) Lee una cadena del archivo y devuelve esta cadena. Una cadena
termina al final de la línea.

file_text_read_real(fileid) Lee un valor real del archivo y devuelve este valor.

file_text_readln(fileid) Salta el resto de la línea en el archivo e inicia al principio de la siguiente línea.


file_text_eof(fileid) Indica si hemos llegado al final del archivo.

Para manipular archivos del sistema puedes utilizar las siguientes funciones:

file_exists(fname) Indica si el archivo con el nombre fname existe (true) o no (false).


file_delete(fname) Borra el archivo con el nombre fname.
file_rename(oldname,newname) Renombra el archivo con el nombre oldname a newname.
file_copy(fname,newname) Copia el archivo fname al nombre newname
directory_exists(dname) Indica si la carpeta dname existe.
directory_create(dname) Crea una carpeta con el nombre dname (incluyendo la ruta a esa carpeta) si no
existe.

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

para ver el tipo de archivos que desees:

fa_readonly archivos de sólo lectura


fa_hidden archivos ocultos
fa_sysfile archivos de sistema
fa_volumeid archivos volume-id
fa_directory carpetas
fa_archive archivos archivados
file_find_next() Devuelve el nombre del siguiente archivo que satisface la máscara y los atributos indicados
previamente. Si no existe tal archivo, devuelve una cadena vacía.

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

archivos en sí, si no al nombre:

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.

filename_drive(fname) Devuelve la información de la unidad del archivo.


filename_ext(fname) Devuelve la extensión del archivo, incluyendo el punto.
filename_change_ext(fname,newext) Devuelve el nombre del archivo con la extensión sustituida por
newext. Si indicas una cadena de texto vacía para newext puedes eliminar la extensión del archivo.

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

cerrarlos cuando hayas terminado con ellos.


file_bin_rewrite(fileid) Limpia el archivo indicado por fileid, es decir, borra todo su contenido y se
sitúa al principio del archivo para empezar a escribir.

file_bin_close(fileid) Cierra el archivo especificado. No olvides llamarla!


file_bin_size(fileid) Devuelve el tamaño en bytes del archivo indicado.
file_bin_position(fileid) Devuelve la posición actual en el archivo en bytes (0 es el principio del
archivo).

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.

file_bin_write_byte(fileid,byte) Escribe un byte de datos al archivo especificado.


file_bin_read_byte(fileid) Lee un byte de datos del archivo.

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.

Las siguientes tres variables de sólo lectura pueden ser útiles:

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

puedes usar las siguientes dos rutinas:

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:

environment_get_variable(name) Devuelve el valor (una cadena de texto) de la variable de entorno


con el nombre especificado

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

cadenas como valores reales. Tenemos las siguientes funciones:


registry_write_string(name,str) Crea una entrada en el registro con el nombre name y como valor
la cadena str.

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).

registry_exists(name) Indica si la entrada name existe.

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

rutinas trabajan por defecto con

el grupo HKEY_CURRENT_USER. Pero si quieres puedes cambiar este grupo raíz de trabajo. Por ejemplo, para obtener

el directorio temporal actual, usa:

path = registry_read_string_ext('\Environment','TEMP');

Las siguientes funciones existen para moverte por el registro con libertad:

registry_write_string_ext(key,name,str) Crea una entrada en el registro dentro de la clave


key con el nombre name y como valor la cadena str.

registry_write_real_ext(key,name,x) Crea una entrada en el registro dentro de la clave key con


el nombre name y el valor real x.

registry_read_string_ext(key,name) Devuelve la cadena con el nombre name dentro de la clave


key (el nombre debe existir, de otra forma se devuelve una cadena vacía).

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).

registry_exists_ext(key,name) Indica si el nombre name existe dentro de la clave key.


registry_set_root(root) Configura la raíz para las otras rutinas. Usa los siguientes valores:
0 = HKEY_CURRENT_USER
1 = HKEY_LOCAL_MACHINE
2 = HKEY_CLASSES_ROOT
3 = HKEY_USERS
Archivos INI

Para pasar ciertos parámetros a un programa es corriente utilizar los archivos INI. Los archivos INI contienen secciones

y cada sección contiene un número de parejas nombre-valor. Por ejemplo:

[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!

ini_close()Cierra el archivo INI actualmente abierto.


ini_read_string(section,key,default) Lee el valor (una cadena de texto) de la llave indicada con
key de la sección indicada como section. Cuando no existe la llave o la sección se devuelve el valor especificado por

default.

ini_read_real(section,key,default) Lee el valor real de la llave indicada con key de la sección


indicada como section. Cuando no existe la llave o la sección se devuelve el valor especificado por default.

ini_write_string(section,key,value) Escribe el valor value (una cadena de texto) en la llave


indicada con key en la sección indicada con section.

ini_write_real(section,key,value) Escribe el valor real value en la llave indicada con key en la


sección indicada con section.

ini_key_exists(section,key) Devuelve si la llave indicada existe en la sección indicada.


ini_section_exists(section) Devuelve si existe la sección indicada.
ini_key_delete(section,key) Elimina la llave indicada de la sección indicada.
ini_section_delete(section) Elimina la sección indicada.

Ejecución de programas

Game Maker también tiene la posibilidad de iniciar programas externos. Hay dos funciones disponibles para esto:

execute_program y execute_shell. La función execute_program inicia un programa, posiblemente con algunos

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

lo que el juego continúa.

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.

execute_shell(prog,arg) Ejecuta el programa (o archivo) en el entorno.

Ambas funciones no funcionarán si el jugador activa el modo seguro en sus preferencias. Puedes comprobar esto

usando la variable de solo lectura:

secure_mode* Indica si el juego está ejecutándose en modo seguro.

Estructuras de datos

Esta función sólo está disponible en la versión registrada de Game Maker.

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

GML, que pueden ser lentos al ejecutarse.

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

éstas pueden guardar valores reales o cadenas de texto.

Observa que las estructuras de datos no se guardan cuando guardas el juego usando las funciones o acciones

apropiadas. Para guardarlas, deberás crear tu propio sistema.

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!

Las estructuras de datos disponibles son las siguientes:

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_push(id,val) Empuja el valor dentro de la pila.


ds_stack_pop(id) Devuelve el valor al final de la pila (esto es, el último valor introducido) y lo elimina de la
pila.

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

datos poseen estas 5 funciones)

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_dequeue(id) Devuelve el último valor de la cola (el primero 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.

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

compilado son mucho más rápidas que los arrays.

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.

ds_list_size(id) Devuelve el número de valores en la lista.


ds_list_empty(id) Devuelve true si la lista está vacía. Es lo mismo que chequear si el número de valores en
la lista es cero.

ds_list_add(id,val) Inserta el valor al final de la lista.


ds_list_insert(id,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(id,pos,val) Reemplaza el valor en la posición pos por val.


ds_list_delete(id,pos) Elimina el valor en la posición pos.
ds_list_find_index(id,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(id,pos) Devuelve el valor en al posición pos.


ds_list_sort(id,ascend) Ordena los valores de la lista. Si ascend es true o 1 los ordena de forma
ascendente, en caso contrario los ordena de manera descendente

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.

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.

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

prioridad y controlar ciertas cosas según su prioridad.

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.

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.

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_create(w,h) Crea una rejilla con la anchura especificada en w y la altura especificada en h. La


función devuelve la id de la rejilla que debe usarse en las demás funciones.

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_resize(id,w,h) Aumenta el tamaño de la rejilla a la nueva anchura y altura especificadas. Las


celdas ya existentes mantienen su valor.

ds_grid_width(id) Devuelve la anchura de la rejilla.


ds_grid_height(id) Devuelve la altura de la rejilla.
ds_grid_clear(id,val) Hace que todos las celdas de la rejilla tomen el valor val (puede ser un número o
una cadena de texto).

ds_grid_set(id,x,y,val)Asigna a la celda x,y el valor val.


ds_grid_add(id,x,y,val) Suma el valor a la celda especificada. Para cadenas de texto, la cadena se
concatena a la que ya existe en la celda.

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.

ds_grid_add_region(id,x1,y1,x2,y2,val)Suma el valor a la región especificada. Para cadenas de


texto, concatena la cadena de texto a la existente en cada celda.

ds_grid_multiply_region(id,x1,y1,x2,y2,val) Multiplica el valor por todas las celdas de la


región (sólo para valores numéricos).

ds_grid_set_disk(id,xm,ym,r,val) Da el valor especificado a toda las celdas dentro del círculo de


centro xm,ym y radio r.

ds_grid_add_disk(id,xm,ym,r,val) Suma el valor a la región especificada por el círculo. Para


cadenas de texto, concatena la cadena de texto a la existente en cada celda.

ds_grid_multiply_disk(id,xm,ym,r,val) Multiplica el valor por todas las celdas de la región (sólo


para valores numéricos).

ds_grid_get(id,x,y) Devuelve el valor de la celda indicada.


ds_grid_get_sum(id,x1,y1,x2,y2) Devuelve la suma de los valores de las celdas de la región
especificada. Sólo funciona con valores numéricos.

ds_grid_get_max(id,x1,y1,x2,y2) Devuelve el máximo valor de las celdas de la región especificada.


Sólo funciona con valores numéricos.

ds_grid_get_min(id,x1,y1,x2,y2) Devuelve el mínimo valor de las celdas de la región especificada.


Sólo funciona con valores numéricos.

ds_grid_get_mean(id,x1,y1,x2,y2) Devuelve el valor medio de las celdas de la región especificada.


Sólo funciona con valores numéricos.

ds_grid_get_disk_sum(id,xm,ym,r) Devuelve la suma de los valores de las celdas de la región


circular especificada. Sólo funciona con valores numéricos..

ds_grid_get_disk_min(id,xm,ym,r) Devuelve el mínimo valor de las celdas de la región circular


especificada. Sólo funciona con valores numéricos.

ds_grid_get_disk_max(id,xm,ym,r) Devuelve el máximo valor de las celdas de la región circular


especificada. Sólo funciona con valores numéricos.

ds_grid_get_disk_mean(id,xm,ym,r) Devuelve el valor medio de las celdas de la región circular


especificada. Sólo funciona con valores numéricos.

ds_grid_value_exists(id,x1,y1,x2,y2,val) Devuelve true si el valor especificado existe en la


región.

ds_grid_value_x(id,x1,y1,x2,y2,val) Devuelve la coordenada x de la celda de la región en la que


aparece el valor especificado.

ds_grid_value_y(id,x1,y1,x2,y2,val) Devuelve la coordenada y de la celda de la región en la que


aparece el valor especificado.

ds_grid_value_disk_exists(id,xm,ym,r,val) Devuelve true si el valor especificado existe en la


región circular.

ds_grid_value_disk_x(id,xm,ym,r,val) Devuelve la coordenada x de la celda de la región circular


en la que aparece el valor especificado.

ds_grid_value_disk_y(id,xm,ym,r,val) Devuelve la coordenada y de la celda de la región circular


en la que aparece el valor especificado

Creando partículas

Esta función solo está disponible en la versión registrada de Game Maker.

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

artificiales, flamas, lluvia, nieve, campos estelares, polvo, etc.


Game Maker contiene un extenso sistema de partículas que puede ser empleado para crear grandes efectos. Aunque no

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

un sistema, ésta será automáticamente dibujada y actualizada por el sistema.

La información sobre partículas se divide en las secciones siguientes:

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

color y su tamaño y del resto se encarga Game Maker.

Los efectos disponibles son:

• 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í

que la posición que especifiques para ellos será irrelevante.

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

funciones siguientes sirven para crear efectos:

effect_create_below(kind,x,y,size,color) Crea un efecto del tipo indicado en kind en la

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.

effect_create_above(kind,x,y,size,color) Igual que la función anterior, pero esta vez el

efecto se crea encima de la instancia, con una profundidad igual a-100000.

Para eliminar todos los efectos usa la función:

effect_clear() Detiene y elimina todos los efectos.

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

comportamiento. Con la elección adecuada puedes crear cualquier tipo de efecto.

Las funciones para crear y destruir tipos de partículas son:

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

aconsejable que lo guardes en una variable global.

part_type_destroy(ind) Destruye el tipo de particular ind. Llama a esta función cuando ya no se necesite

el tipo de partícula para ahorrar espacio.

part_type_exists(ind) Devuelve si el tipo indicado de particular existe.

part_type_clear(ind) Devuelve el tipo de particular ind a sus valores por defecto.


La forma de una partícula
Las partículas tienen una forma. Esta forma se indica mediante un sprite. Puedes usar cualquier sprite que quieras para

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

indican con las constantes:

• 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)

Para indicar la forma debes usar la función:

part_type_shape(ind,shape) Aplica la forma tipo shape a la partícula ind.

También puedes usar tu propio sprite con la función:

part_type_sprite(ind,sprite,animate,stretch,random) Asigna el sprite para el tipo de

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

como la imagen inicial.

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

seguirá el camino que ésta describa.

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

necesitas especificar ninguna propiedad de color.

part_type_color1(ind,color1) Indica un sólo color para la partícula.

part_type_color2(ind,color1,color2) Especifica dos colores entre los que se interpolará.

part_type_color3(ind,color1,color2,color3) Ahora los colores representan el color incial,

medio y final .
part_type_color_mix(ind,color1,color2) El color será seleccionado de una mezcla aleatoria de

estos dos colores y permanecerá inalterado durante la vida de la partícula.

part_type_color_rgb(ind,rmin,rmax,gmin,gmax,bmin,bmax) Se puede usar para

especificar que cada partícula tendrá un color fijo pero escogido de un rango de colores que especificas en modo rgb.

part_type_color_hsv(ind,hmin,hmax,smin,smax,vmin,vmax) Igual que la anterior, pero

esta vez el rango se especifica en modo hsv.

Además del color, también puedes especificar un valor de transparencia. Así puedes conseguir que las partículas

desaparezcan poco a poco.

part_type_alpha1(ind,alpha1) Aplica un único valor de transparencia (entre 0 y 1) al tipo de partícula.

part_type_alpha2(ind,alpha1,alpha2) Igual que la anterior, pero esta vez la transparencia en


cada momento se interpola entre las dos especificadas.

part_type_alpha3(ind,alpha1,alpha2,alpha3) Igual que a anterior, pero esta vez la

transparencia en cada momento se interpola entre las tres especificadas.

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)

para el tipo de partícula.

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.

part_type_life(ind,life_min,life_max) Establece los límites del tiempo de vida para el tipo de

partícula (Ambos son 100 por defecto).

part_type_step(ind,step_number,step_type) Establece el número y tipo de partículas que

deben ser generadas en cada step para el tipo indicado de particula. Si usas un número negativo para step_number,

por ejemplo -5, se generará una partícula cada 5 steps.

part_type_death(ind,death_number,death_type) Establece el número y tipo de partículas que

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

cuando sea destruida por un destructor (lee más adelante).

Movimiento de las partículas


Las partículas se pueden mover durante su vida. Pueden tener velocidad inicial y dirección y cambiar durante su vida.

También pueden verse afectadas por la gravedad.

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).

part_type_direction(ind,dir_min,dir_max,dir_incr,dir_wiggle) Establece las

propiedades de dirección para el tipo de partícula, en grados antihorarios (0-360). (Todos los valores son 0 por

defecto).

part_type_gravity(ind,grav_amount,grav_dir) Establece las propiedades de gravedad para el

tipo de partícula. (Por defecto no hay valor de gravedad).


Sistemas de partículas

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

destructores que las eliminan.

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.

Así que asegúrate de hacerlo cuando ya no los necesites.

Las siguientes funciones permiten manejar los sistemas de partículas:

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

del sistema para ahorrar espacio.

part_system_exists(ind) Devuelve si el sistema de partículas ind existe.

part_system_clear(ind) Devuelve el sistema ind a sus valores por defecto, removiendo todas las

partículas, emisores y atractores en él.

part_system_draw_order(ind,oldtonew) Establece el orden en el que el sistema dibuja 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.

part_system_depth(ind,depth) Especifica la profundidad del sistema de partículas.

part_system_position(ind,x,y) Especifica la posición en la que se dibuja el sistema de partículas. Si

quieres dibujar el sistema relativo a un objeto puedes usar sus coordenadas.


Como ya hemos visto, los sistemas de partículas se dibujan y actualizan automáticamente. Pero a veces puedes no

querer que esto sea así. Las siguientes funciones te permiten controlar estos aspectos:

part_system_automatic_update(ind,automatic) Indica si el sistema debe actualizare

automáticamente (1) o no (0).

part_system_automatic_draw(ind,automatic) Indica si el sistema debe dibujarse

automáticamente (1) o no (0).

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.

part_system_drawit(ind) Esta función dibuja el sistema de partículas si has desactivado la opción

automática. Debes llamar a esta función desde el evento draw de algún objeto.

Las siguientes funciones se encargan de las partículas de un sistema de partículas:

part_particles_create(ind,x,y,parttype,number) Esta función crea number partículas del

tipo ind en la posición (x,y) dentro del sistema.

part_particles_create_color(ind,x,y,parttype,color,number) Esta función crea

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

partícula sólo usa un color o no define ninguno.

part_particles_clear(ind) Esta función elimina todas las partículas del sistema.

part_particles_count(ind) Esta función devuelve el número de partículas en el sistema.

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

emisor tiene las siguientes propiedades:

• 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

misma oportunidad de que aparezca una partícula


o ps_distr_gaussian indica una distribución Gaussiana en la cual se generan más

partículas en el centro que en las orillas de la región

• particle type indica el tipo de partículas que serán generadas

• 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

particular en promedio una vez cada 5 steps.

Las siguientes funciones permiten establecer los emisores y permitirles crear partículas. Nota que cada una de ellas

necesita el id del sistema de partículas al que pertenecen como primer argumento.

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_destroy(ps,ind) Destruye el emisor ind en el sistema de partículas ps. Llama a esta

función si no necesitas más del emisor para ahorrar espacio.

part_emitter_destroy_all(ps) Destruye todos los emisores del sistema de partículas ps.

part_emitter_exists(ps,ind) Devuelve true si el emisor ind existe en el sistema ps.

part_emitter_clear(ps,ind) Devuelve el emisor ind a sus valores por defecto.

part_emitter_region(ps,ind,xmin,xmax,ymin,ymax,shape,distribution)
Establece la región y distribución del emisor.

part_emitter_burst(ps,ind,parttype,number) Emite number partículas del tipo partype en

forma de estallido (una sola vez) desde el emisor ind.

part_emitter_stream(ps,ind,parttype,number) Desde este momento se crearán number

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

una particular cada 5 steps.

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:

• x,y indican la posición del atractor.

• force indican la fuerza de atracción del atractor. El cómo actúe la fuerza sobre las partículas depende de

los siguientes parámetros.

• dist indica la máxima distancia a la cual el atractor tiene efecto. Solo las partículas con una distancia al

atractor menor que dist serán atraídas.

• kind indica el tipo de atractor. Se tienen los siguientes valores


o ps_force_constant indica que la fuerza es constante independientemente de la distancia.

o ps_force_linear indica una fuerza que aumenta linealmente. A la distancia máxima la

fuerza es 0 mientras que en la posición del atractor la fuerza tiene el valor force indicado.

o ps_force_quadratic indica que la fuerza aumenta de manera cuadrática.

• 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

sea false se moverá hacia él con velocidad constante.

Las siguientes funciones definen atractores. Nota que todas necesitan como primer argumento el índice del sistema de

partículas al cual pertenecen.

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

a esta función si ya no necesitas el atractor para ahorrar espacio.

part_attractor_destroy_all(ps) Destruye todos los atractores que hayan sido creados en el sistema

de partículas ps.

part_attractor_exists(ps,ind) Devuelve true si el atractor ind existe dentro del sistema de

partículas ps.

part_attractor_clear(ps,ind) Devuelve los valores del atractor a sus valores por defecto.

part_attractor_position(ps,ind,x,y) Establece a (x,y) la posición del atractor ind.

part_attractor_force(ps,ind,force,dist,kind,aditive) Establece las propiedades de

fuerza del atractor ind.

Destructores

Los destructores destruyen las partículas cuando aparecen dentro de su región. Un sistema de partículas puede tener

un número arbitrario de destructores. Un destructor cuenta con las siguientes propiedades:

• 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

primer argumento el índice del sistema de partículas al que pertenece el destructor.


part_destroyer_create(ps) Crea un nuevo destructor en el sistema de partículas ps. Devuelve el índice

del destructor. Este índice debe usarse en las llamadas a las funciones siguientes para configurar las propiedades del

destructor.

part_destroyer_destroy(ps,ind) Destruye el destructor ind dentro del sistema de partículas ps.

Llama a esta función cuando ya no hagas uso del destructor para ahorrar espacio.

part_destroyer_destroy_all(ps) Destruye todos los destructores que hayan sido creados en el

sistema de partículas ps.

part_destroyer_exists(ps,ind) Devuelve true si el destructor ind existe dentro del sistema de

partículas ps.

part_destroyer_clear(ps,ind) Devuelve el destructor a sus valores por defecto.

part_destroyer_region(ps,ind,xmin,xmax,ymin,ymax,shape) Establece la región del

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

número arbitrario de deflectores. Un deflector tiene las siguientes propiedades:

• 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:

o ps_deflect_horizontal refleja las partículas horizontalmente; normalmente usado

para paredes verticales

o ps_deflect_vertical refleja las partículas verticalmente; normalmente usado para

paredes horizontales

• friction la cantidad de fricción como resultado de un impacto con el deflector. Entre mayor sea este

valor más se alentará la particular al impactarse con el deflector.

Se tienen las siguientes funciones para configurar las propiedades del deflector. Nota que todas necesitan como primer

argumento el índice del sistema de partículas al cual pertenece el deflector.

part_deflector_create(ps) Crea un nuevo deflector en el sistema de partículas ps. Devuelve el índice

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

función cuando ya no necesites el deflector para ahorrar espacio.

part_deflector_destroy_all(ps) Destruye todos los deflectores que hayan sido creados en el sistema

de partículas ps.

part_deflector_exists(ps,ind) Devuelve true si el deflector ind existe dentro del sistema de

partículas ps.
part_deflector_clear(ps,ind) Devuelve el deflector a sus valores por defecto.

part_deflector_region(ps,ind,xmin,xmax,ymin,ymax) Establece la región para el


deflector.

part_deflector_kind(ps,ind,kind) Establece el tipo de deflector.

part_deflector_friction(ps,ind,friction) Establece la fricción del deflector.

Cambiadores

Los cambiadores cambian ciertas partículas cuando aparecen dentro de su región. Un sistema de partículas puede tener

un número arbitrario de cambiadores. Un cambiador tiene las siguientes propiedades:

• 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.

• parttype2 indica el tipo de partícula al que se cambiará.

• kind indica el tipo del cambiador. Puede tener uno de los siguientes valores:

o ps_change_motion sólo cambia los parámetros de movimiento de la partícula, no el color o

forma ni las propiedades de tiempo de vida

o ps_change_shape sólo cambia los parámetros de forma de la partícula como el tamaño, el

color y forma

o ps_change_all cambia todos los parámetros, básicamente significa que la partícula es

destruida y se crea una nueva del nuevo tipo.

Las siguientes funciones permiten establecer las propiedades del cambiador. Nota que cada una de ellas necesita como

primer argumento el índice del sistema de partículas al que pertenece el cambiador.

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(ps,ind) Destruye el cambiador ind en el sistema de partículas ps. Llama a esta

función si ya no necesitas el cambiador para ahorrar espacio.

part_changer_destroy_all(ps) Destruye todos los cambiadores que hayan sido creados en el sistema

de partículas ps.

part_changer_exists(ps,ind) Devuelve true si el cambiador ind existe 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.

part_changer_types(ps,ind,parttype1,parttype2) Establece el tipo de partícula que debe

ser cambiado y el tipo al que debe ser cambiado.

part_changer_kind(ps,ind,kind) Establece el tipo de cambiador.

Ejemplo de fuegos artificiales

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();

// the firework particles


pt1 = part_type_create();
part_type_shape(pt1,pt_shape_flare);
part_type_size(pt1,0.1,0.2,0,0);
part_type_speed(pt1,0.5,4,0,0);
part_type_direction(pt1,0,360,0,0);
part_type_color1(pt1,c_red);
part_type_alpha2(pt1,1,0.4);
part_type_life(pt1,20,30);
part_type_gravity(pt1,0.2,270);

// 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

efecto durará para siempre.

Juegos multijugador

Esta funcionalidad sólo está disponible en la versión registrada de Game Maker.

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.

La ayuda sobre este tema se encuentra en las siguientes secciones:

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:

mplay_init_ipx()inicializa una conexión IPX.


mplay_init_tcpip(addr)inicializa una conexión TCP/IP. addr es una cadena que indica la dirección web o
IP, p. Ej. 'www.gameplay.com' o '123.123.123.12', posiblemente seguida por un número de puerto (p. Ej. ':12'). Sólo

se necesita la dirección para unirse a una sesión (ve a continuación). En una red local no se necesitan direcciones.

mplay_init_modem(initstr,phonenr)inicializa una conexión vía módem. initstr es la cadena de


inicialización para el módem (puede estar vacía). phonenr es una cadena que contiene el número telefónico a marcar

(p. Ej. '0201234567'). Sólo se necesita el número telefónico al unirse a una sesión (ve a continuación).

mplay_init_serial(portno,baudrate,stopbits,parity,flow) inicializa una conexión en


serie. portno es el número de puerto (1-4). baudrate es la velocidad a emplear en baudios (100-256K). stopbits

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

cambiar la configuración manualmente.

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

una conexión exitosa disponible puedes emplear la siguiente función

mplay_connect_status()devuelve el estado de la conexión actual. 0=no hay conexión, 1=conexión IPX,


2=conexión TCP/IP, 3=conexión vía módem, y 4=conexión serial.

Para finalizar la conexión llama a la función

mplay_end() finaliza la conexión actual.

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

de tu computadora. La siguiente función te será de ayuda:

mplay_ipaddress()devuelve la dirección IP de tu máquina (p. Ej. '123.123.123.12') como cadena. Puedes p.


Ej. mostrarla en pantalla. Nota que esta rutina es lenta por lo que mejor no la llames a menudo.

Creando y uniéndose a sesiones

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:

mplay_session_create(sesname,playnumb,playername) Crea una nueva nueva sesión en la


conexión actual. sesname es una cadena que indica el nombre de la sesión. playnumb indica el número máximo de

jugadores permitidos para este juego (usa 0 para un número arbitrario de jugadores). playname es tu nombre como

jugador. Indica si ha tenido éxito.

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

importantes para esto:

mplay_session_find() Busca todas las sesiones que aún aceptan jugadores y devuelve el número de

sesiones encontradas.

mplay_session_name(numb) Devuelve el nombre de la sesión número numb (0 es la primer sesión). Esta


rutina puede ser llamada sólo después de haber llamado a la anterior.

mplay_session_join(numb,playername) Con esta rutina te unes a la sesión número numb (0 es la

primer sesión). playername es tu nombre como jugador. Indica si ha tenido éxito.

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).

Para checar el estado de la sesión actual puedes usar la siguiente función

mplay_session_status() Devuelve el estado de la sesión actual. 0 = no hay sesión, 1 = sesión creada, 2 =


se unió a la sesión.

Un jugador puede detener una sesión empleando la siguiente rutina:

mplay_session_end() Finaliza la sesión para este jugador.

Jugadores

Cada instancia del juego que se una a una sesión es un jugador. Como se indicó antes, los jugadores tienen nombres.

Hay tres rutinas referentes a los jugadores:

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

recibir mensajes de otros jugadores.

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

mode). Para cambiar esto usa la siguiente rutina:

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.

Existen las siguientes rutinas de mensajes:

mplay_message_send(player,id,val) Envía un mensaje al jugador player (ya sea un identificador o


un nombre; usa 0 para enviar el mensaje a todos los jugadores). id es un entero que funciona como identificador del

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

contiene un mensaje de texto la longitud máxima del mensaje es de 30000 caracteres.

mplay_message_send_guaranteed(player,id,val) Envía un mensaje al jugador player (ya sea


un identificador o un nombre; usa 0 para enviar el mensaje a todos los jugadores). id es un entero que funciona como

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

contiene un mensaje de texto la longitud máxima del mensaje es de 30000 caracteres.


mplay_message_receive(player) Recibe el siguiente mensaje de la cola de mensajes que llegó del
jugador player (identificador o nombre). Usa 0 para indicar mensajes de cualquier jugador. La rutina indica si de hecho

hubo un nuevo mensaje. Si es así, puedes emplear las siguientes rutinas para obtener su contenido:

mplay_message_id() Devuelve el identificador del último mensaje recibido.


mplay_message_value() Devuelve el valor del último mensaje recibido.
mplay_message_player() Devuelve el jugador que envió el último mensaje recibido.
mplay_message_name() Devuelve el nombre del jugador que envió el último mensaje recibido.
mplay_message_count(player) Devuelve el número de mensajes restantes en la cola de espera del
jugador player (usa 0 para contar todos los mensajes).

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

mensajes diferentes causaría serios problemas).

Usando DLLs

Esta función solo está disponible en la versión registrada de Game Maker.

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

sea un valor real o una cadena terminada en carácter nulo.

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;

uses SysUtils, Classes;

function MyMin(x,y:double):double; cdecl;


begin
if x<y then Result := x else Result := y;
end;

var res : array[0..1024] of char;

function DoubleString(str:PChar):PChar; cdecl;


begin
StrCopy(res,str);
StrCat(res,str);
Result := res;
end;

exports MyMin, DoubleString;

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

del juego usarías el siguiente código GML:

{
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:

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.

Así, por ejemplo, escribirías:

{
aaa = external_call(global.mmm,x,y);
sss = external_call(global.ddd,'Hello');
}

Si ya no utilizarás la DLL, lo mejor sería liberarla.

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.

en el evento game end.

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

siguiente función del GML

execute_string(str) Ejecuta la pieza de código en la cadena str.

Alternativamente puedes dejar que la DLL cree un archivo con un script que puede ser ejecutado (esta función también

puede ser empleada para luego modificar el comportamiento del juego).

execute_file(fname) Ejecuta el código en el archivo fname.

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:

window_handle() Devuelve el handle de la ventana principal.

Nota: las DLLs no pueden ser empleadas en modo seguro.

El empleo de DLLs externas es una función extremadamente poderosa. Pero por favor haz uso de ellas sólo si sabes lo

que estás haciendo

Gráficos 3D

Esta función sólo está disponible en la versión registrada de Game Maker.

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

esto por ti mismo (o no usar el ratón del todo).

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

circunstancias tendrás que hacer las cosas por ti mismo.

• 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

de perspectiva, hidden surface removal, iluminación, y niebla, sin mucha explicación.

• 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á

realmente optimizado en velocidad.

Si todo esto no te desanimó, continúa leyendo.

Esta sección se divide en los siguientes temas:

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

2D si lo deseas. Las siguientes dos funciones existen para ello.

d3d_start ( ) Inicia el modo 3D.

d3d_end ( ) Detiene el modo 3D.

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

sobreponerse no tengan la misma profundidad!

En segundo lugar, la proyección ortográfica es remplazada por una perspectiva. Esto significa lo siguiente.

Normalmente el tamaño de las instancias en la pantalla es independiente de su profundidad. Con una

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

del cuarto o la vista).

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

para las vistas 3-dimensionales.


En realidad puedes cambiar hidden surface removal (eliminar las superficies ocultas) y la proyección de

perspectiva encendido o apagado usando las siguientes funciones.

d3d_set_hidden (habilita) Habilita eliminar superficie oculta (true) o la deshabilita

(false).

d3d_set_perspective(habilita) Habilita el uso de una proyección en perspectiva

(true) o la deshabilita (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:

d3d_set_depth(profundidad) Establece la profundidad usada para dibujar.

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

diferentes profundidades. A partir de este momento hablaremos de la coordenada-z en vez de la profundidad

(depth). Así que debemos especificar coordenadas como (x,y,z). Para esto existe una versión especial de

funciones de dibujado avanzado:

d3d_primitive_begin(tipo) Inicia una primitiva 3D de un tipo indicado: pr_pointlist,


pr_linelist,pr_linestrip,pr_trianglelist,pr_trianglestrip o
pr_trianglefan.
d3d_vertex(x,y,z) Añade el vértice (x,y,z) a la primitiva, usando color y valor alfa

especificados con anterioridad.

d3d_vertex_color(x,y,z,col,alpha) Añade el vértice (x,y,z) a la primitiva, con su

propio color y valor alpha. Esto te permite crear primitivas con un suave cambio de color y valores

alfa.

d3d_primitive_end( ) Finaliza la descripción de la primitiva. Esta función realmente la

dibuja.

Por ejemplo, para dibujar un tetraedro (pirámide de tres lados) situada en el plano z=0 con su punta en z =

200, puedes usar el siguiente código:


{
d3d_primitive_begin(pr_trianglelist);
d3d_vertex(100,100,0);
d3d_vertex(100,200,0);
d3d_vertex(150,150,200);
d3d_vertex(100,200,0);
d3d_vertex(200,200,0);
d3d_vertex(150,150,200);
d3d_vertex(200,200,0);
d3d_vertex(100,100,0);
d3d_vertex(150,150,200);
d3d_vertex(100,100,0);
d3d_vertex(100,200,0);
d3d_vertex(200,200,0);
d3d_primitive_end();
}

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

antes añadiendo la función draw_set_color(col) entre las vértices.

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-

izquierda es (0,0). En ese caso necesitas girar la textura verticalmente.

d3d_primitive_begin_texture(tipo,texid) Inicia una primitiva 3D del tipo

indicado con la textura indicada.

d3d_vertex_texture(x,y,z,xtex,ytex) Añade el vértice (x,y,z) a la primitiva con

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.

d3d_primitive_end() Finaliza la descripción de la primitiva. Esta función en realidad la

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

tarea de definir tus polígonos de la manera correcta. Existe la siguiente función:

d3d_set_culling(separar) Indica iniciar backface culling (true) o detener backface

culling (false).

Dibujando formas básicas

Existen varias funciones para dibujar formas básicas, como bloques y paredes. Nota que estas formas también

funcionan correctamente con backface culling.

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

pasos rotacionales deben ser tomados. Un valor típico es 24.


d3d_draw_wall(x1,y1,z1,x2,y2,z2,texid,hrepeat,vrepeat) Dibuja

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.

vrepeat hace lo mismo con el borde vertical.

La siguiente pieza de código dibuja dos bloques:

{
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

miras, las siguientes funciones existen.

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-

xy como en un primera persona puedes usar

{
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

los tres siguientes parámetros. Finalmente usamos el vector up como antes.

¡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

poner la proyección que desees. ¡Esto debe hacerse en un evento Draw!

Existe también una versión extendida de la función anterior:

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

vertical y horizontal de la vista, y los planos cercanos y lejanos de recorte.

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:

d3d_set_projection_ortho(x,y,w,h,ángulo) Pone una proyección normal ortográfica

del área indicada en el cuarto, girando sobre el ángulo indicado.

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

ejemplo siguiente muestra como crear una capa con el puntaje.

{
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

que ponen la transformación y las funciones que añaden transformaciones.

d3d_transform_set_identity () Pone la transformación a la identidad (ninguna

transformación).

d3d_transform_set_translation(xt,yt,zt) Pone la transformación a una

conversión sobre el vector indicado.

d3d_transform_set_scaling(xs,ys,zs) Pone la transformación a un

escalamiento con las cantidades indicadas.

d3d_transform_set_rotation_x(ángulo) Pone la transformación a una rotación

alrededor del eje-x con la cantidad indicada.

d3d_transform_set_rotation_y(ángulo) Pone la transformación a una rotación

alrededor del eje-y con la cantidad indicada.


d3d_transform_set_rotation_z(ángulo) Pone la transformación a una rotación

alrededor del eje-z con la cantidad indicada.

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.

d3d_transform_add_translation(xt,yt,zt) Añade una transformación sobre

vector indicado.

d3d_transform_add_scaling(xs,ys,zs) Añade un escalamiento con las

cantidades indicadas.

d3d_transform_add_rotation_x(ángulo) Añade una rotación alrededor del eje-x

con la cantidad indicada.

d3d_transform_add_rotation_y(ángulo) Añade una rotación alrededor del eje-y

con la cantidad indicada.

d3d_transform_add_rotation_z(ángulo) Añade una rotación alrededor del eje-z

con la cantidad indicada.

d3d_transform_add_rotation_axis(xa,ya,za,ángulo) Añade una

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

funciones de añadir transformaciones.

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

podemos usar el código siguiente.

{
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:

d3d_transform_stack_clear() Limpia el montón de transformaciones.

d3d_transform_stack_empty() Devuelve si el montón de transformación está vacío.

d3d_transform_stack_push() Empuja la transformación actual sobre el montón.

Devuelve si hay espacio sobre el montón para empujarlo allí (si olvidas sacar la transformación en

algún momento te quedarás sin espacio en el montón).

d3d_transform_stack_pop() Saca la transformación superior del montón y la hace la

actual. Devuelve si había una transformación sobre el montón.

d3d_transform_stack_top() Hace a la transformación superior la actual, pero no lo

quita del montón. Devuelve si había tras una transformación sobre el montón.

d3d_transform_stack_discard() Quita la transformación superior del montón, pero

no lo hace la actual. Vueltas si había tras una transformación en el montón.

La utilización de la transformación es un mecanismo poderoso. Pero sé cuidadoso y siempre restaura la

transformación a la identidad una vez que terminaste.

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

lejos. Para habilitar o deshabilitar la niebla usa la función siguiente:

d3d_set_fog(habilitar,color,principio,fin) Habilita o deshabilita el

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

alumbradas no es fácil pero el efecto es muy bueno.

Para permitir la iluminación puedes usar la función siguiente;

d3d_set_lighting(habilitar) Habilita o inhabilita el uso de iluminación.

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

shading es usado. Esto puede ser cambiado usando la función siguiente:

d3d_set_shading(smooth) Establece si usar smooth shading o no.

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

especular.) Las funciones siguientes existen para definir y usar luces:

d3d_light_define_direction(ind,dx,dy,dz,col) Define una luz dirigida.

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.

d3d_light_define_point(ind,x,y,z,rango,col) Define una luz de punto. ind

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.

Esta función no enciende la luz.

d3d_light_enable(ind,permitir) Habilita (true) o deshabilita (false) la luz número

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

adicionales están disponibles para definir los vértices de primitivas:

d3d_vertex_normal(x,y,z,nx,ny,nz) Añade el vértice (x,y,z) a la primitiva, con

vector normal (nx,ny,nz).


d3d_vertex_normal_color(x,y,z,nx,ny,nz,col,alpha) Añade el vértice

(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,

mezclándose con el color y valor alpha establecidos antes.

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

(xtex,ytex) en la textura, mezclándose con su propio color y valor alpha.

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

llamada a ese modelo.

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.

d3d_model_destroy(ind) Destruye el modelo especificado, liberando memoria.

d3d_model_clear(ind) Limpia el modelo especificado, eliminando todas sus primitivas.

d3d_model_save(ind,fname) Salva el modelo ind al archivo especificado en fname.

d3d_model_load(ind,fname) Carga el modelo desde el archivo especificado.

d3d_model_draw(ind,x,y,z,texid) Dibuja el modelo en la posición (x,y,z). texid es la textura que se

aplicará al modelo. Usa -1 para no usar texturas. Si quieres rotar o escalar el modelo usa las rutinas de transformación

descritas en el apartado de Transformaciones.


Para cada primitiva existe una función equivalente que la añade a un modelo. Estas funciones se usan igual que las que

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_primitive_begin(ind,kind) Añade al modelo una primitiva 3D del tipo siguiente

indicado en kind: pr_pointlist, pr_linelist, pr_linestrip, pr_trianglelist,


pr_trianglestrip o pr_trianglefan.
d3d_model_vertex(ind,x,y,z) Añade el vértice (x,y,z) al modelo.

d3d_model_vertex_color(ind,x,y,z,col,alpha) Añade el vértice (x,y,z) al modelo con su

propio color y valor de transparencia.

d3d_model_vertex_texture(ind,x,y,z,xtex,ytex) Añade el vértice (x,y,z) al modelo con la

posición (xtex,ytex) en la textura.

d3d_model_vertex_texture_color(ind,x,y,z,xtex,ytex,col,alpha) Añade el

vértice (x,y,z) al modelo con valores de textura y color.

d3d_model_vertex_normal(ind,x,y,z,nx,ny,nz) Añade el vértice (x,y,z) al modelo con el

vector normal (nx,ny,nz).

d3d_model_vertex_normal_color(ind,x,y,z,nx,ny,nz,col,alpha) Añade el vértice

(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.

d3d_model_primitive_end(ind) Finaliza la descripción de la primitiva en el modelo.

Además de primitivas también puedes añadir formas al modelo. Las funciones son análogas a las ya vistas usando el

índice del modelo en lugar del de la textura:

d3d_model_block(ind,x1,y1,z1,x2,y2,z2,hrepeat,vrepeat) Añade un bloque al

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.

d3d_model_ellipsoid(ind,x1,y1,z1,x2,y2,z2,hrepeat,vrepeat,steps) Añade una

elipsoide al modelo.

d3d_model_wall(ind,x1,y1,z1,x2,y2,z2,hrepeat,vrepeat) Añade una pared al modelo.

d3d_model_floor(ind,x1,y1,z1,x2,y2,z2,hrepeat,vrepeat) Añade un suelo al modelo.


El uso de modelos puede aumentar considerablemente la velocidad de los gráficos en los juegos 3D, así que deberías

usarlo siempre que sea posible

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.

Lo segundo que debemos tener en cuenta es la finalidad de nuestro


script antes de empezar es decir debemos saber para que haremos
nuestro script. No es lo mismo hacer un script para calcular la
hipotenusa de un triangulo (xd quizas me pase un poco) o crear un
script para dibujar un rectangulo (demasiado facil). Vamos que si es
la primera vez que creas un script no intentes hacer algo dificil.

Lo tercero Hay que tener unos conocimientos minimos del gml no se


puede usar lo de arrastrar y soltar por que no funciona aqui solo
codigo. Los conocimientos de gml van en proporcion a la dificultad de
lo que quieras hacer. Volviendo al punto 2 si no tienes muchos
conocimientos y quieres hacer algo muy dificil vas a tener que estar
preguntando.

Lo cuarto debes saber como se aplica un script. Si no sabes a


continuacion te lo explico:

Puedes usarlo usando arrastrar y soltar es un icono en la pestaña


code o poniendo en el codigo (usamos el script que he puesto arriba)
draw() lo de los parentesis es para poner los arguments pero eso ya
lo explicare.

Y la quinta cosa que debes tener en cuenta es en que evento


aplicaras el script si lo pondras en step o en create. Esto importa
mucho ya que si lo pones en create se hara nada mas se cre el
personaje si lo pones en step lo estara haciendo continuamente. Otro
ejemplo si lo pones en animation end lo ejecutara cuando se acabe la
animacion.
Arrays.

Imaginemos que el usuario debe entrar 5 números para hacer un


promedio. Podríamos usar las variables num1, num2, num3, num4 y
num5, por ejemplo. Pero si el usuario tuviera que introducir 100
valores a lo largo de la partida, la cosa ya sería más difícil. Para ello
tenemos las arrays.

Una array es una estructura de valores ordenada de forma secuancia.


una forma gráfica de expresar un array sería esta:

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

Y podemos modificarlos a nuestro antojo

Array[1]-=3
Array[3]=-4

4 -4 1

Vamos a hacer un programa que nos va a pedir 10 valores y


seguidamente hará el promedio. Añade un objeto y en su evento de
creación añade el código necesario para que nos pida los valores y
haga la mediana:

numero[1]=get_integer("Introduce el primer numero","")


numero[2]=get_integer("Introduce el segundo numero","")
numero[3]=get_integer("Introduce el tercer numero","")
numero[4]=get_integer("Introduce el quarto numero","")
numero[5]=get_integer("Introduce el quinto numero","")
numero[6]=get_integer("Introduce el sexto numero","")
numero[7]=get_integer("Introduce el septimo numero","")
numero[8]=get_integer("Introduce el octavo numero","")
numero[9]=get_integer("Introduce el noveno numero","")
numero[10]=get_integer("Introduce el decimo numero","")

mediana=mean(numero[1],numero[2],numero[3],numero[4],numero
[5],numero[6],numero[7],numero[8],numero[9],numero[10])

Ahora ne su evento de dibujo añade el código necesario para que se


dibuje la variable de la mediana:

draw_text(50,50,string(mediana))

Y "voilá". Aunque viendo este ejemplo no pueda parecer de utilidad,


las arrays nos pueden evitar de usar muchas variables y hacernos un
lío.

Lectura / Escritura de Archivos INI.


Los archivos *.ini son archivos especialmente
diseñados para trabajar con los datos organizados en
registros y variables.

Para empezar, los archivos *.ini están divididos en


registros que a su vez estan divididos en variables.
Un ini común:

[HP]

hp = 375
maxhp = 500

[nivel]

exp = 5075
nivel = 4

Este ini tiene dos registros, HP y nivel. Cada uno


de ellos tiene dos variables. HP tiene las variables
hp y maxhp, con los valores 375 y 500,
respectivamente. nivel tiene las variables exp y
nivel, con los valores 5075 y 4.
Game Maker proporciona funciones para leer y
escribir datos fácilmente en los archivos *.ini.
Para empezar, hay que abrirlo con:

ini_open(fname);

Donde fname es un string conteniendo la dirección


relativa o absoluta del archivo.

Una vez abierto, podemos realizar dos tipos de


operaciones con él:

Operaciones de escritura:

Mediante las funciones de escritura podemos escribir


reales o strings. Las funciones son:
ini_write_real(key, var, value);
ini_write_string(key, var, value);
Estas funciones escriben el valor value en la
variable var del registro key. Para escribir valores
reales recomiendo usar la función de escritura de
strings mediante un cast del valor a string mediante
la función string(x).

Operaciones de lectura:

Mediante las funciones de lectura, podemos leer


valores del ini. Son:
ini_read_real(key, var, def);
ini_read_string(key, var, def);
Estas funciones devuelven el valor de la variable
var del registro key. Si no se puede ejecutar la
lectura, devuelven el valor def.
Una vez se ha acabado de ejecutar operaciones con el
ini, hay que cerrarlo mediante la función:

ini_close();

Bucles
Hola a todos, hoy explicaré los bucles.

Los bucles son unas sentencias que permiten repetir un


determinado conjunto de órdenes tantas veces como queramos,
ya sean 1, 3, 7, 10, 57, 94 o infinitas.

Hay 3 tipos de bucles:

Bucle básico, también conocido como while.

Llamado así por la sentencia que lo ejecuta. El bucle while


permite ejecutar un conjunto de instrucciones mientras una
instrucción se cumple.
Se ejecuta así:
while (/*cond*/)
{
//acciones
}
Mientras la sentencia /*cond*/ devuelva diferente de 0
(true), se ejecutará la(s) sentencia(s) //acciones.

Bucle do while.

Básicamente es lo mismo que el bucle básico, pero ejecuta


las instrucciones antes de comprovar la condición. Para
ejecutarlo el código es:
do
{
//acciones
}while(/*cond*/);
Básicamente la explicación es la misma que el while, pero
se ejecuta //acciones antes de comprobar /*cond*/.

Bucle for.

Es un bucle bastante más avanzado. Incluye una


incialización, una condición y una actualización. Se
ejecuta mediante la sentencia:
for (/*init*/; /*cond*/; /*act*/)
{
//acciones
}
Esto equivaldría a:
/*init*/
while (/*cond*/)
{
//acciones
/*act*/
}

Este bucle permite ejecutar una serie de acciones


(//acciones) mientras se cumpla una condición /*cond*/, que
viene inicializada por /*init*/ y se actualiza con /*act*/.

Sentencia repeat.

Es una sentencia exclusiva del GML que es un "pseudobucle".


Se usa así:
repeat (/*num*/)
{
//acciones
}
Repite /*num*/ veces la(s) accion(es) //acciones. Es
considerado un pseudobucle porque no utiliza una condición
para regular su uso.

FSM IA (inteligencia Artificial)


sobre Inteligencia Artificial. Lo primero que veremos es la
arquitectura de máquina de estados finitos. Esta arquitectura es la
más usada para programar la IA en los juegos profesionales y
muchos otros ámbitos como la rob?tica, los automatismos o la
simulación.

¿Qué es una máquina de estados finitos?


Una máquina de estados finitos (Finite State Machine, FSM)se
compone de las siguientes partes:
• Estados
• Transiciones
• Acciones

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

La primera condición Si estás cansado indica que si estamos en el


estado de buscar oro pasemos al estado de descanso.
La segunda condición Si te has recuperado indica que si estamos
en el estado de descanso volvamos al estado de buscar oro.
Observad que cada condición sólo tiene sentido si se aplica cuando el
minero está en el estado correcto. Es decir, si el minero está en el
estado de descanso no tiene sentido comprobar si está cansado, ya
que aunque fuera cierto, no haríamos más que decir al minero que se
pusiera en estado de descanso..¡pero el minero ya está en ese
estado!

Cuando tenemos varios estados, puede que desde un estado


podamos pasar a varios estados distintos dependiendo de qué
condiciones se cumplan. Para llevar un control sobre esto y no
perdernos cuando tengamos más estados y varias transiciones
posibles, se suele crear una tabla de transiciones:
Condición\Estado actual A B C
1 C C X
2 X A X
3 X X B

En esta tabla vemos lo siguiente:


• Si estamos en el estado A o B y se cumple la condición 1 pasamos
al estado C
• Si estamos en el estado B y se cumple la condición 2 pasamos al
estado A
• Si estamos en el estado C y se cumple la condición 3 pasamos al
estado B

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

Esta tabla nos dice lo siguiente:


• Si el minero está en el estado de Buscar oro y está cansado, pasa
al estado Descansar.
• Si el minero está en el estado de Descansar y se ha recuperado,
pasa al estado Buscar oro.
• Si el minero está en el estado de Buscar oro o en el de
Descansar y está hambriento, pasa al estado Comer.
• Si el minero está en el estado de Comer y ya se ha saciado, pasa
al estado Buscar oro.

Las condiciones que lanzan transiciones de un estado a otro pueden


ser tan complejas como queramos. Por ejemplo, podemos decir que
un jugador de fútbol sólo tire a portería si tiene el balón, está dentro
del área, el portero está en el centro de la portería y no tiene a
ningún compañero para pasarle el balón.
Acciones
Una acción es la descripción de una actividad que la FSM puede
ejecutar en un momento concreto. Lógicamente, las acciones
dependerán del estado en el que nos encontremos.

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.

Las acciones se dividen en 4 tipos:


• Entrada: son las acciones que se ejecutan al entrar a un estado
concreto.
• Salida: son las acciones que se ejecutan al salir de un estado
concreto.
• Estado: son las acciones que se ejecutan regularmente por estar en
un estado.
• Transción: son las acciones que se ejecutan al pasar de a un
estado concreto a otro.

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.

Para hacer esto posible, necesitaremos que el sistema recuerde el


estado en el que se encontraba antes de entrar al estado global, para
así volver a ese estado una vez que finalize la ejecución del estado
global.

FSMs dentro de FSMs


Las FSMs son herramientas muy potentes, pero conforme crezca la
complejidad del sistema será más difícil entender cómo funciona el
sistema. Para simplificar esto, podemos tener FSMs dentro de otras
FSMs. Por ejemplo, en el juego Unreal 2 hay una FSM general para
controlar a cada enemigo. Pero además dentro de cada estado
existen otras FSMs para realizar tareas específicas, como defender
una zona de ataques enemigos o explorar el nivel.

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.

Hasta aquí la introducción a las máquinas de estados finitos. En la


pr?xima entrega veremos cómo implementar una FSM en GML.

Para finalizar veremos cómo hacer un ejemplo completo de un top-


down shooter (juego de disparos visto desde arriba). Con el fín de
hacer el ejemplo lo más sencillo posible usaremos GM6 registrado
para poder usar las funciones para calcular paths en laberintos. De
todas formas, a parte de esto el ejemplo será igual para GM6 sin
registrar o GM5.

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.

Antes de nada, vamos a crear el escenario.

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.

Bien, ya estamos listos para definir al jugador.

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.

El jugador tendrá 2 estados:


• Vivo: el estado normal del jugador, en el que podrá moverse y
disparar.
• Muerto: el jugador se quedará muerto en el campo de batalla y
lógicamente no podrá disparar ni moverse. Pasado un tiempo, volverá
al estado Vivo perdiendo una vida.
Necesitaremos 2 características para controlar estos estados:
• Salud: la salud que tiene el jugador.
• Vidas: número de vidas que tiene el jugador.
• Tiempo de muerte: tiempo que el jugador permanece en el
estado Muerto antes de volver al estado Vivo.

La tabla de transiciones del jugador será bastante sencilla:


Condición\Estado actual Vivo Muerto
Salud<=0 Muerto X
Vidas>0 & Tiempo de muerte=0 X Vivo

Viendo esta tabla sabemos que:


• Si el jugador está en el estado Vivo y su salud se agota
(Salud<=0) pasa al estado Muerto.
• Si el jugador está en el estado Muerto y ya ha cumplido su tiempo
de muerte (Tiempo de muerte=0) y todavía le quedan vidas
(Vidas>0) pasa al estado Vivo.

Bueno, ya podemos empezar a programar el jugador.


Creamos un objeto y lo llamamos objPlayer. Creamos un sprite y lo
llamamos sprPlayerVivo. Este sprite será un círculo azul de tamaño
32x32 y centro en (16,16). Copiamos el sprite, le cambiamos el color
a negro y lo llamamos sprPlayerMuerto. Ahora volvemos al sprite
sprPlayerVivo y lo editamos, dibujando una línea amarilla horizontal
desde el punto (16,16) hasta el punto (31,16). Esto nos servirá para
poder ver la dirección en la que mira el jugador en todo momento. En
el Evento Create del objeto especificamos las características de la
FSM:
/*CARACTERISTICAS DE LA FSM*/
estado = "Vivo";
execute_string(estado+"(0)");

Y a continuación declaramos las características del jugador:

/*CARACTERISTICAS DEL JUGADOR*/


vidas = 5;
saludMax = 100;
salud = saludMax;
tiempoMuerteMax = 150;
tiempoMuerte = tiempoMuerteMax;

En el Evento Step como siempre, definimos el funcionamiento de la


FSM:

execute_string(estado+"(1)");
image_angle = direction;//rotar el sprite (s?lo GM6)

Ahora vamos a definir los estados. Empezamos por el estado


Muerto:

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;
}

Ahora creamos el estado Vivo:

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;
}

En este estado, el jugador realizará 2 acciones, Disparar y Andar.


Creamos un script y lo llamamos scrAndar:
//scrAndar(avanzar, retroceder, izquierda, derecha, velocidad)
var avanzar, retroceder, izquierda, derecha, vel;
avanzar = argument0;
retroceder = argument1;
izquierda = argument2;
derecha = argument3;
vel = argument4;

if (izquierda) direction += 5;
else if (derecha) direction -= 5;

if (avanzar) retroceder = false;


if ((avanzar)||(retroceder)){
var i;
for (i=0;i<=vel;i+=1){
if (place_meeting(x+(i+1)*cos(degtorad(direction+retroceder*180)),
y-(i+1)*sin(degtorad(direction+retroceder*180)), objPared)){
x += i*cos(degtorad(direction+retroceder*180));
y -= i*sin(degtorad(direction+retroceder*180));
break;
}else if (i = vel){
x += vel*cos(degtorad(direction+retroceder*180));
y -= vel*sin(degtorad(direction+retroceder*180));
}
}
}

Este script es bastante sencillo: se chequean las teclas de entrada y


en función de ellas se calcula la dirección y se mueve al personaje si
es que no choca contra una pared. Observad que las teclas se pasan
como argumento al script (scrAndar(keyboard_check(vk_up),
keyboard_check(vk_down), keyboard_check(vk_left),
keyboard_check(vk_right), velocidad);) con lo que este script
servirá para mover al personaje con cualquier combinación de teclas
que queréis. Podéis incluso usarlo para un segundo jugador que
utilice las teclas WASD:

scrAndar(keyboard_check(ord('W')),
keyboard_check(ord('S')),keyboard_check(ord('A')),keyboard_check(
ord('D')), velocidad);

Sólo tenéis que fijaros en el orden en el que se ponen las teclas en el


script (avanzar-retroceder-izquierda-derecha).

Si os habéis fijado, el script toma comom argumento una variable


llamada velocidad. Podríamos hacer que esta variable fuera local del
script (var) y definirla ahí mismo, pero resulta mejor hacer que la
velocidad sea una característica del jugador, así podremos hacer que
jugadores con distintas velocidades puedan usar este mismo script.
Para ello, volvemos al Evento Create y ponemos:

velocidad = 4;

El siguiente paso es crear el script scrDisparar:

//scrDisparar(disparar)
if (tiempoDisparo = 0){
if (argument0){
with (instance_create(x, y ,objBala)){
speed = 8;
direction = direction;
parent = id;
}
tiempoDisparo = tiempoDisparoMax;
}
}else tiempoDisparo -=1;

Ahora debemos definir la variable tiempDisparo que dirá el tiempo


entre dos disparos sucesivos. Vamos de nuevo al Evento Create de
objPlayer y ponemos:

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();

También añadimos el Evento de Colisión con el objeto objPlayer


y añadimos lo siguiente:

if ((parent != other.id)&&(other.estado != "Muerto")){


other.salud -=1;
instance_destroy();
}

Lo que hacemos es ver si la bala está chocando con el jugador y el


juagdor no ha sido quien la ha disparado ni está muerto. Entonces,
disminuimos la salud del jugador. Este mismo código nos servirá más
adelante.

Listo! Ya podemos crear un room, poner una instancia de objPlayer,


unas cuantas paredes y darnos una vuelta disparando :D

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.

Para controlar estos estados necesitaremos las siguientes


características:
• Salud.
• Jugador visible: si puede ver al jugador.
• Solo: indica si el enemigo se encuentra solo o va acompañado por
más enemigos.
Ya podemos crear la tabla de transiciones:
Condición\Est Ataca Patrull Ayuda **EnemigoMuert
Huir
ado actual r ar r o**
Salud < salud
del jugador/4 Huir X X X X
& Solo
Jugador visible X X Atacar Atacar X
Jugador no Patrull
X X X X
visible ar
Compañero Patrull
X X X X
cerca ar
Mensaje
X X Ayudar X X
"Ayuda"
Recibir balazo X X Atacar X X
EMuert EMuert EMuert
Salud <= 0 EMuerto X
o o o

Bueno, ya podemos empezar a definir el enemigo. Creamos el objeto


objEnemy y definimos sus características:

/*CARACTERISTICAS DE LA FSM*/
estado = "Patrullar";
estado_global = "";
mensaje = -1;

/*CARACTERISTICAS DEL ENEMIGO*/


saludMax = 100;
salud = saludMax;
recibirBalazo = false;
/*PROPIEDADES*/
velocidadNormal = 4;
velocidadRapida = 6;
tiempoDisparoMax = 15;
tiempoDisparo = 0;

Observad que no hemos definido las características solo ni


jugadorVisible. Esto es debido a que estas características necesitan
de varias operaciones para chequear si son verdaderas o falsas, por
ello es mejor realizar unos scripts que devuelvan true o false y así
podremos usarlos como si se trataran de variables.

Dicho, esto, comenzamos por definir el script jugadorVisible. Este


script verá si el enemigo puede ver al jugador, lanzando una línea
recta y viendo si intersecta con alguna pared. Si esta línea no se
cruza con nada, significará que podemos ver al jugador. Además,
tendremos en cuenta hacia dónde está mirando el enemigo. Es decir,
el enemigo no podr? ver al jugador si está de espaldas a éste:

//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;

Ahora creamos el script scrSolo que servirá para ver si el enemigo


está sólo o tiene algún compañero cerca en estado de ataque o de
ayuda:

//scrSolo() var i, enemigo; for (i=0;i

Ya podemos definir el funcionamiento de la FSM. En el Evento step


ponemos lo de siempre (atención que esta vez sí tenemos un estado
global, el estado EnemigoMuerto):

if (estado_global != "") execute_string(estado_global+"(1)");


else{

if (salud <= 0){


estado_global = "EnemigoMuerto";
execute_string(estado_global+"(0)");
}else execute_string(estado+"(1)");

}
image_angle = direction;

Ya podemos empezar a definir los estados. Empezamos por el estado


Patrullar:

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;
}

En ausencia de condiciones el enemigo se dedicará a vagar por el


escenario. Definimos esta acción en el script scrVagar:

//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));

Este script hace que el enemigo se mueva en la dirección en la que


va. Si ve que siguiendo en esa dirección chocará con una pared,
intenta moverse en una dirección perpendicular. Si aún así choca con
otra pared, se mueve en la otra dirección perpendicular. Si vuelve a
chocar, se da la vuelta y vuelve por donde había venido. Observad
que para realizar los chequeos usamos 2*velocidadNormal. De esta
forma podemos hacer que el enemigo "vea el futuro" y podrá
moverse de una manera más realista, ya que si usáramos el valor
velocidadNormal tendría menos tiempo de reacción y siempre iría
pegado a las paredes.

Para saber si hemos recibido un balazo nos serviremos del objeto


objBala. En él, creamos el Evento de colisión con el objeto
objEnemy y ponemos lo siguiente:
if ((parent.object_index !=
other.object_index)&&(other.estado_global = "")){

other.salud -=1;
other.recibirBalazo = true;
instance_destroy();

Como véis, es casi igual que el del jugador.

Ahora definimos el estado Atacar:

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;

El script scrDebil se encargará de chequear si el enemigo tiene


menos de la energía indicada del jugador:

//scrDebil(energia)
if (instance_exists(objPlayer)){

if (salud < objPlayer.salud/argument0) return true;

}
return false;

El script scrNearestFriend devuelve la id del compañero más


cercano que no está muerto:

var i, dist_min, nearest;


dist_min = room_width;
nearest = 0;
for (i=0; i
var friend, dist;
friend = instance_id[i];
if ((friend.object_index = object_index)&&(friend.id != id)){
if (friend.estado_global = ""){
dist = distance_to_object(friend);
if (dist nearest = friend; } } } }
return nearest;

Por último, el script scrAcercarse se encarga de acercarse al


personaje hasta la distancia de ataque. Esto puede resultar bastante
complejo ya que muchas veces el enemigo no podrá ir directamente
hasta donde se encuentre el jugador, ya que puede que haya paredes
entre ellos. Para solucionar este problema vamos a usar las potentes
funciones de planificaci?n de movimientos que incorpora GM. Con el
fín de reducir el uso de memoria y aumentar la velocidad del juego,
vamos a crear un objeto controlador para esto.

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*.

En el evento de creación del objeto ponemos:

var width, height;


width = sprite_get_width(sprEnemyVivo);
height = sprite_get_height(sprEnemyVivo);
rejilla = mp_grid_create(0, 0,
floor(room_width/width),floor(room_height/height), width, height);
mp_grid_add_instances(rejilla, objPared, false);

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);

Ahora, cuando desde un enemigo queramos pedirle que calcule un


camino desde la posición actual hasta la posición del jugador
tendremos que hacer:

with (objControlador) mp_grid_path(rejilla, other.path, other.x,


other.y, objPlayer.x, objPlayer.y, true);

Esta función crea un path y lo guarda en other.path, e decir, en la


variable path de la instancia de objEnemy que la llamó. Por ello,
tenemos que crear esta variable en objEnemy:

path = path_add();//creamos un path vac?o


Ahora ya podemos crear el script scrAcercarse:

//scrAcercarse(objetivo)
if (instance_exists(argument0)){

if (distance_to_object(argument0) > 100 ){


if (path_index != path){
with (objControlador) mp_grid_path(rejilla, other.path, other.x,
other.y, argument0.x, argument0.y, true);
path_start(path, velocidadRapida, 0, true);
}else if (path_position = 1) path_end();
return true;
}else path_end();

}
return false;

El script devuelve false si el enemigo está suficientemente cerca


como para atacar y true si todavía debe acercarse (además calcula
un path y se lo asigna al enemigo).

El siguiente script que necesitamos es scrPelear:

direction = point_direction(x, y, objPlayer.x, objPlayer.y );


var i;
if (esquiva = 0) {

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);

El enemigo apunta al jugador y comienza a disparar mientras se va


moviendo alrededor de él y cambiando de dirección. Para poder
manejar este script necesitamos declarar las variables siguientes:

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;
}

Podemos hacer una copia del sprite sprEnemyVivo, oscurecerlo un


poco y guardarlo como sprEnemyMuerto.

Ya hemos llegado al último estado, Ayudar. En este estado el


enemigo se dirige hasta el compañero que le pidió ayuda para
socorrerle. Pero antes de crearlo, vamos a hacer primero el sistema
de mensajes.

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();

En esta ocasión sólo tenemos un tipo de mensaje. Adem?s, no vamos


a mostrar el texto así que no necesitamos inclu?rlo. Y como todos los
mensajes van a tener la misma prioridad podemos usar una cola
normal y corriente.
Creamos ahora nuestro script para enviar mensajes,
scrEnviarMensaje

//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);

//aumentar el identificador del mensaje


objControlador.mensaje_id +=1;

Igual que hicimos en el tutorial anterior, el objeto controlador se


encargará de leer los mensajes con el script scrRecibirMensaje:

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;
}

En este caso, guardamos el remitente en una variable de la instancia


a la que mandamos el mensaje (Destino.remitente = Remitente;).
Por ello, debemos definir esta variable en objEnemy:
remitente = 0;

En el Event Step de objControlador indicamos que lea los


mensajes:

scrRecibirMensaje();

Ahora ya podemos crear el último estado, Ayudar:

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;
}

Ya hemos terminado con la IA! :D

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);
}

instance_create(2*w, room_height-2*h, objPlayer);


view_hborder[0] = ((view_wview[0]-
sprite_get_width(sprPlayerVivo))/2) -1;
view_vborder[0] = ((view_hview[0]-
sprite_get_height(sprPlayerVivo))/2) -1;

En la pestaña de las views tendremos que activar la View 0 y hacerla


visible al comienzo del room.
Ahora desde el editor de rooms añadimos unas cuantas paredes para
crear un nivel laberíntico con algunas zonas despejadas para pelear y
ponemos unos cuantos enemigos.

Ejecutamos el juego e intentamos sobrevivir :D

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;
}

De esta forma la IA se puede programar muy sencillamanete ya que


básicamente se reducen a una serie de if(){}else if(){}else if
(){}.... Esto además hace que sea tremendamente sencillo añadir
nuevos estados o modificar la lógica de los ya presentes.
En juegos como Unreal o Quake se usa este mismo sistema para
programar la IA de los bots (los enemigos). Pero además, utilizan
otros métodos para hacer que los bots "aprendan" e intenten
sorprender al jugador en cada partida.

Para ello, se modifica la lógica de los estados usando métodos como


los algoritmos genéticos, las redes neuronales o las
secuencias de reglas SOAX.

En próximos tutoriales veremos cada una de estas técnicas y


aprenderemos a implementarlas para crear personajes
verdaderamente inteligentes.

correojon, 27 de Mayo de 2006

OCT 22 2007 IA: FSM III


by Fenris78 | 159 Views | 0 Comments | Rating: (0 rates)
En esta nueva entrega del curso de FSMs continuaremos con el
ejemplo anterior y lo haremos un poco más complejo introduciendo la
capacidad de enviar y recibir mensajes. Para empezar, añadiremos la
Central de Policía, capaz de comunicarse con el policía.

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.

Además, el policía podrá enviar mensajes a la Central:


• "Mandar una unidad": si el policía está ocupado y presencia un
delito, enviará un mensaje a la Central para que manden una unidad
a hacerse cargo.

El policía tendrá ahora 2 nuevos estados,


IrAlBancoYDetenerAtraco e IrALaCentralYRecibirInfo. Su tabla
de transiciones será ahora:

Condición\Es BuscarY Entregar IrAlBAr IrAlBan IrALaCe *IrACa


tado Atrapar Criminal YBeber co ntral sa*
Agresividad>=
Entregar
Agresividad X X X X X
Criminal
criminal
Detenciones=
IrAlBarY
Detenciones X X X X X
Beber
requeridas
BuscarY
Detenciones X X X X X
Atrapar
Mensaje IrAlBanc IrAlBanc IrAlBan IrAlBan
X X
"Atraco!" o o co co
Mensaje IrALaCe
X X X X X
"Informaci?n" ntral
Agresividad>= BuscarY
X X X X X
7 Atrapar
Cansancio>=
IrACas
Cansancio_ma IrACasa IrACasa IrACasa IrACasa X
a
x
Cansancio=0
&
BuscarY
Detenciones> X X X X X
Atrapar
=Detenciones
requeridas
Atraco X X X IrAlBarY X X
detenido Beber
Atraco no BuscarY
X X X X X
detenido Atrapar

Recordad que el estado IrACasaYDormir es un estado global y que


al salir de él se puede volver al estado anterior que se estaba
ejecutando.
He tenido que acortar los nombres de los estados para que la tabla
no saliera demasiado grande. Recordad que los estados del policía
eran:
• BuscarYAtraparCriminales
• EntregarCriminal
• IrAlBaryBeber
• **IrACasaYDormir**
• IrAlBancoYDetenerAtraco
• IrALaCentralYRecibirInfo

Para que el policía responda cuando recibe un mensaje, añadimos


una nueva característica en su Evento Create junto con las que ya
habíamos definido en el tutorial anterior:
[code]
mensaje = -1;
[/code]
Esta variable tomará distintos valores dependiendo del mensaje que
reciba el policía, así podremos chequear qué valor tiene y actuar en
consecuencia.

Bueno, es hora de ver cómo haremos el sistema de mensajes.

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").

Los campos Remitente y Destino son muy importantes. Usando las


ids de las instancias podremos decir exactamente a qui?n va dirigido
el mensaje. Además, si en nuestro juego existe más de un policía o
más de una central será imperativo determinar a quién queremos
enviarle el mensaje. Usando la variable id de GM podemos hacerlo
sin mayores complicaciones.

Para el sistema de mensajes vamos a utilizar una cola de prioridad.


Una cola de prioridad es una estructura de datos de GM que permite
introducir valores asignándoles una prioridad. Así, podemos poner
una prioridad alta a los mensajes importantes para que sean
atendidos antes que los mensajes menos importantes. Además, para
manejar todo el sistema de mensajes usaremos un objeto
controlador. Creamos un nuevo objeto y lo llamamos
obj_ControlMensajes. En su Evento Create ponemos el siguiente
código:
[code]
mensajes = ds_priority_create();
mensaje_id=0;
[/code]
Con esto creamos nuestra lista de prioridad e inicializamos la variable
mensaje_id que nos servirá para ir asignando a cada mensaje un
identificador único. Así podremos distinguir los mensajes entre sí.
Para guardar las características de los mensajes usaremos mapas de
memoria, que son otra estructura de datos de GM. Los mapas nos
permiten añadir un valor y asociarlo a una clave. Así, podremos
guardar las características de cada mensaje y asociarlas al
identificador del mensaje para poder acceder a ellas cuando las
necesitemos. Añadimos lo siguiente:
[code]
mensaje_remitente=ds_map_create();
mensaje_destino=ds_map_create();
mensaje_tipo=ds_map_create();
[/code]
Por último creamos un vector que guardará el texto del mensaje. Los
mensajes podran ser de 3 tipos (0, 1 ? 2), así:
[code]
mensaje_texto[0] = "Se esta produciendo un atraco en el banco!";
mensaje_texto[1] = "Dirigete a la central para recibir informacion";
mensaje_texto[2] = "Se esta produciendo un delito, enviad una
unidad!";
[/code]
Puede que os parezca que utilizar un objeto para esto es un gasto
inútil, ya que podría hacerse con variables globales y que cada
instancia se encargara de sus mensajes. Sin embargo si lo hacemos
de esa forma tendremos muchos problemas cuando tengamos varias
instancias, ya que todas ellas estarán accediendo a la vez a los
mismos recursos (la lista de prioridad y los mapas de memoria), con
lo que facilmente podremos provocar errores. De esta otra forma, con
un objeto controlador, dejamos todo el sistema de mensajes en sus
manos con lo que será muy difícil que algo vaya mal. Además así será
mucho más facil incluir nuevos objetos que puedan enviar y recibir
mensajes (veréis esto de froma mucho más evidente segun avance el
tutorial).

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);

//aumentar el identificador del mensaje


obj_ControlMensajes.mensaje_id +=1;

//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.

Recibir y leer mensajes


El objeto obj_ControlMensajes se encargará de chequear la cola de
prioridad de mensajes para ver si hay mensajes nuevos. Si es así,
cogerá el mensaje con mayor prioridad y lo enviará al destino
adecuado. Creamos un script llamado recibirMensaje y ponemos lo
siguiente:
[code]
if !ds_priority_empty(mensajes){//Si hay mensajes en la cola
var Id, Remitente, Destino, Tipo;
//encontrar la id del mensaje con más prioridad y borrarlo de la
cola
Id = ds_priority_delete_max(mensajes);
//obtener las caracter?sticas del mensaje y borrarlas de los mapas
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);
Tipo = ds_map_find_value(mensaje_tipo,Id);
ds_map_delete(mensaje_tipo,Id);
//Notificar al remitente de que tiene un mensaje
Destino.mensaje = Tipo;
//Mostrar el mensaje
show_message("Mensaje recibido:#"+mensaje_texto[Tipo]);
}
[/code]
Es muy importante llevar un control estricto de los mensajes,
eliminándolos cuando los leemos y borrando sus valores de todos los
mapas de memoria para ahorrar memoria y evitar errores.

Observad que en este caso concreto no hemos usado para nada el


valor del Remitente. Sin embargo me ha parecido interesante
incluirlo para que veáis cómo se pueden manejar los datos a través
del mensaje. Además, como el valor de Remitente es la id de la
instancia que envió el mensaje, podéis usarlo para acceder a
cualquier característica de esta instancia. Por ejemplo, podrás
modificar el código para mostrar el mensaje y que incluyera el
nombre de la instancia que lo envía. Por ejemplo, si la Central tiene
una característica que es nombre e indica el nombre de la comisaría,
podríais mostrarlo haciendo:
[code]
show_message("Mensaje recibido de
"+Remitente.nombre+":#"+mensaje_texto[Tipo]);
[/code]
Y así el mensaje que se mostraría en pantalla podría ser algo como:
[quote="Mensaje recibido"]
Mensaje recibido de Comisaría del Norte:
Se esta produciendo un atraco en el banco!!
[/quote]
También podéis usar este valor para actuar sobre las características
de la otra instancia. Si el policía encuentra a un criminal y no tiene
agresividad suficiente para detenerlo puede enviar un mensaje
pidiendo refuerzos. La central recibiría el mensaje y con la id del
Remitente podría aumentar al agresividad de éste:
[code]Remitente.agresividad += 1;[/code]
Lo único que nos queda es ir al [b]Evento Step[/b] del
[b]obj_ControlMensajes[/b] y decirle que realize el chequeo de
mensajes:
[code]
recibirMensaje();
[/code]
Listo!

Bueno, con esto ya hemos terminado el sistema de mensajes! Lo


siguiente que haremos será modificar la IA del policía para que
reaccione a los mensajes que le lleguen y para que él mismo envíe
sus propios mensajes.

Actualización de la IA del policía


Para que tengáis más a mano la tabla de transiciones la vuelvo a
poner aquí, estudiarla atentamente para poder entender el
comportamiento del policía:

Condición\Es BuscarY Entregar IrAlBAr IrAlBan IrALaCe *IrACa


tado Atrapar Criminal YBeber co ntral sa*
Agresividad>=
Entregar
Agresividad X X X X X
Criminal
criminal
Detenciones=
IrAlBarY
Detenciones X X X X X
Beber
requeridas
BuscarY
Detenciones X X X X X
Atrapar
Mensaje IrAlBanc IrAlBanc IrAlBan IrAlBan
X X
"Atraco!" o o co co
Mensaje IrALaCe
X X X X X
"Información" ntral
Agresividad>= BuscarY
X X X X X
7 Atrapar
Cansancio>=
IrACas
Cansancio_ma IrACasa IrACasa IrACasa IrACasa X
a
x
Cansancio=0
BuscarY
& X X X X X
Atrapar
Detenciones>
=Detenciones
requeridas
Atraco IrAlBarY
X X X X X
detenido Beber
Atraco no BuscarY
X X X X X
detenido Atrapar

Como hemos dicho al principio, el policía tendrá ahora 2 nuevos


estados. El primero de ellos será IrAlBancoYDetenerAtraco. En
este estado, el agente irá al banco e intentará detener el atraco con
una probabilidad de éxito del 50% ([b]floor(random(2))[/b]). Si lo
consigue se irá al bar a celebrarlo, si no, volverá a patrullar para
buscar a los culpables:
[code]
switch argument0{
case 0://Entrar
show_message("Voy corriendo!");
break;
case 1://Ejecutar
if (floor(random(2))){
show_message("Alto! Nadie va a robar este banco en mi
turno");
execute_string(estado+"(2)");//ejecutar acciones de salida
estado="IrAlBarYBeber";
}else{
show_message("He llegado demasiado tarde...voy a buscar a
los culpables de esto!");
estado="BuscarYAtraparCriminales";
}
break;
case 2://Salir
show_message("Conseguido! Esto hay que celebrarlo");
break;
case 3://Transición
break;
}
[/code]

El otro estado del policía es IrALaCentralYRecibirInfo. En este


estado el policía acudirá a la central para recibir información sobre los
malhechores. Su agresividad irá aumentando, de forma que tendrá
más éxito al detener criminales. Cuando su agresividad crezca lo
suficiente, volverá a patrullar las calles:
[code]
switch argument0{
case 0://Entrar
show_message("Voy corriendo a la central");
break;
case 1://Ejecutar
if (agresividad>=7){
show_message("Esta informaci?n me va a resultar muy util
para detener criminales");?
estado="BuscarYAtraparCriminales";
}else{
show_message("Necesito mas informacion...");
agresividad+=1;
}
break;
case 2://Salir
break;
case 3://Transición
break;
}
[/code]

Lo último que nos falta es modificar el código del [b]Evento Step[/b]


del policía para que pueda recibir mensajes:
[code]
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{
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

Si el mensaje es de tipo=0 (Atraco en el banco) el policía dejará lo


que está haciendo y se dirigirá al banco. En cierta forma, es como si
el estado IrAlBancoYDetenerAtraco fuera un estado global, ya que
puede interrumpir a todos los demás excepto al estado global
IrACasaYdormir. Por eso, resulta más sencillo implementarlo en el
código general de la FSM (el policía) en el evento step, en lugar de ir
al código de cada estado y añadir la misma condición una y otra vez.
Observad que volvemos a poner [b]mensaje = -1[/b] porque el
mensaje ya se ha recibido y se ha actuado, con lo que ya nos
podemos librar de él.

Sin embargo el otro mensaje (tipo=1, Información sobre un criminal)


sólo tendrá sentido en el estado BuscarYAtraparCriminales. Así
que vamos al código de este estado y lo modificamos:
[code]switch argument0{
case 0://Entrar
show_message("Me faltan "+string(detenciones_max-
detenciones)+" detenciones");
break;
case 1://Ejecutar
if (mensaje=2){
estado = "IrALaCentralYRecibirInfo";
execute_string(estado+"(0)");
}else{
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]

Con esto hemos terminado con el sistema de recepción de mensajes


pero también queremos que el policía los envíe. El policía enviará un
mensaje de tipo 2 (informar de un delito) cuando se encuentre en el
estado EntregarCriminal y vea un delito. Así que vamos al código
del estado EntregarCriminal:
[code]
switch argument0{
case 0://Entrar
show_message("Voy a la comisaria a entregar al criminal");
if (!floor(random(3))){
enviarMensaje(obj_central,2);
}
break;
case 1://Ejecutar
if (detenciones >= detenciones_max){
show_message("Ya he cumplido con mi labor, creo que ire a
celebrarlo al bar.");
estado = "IrAlBarYBeber";
}else{
show_message("Ya llevo "+string(detenciones)+"
detenciones");
estado = "BuscarYAtraparCriminales";
}
execute_string(estado+"(0)");
break;
case 2://Salir
break;
case 3://Transición
break;
}
[/code]
El policía enviará un mensaje con una probabilidad del 33%.
Observad que en este caso sólo existe una instancia del objeto
obj_central, con lo que podemos enviar el mensaje usando el
nombre del objeto (obj_central) como destino del mensaje
(enviarMensaje(obj_central,2);). Cuando haya más de una
instancia del mismo objeto tendremos que usar la id para mandar el
mensaje a la instancia correcta. Si usáramos el nombre del objeto, el
mensaje se enviaría a todas las instancias del objeto (lo que
tambié puede ser algo útil, por ejemplo para avisar a todos los
policías a la vez para que vayan al banco a detener el atraco).

El policía ahora es capaz de recibir mensajes, actuar según ellos y


enviar los suyos propios. Como habéis visto, para poder usar el
sistema de mensajes casi no hemos hecho cambios. Estos es gracias
a que hemos puesto toda la funcionalidad de los mensajes en el
objeto [b]obj_ControlMensajes[/b]. Así podremos poner cualquier
otro personaje en el juego y darle facilmente la capacidad de enviar y
recibir mensajes.

Y como prueba de esto último, vamos a añadir por fín la Central de


policía.

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.

Como siempre, pimero definimos sus estados:


• Vigilar: en este estado la central mandará mensajes al policía de
forma aleatoria para que vaya al banco o acuda a recibir información.
• EnviarUnidad: la central enviará una unidad de policía a
investigar los avisos del policía.
Para controlar estos estados, necesitaremos unas características:
• Tiempo de envío: un contador de tiempo para enviar mensajes.
Así, la tabla de transiciones de la central será:
Condición\Estado actual Vigilar EnviarUnidad
Mensaje "Mandar una unidad" EnviarUnidad X

Como veis, es muy sencilla. Al recibir un mensaje enviará una


unidad. Después, volverá al estado Vigilar.

Bien, creamos un objeto llamado obj_central y su Create Event


definimos las características de la FSM como hicimos con el policía:
[code]
/*CARACTERISTICAS DE LA FSM*/?
estado = "Vigilar";?
mensaje = -1;
[/code]
En este caso no necesitamos definir ningún estado global.

A continuación definimos las características de la central:


[code]
tiempo_envio = 15+floor(random(15));
[/code]

En el Evento Step definimos el funcionamiento de la FSM:


[code]
execute_string(estado+"(1)");
[/code]

Y ahora definimos los estados. Creamos el script EnviarUnidad:


[code]
switch argument0{
case 0://Entrar
break;
case 1://Ejecutar
show_message("Enviamos una unidad a investigar");
estado = "Vigilar";
break;
case 2://Salir
break;
case 3://Transición
break;
}
[/code]

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]

Listo! Ya hemos terminado con nuestro sistema de FSM con


mensajería! :D

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.

Resumiendo, para poder crear vuestra propia IA es indispensable


saber manejar los eventos. GM nos proporciona muchas ventajas por
su sistema de eventos, pero con este método podráis crear vuestros
propios eventos específicos para cada juego consiguiendo unas IA
más complejas pero más sencillas de programar que con el sistema
de GM.

correojon, 25 de Mayo de 2006


OCT 22 2007 IA: FSM III
by Fenris78 | 666 Views | 0 Comments | Rating: (0 rates)
Seguimos con la serie de artìculos sobre Inteligencia Artificial. En esta
ocasión, veremos cómo implementar un máquina de estados finita en
GML. Para este ejemplo haremos un ejemplo sencillo, sin gráficos ni
nada, en el que la salida serán mensajes de texto.
Descripción general
En este ejemplo crearemos un policía que patrulla las calles buscando
criminales. Si encuentra un criminal intentará atraparlo. Si lo
consigue, lo llevará a comisaría y lo meterá a la cárcel. Si no lo
consigue, el policía se volverá más agresivo. Cuando consiga 5
detenciones el policía habrá cubierto su cupo de detenciones diarias y
podrá irse al bar a celebrar el cumplimiento de su trabajo. Además de
todo esto, el policía se irá cansando con lo que llegado el momento,
dejará lo que está haciendo y volverá a su casa a dormir.

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.

Para poder pasar de un estado a otro deben cumplirse unas


condiciones. Para ello, necesitaremos conocer las características del
policía:
• Cansancio: nivel de cansancio actual del policía.
• Cansancio máximo: nivel máximo de cansancio que puede
soportar el policía.
• Detenciones: número de detenciones que ha realizado.
• Detenciones requeridas: número de detenciones que necesita
realizar para terminar su jornada laboral.
• Agresividad: agresividad del policía. Necesaria para atrapar
criminales.
• Agresividad criminal: agresividad del criminal. Si es mayor que
la del policía se escapará.
Una vez que hemos definido todo esto, ya podemos escribir la tabla
de transiciones del policía:

Condición\Estad BuscarYAtraparCri IrAlBArYB


EntregarCriminal
o actual minales eber
Agresividad>=Agre
EntregarCriminal X X
sividad criminal
Detenciones=Dete
X IrAlBarYBeber X
nciones requeridas
BuscarYAtraparCri
Detenciones<> X X
minales

Como veis, en la tabla no añadimos la condición para entrar al estado


global IrACasaYdormir. Podría ponerse, pero sería algo tonto ya
que sería igual para todos los estados: si Cansancio>=Cansancio
máximo->IrACasaYdormir.
Bueno, una vez que tenemos todo esto ya podemos empezar a
programar.

Características del policía


Creamos el objeto obj_policia y en su Create Event inicializamos
las caracter?sticas de la máquina de estados finitos:
[code]
/*CARACTERISTICAS DE LA FSM*/
estado = "BuscarYAtraparCriminales";
estado_global = "IrACasaYDormir";
[/code]
Al comienzo del juego el policía se encontrará durmiendo en su casa.
Cuando se ejecute el estado global interrumpirá cualquier estado que
estuviéramos llevando a cabo. Una vez que finalize este estado,
volveremos al estado anterior. Por último, decimos que el estado
global en este momento es IrACasaYDormir. De esta forma, este
estado interrumpe al estado actual que definimos en la primera línea
(estado = "BuscarYAtraparCriminales") y así conseguiremos que
inicialmente el policía se encuentre en casa durmiendo.

A continuación inicializamos las características del policía:


[code]
/*CARACTERISTICAS DEL POLICIA*/
cansancio = 0;
cansancio_max = 10;
detenciones = 0;
detenciones_max = 5;
agresividad = floor(random(10));
agresividad_criminal = 0;
[/code]
Inicialmente el policía no está cansado (cansancio = 0). Además, no
ha realizado ninguna detención (detenciones = 0). Su agresividad
es aleatoria (agresividad = floor(random(10))). Así cada vez que
ejecutemos el juego el policía se comportará de manera diferente.

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]

Ahora vamos a programar el estado EntregarCriminal. En este


estado el policía simplemente llevará al criminal a comisaría, verá
cuántas detenciones ha hecho y entonces decidirá si captura más
criminales o se va al bar a tomarse unas copas:
[code]
switch argument0{
case 0://Entrar
show_message("Voy a la comisaria a entregar al criminal");
break;
case 1://Ejecutar
if (detenciones >= detenciones_max){
show_message("Ya he cumplido con mi labor, creo que ire a
celebrarlo al bar.");
estado = "IrAlBarYBeber";
}else{
show_message("Ya llevo "+string(detenciones)+"
detenciones");
estado = "BuscarYAtraparCriminales";
}
execute_string(estado+"(0)");
break;
case 2://Salir
break;
case 3://Transici?n
break;
}
[/code]

Bien, el siguiente estado será IrAlBarYBeber. En este estado el


policía simplemente se tomará unas copas hasta que está cansado y
se quiera ir a casa.
[code]
switch argument0{
case 0://Entrar
show_message("Jeje, ya estoy en el bar, se acabo el trabajo
por hoy!");
detenciones = 0;
break;
case 1://Ejecutar
show_message("Hmmm...este whisky esta delicioso");
break;
case 2://Salir
break;
case 3://Transición
break;
}
[/code]
Como veis, este estado es muy sencillo y no necesita comentarios :)

Ahora ya sólo nos falta el estado global "IrACasaYDormir":


[code]
switch argument0{
case 0://Entrar
show_message("Uff, que cansado estoy! Voy a dormir un
rato");
break;
case 1://Ejecutar
if (cansancio > 0){
show_message("ZZzzzzzz....");
cansancio -= 1;//recuperarse
}else{
show_message("Que bien me ha venido este descanso");
execute_string(estado_global+"(2)");//ejecutar acciones de
salida
if (detenciones = 0){
estado = "BuscarYAtraparCriminales";
}
execute_string(estado+"(0)");//volver al estado anterior
}
break;
case 2://Salir
if (detenciones >= detenciones_max){
show_message("Listo para un nuevo dia de trabajo!");
detenciones = 0;
}else{
show_message("Ya solo me quedan
"+string(detenciones_max-detenciones));
}
estado_global = "";
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]

Listo! Ya hemos creado nuestra primera FSM en GML! :D

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!

Os animo a que pongáis aquí vuestros experimentos!

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:

Una lista guarda una colección de valores en un orden determinado.


Puedes añadir valores a la lista en 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
compilado son mucho más rápidas que los arrays.

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){...}

//es lo mismo que este otro


if (ds_list_size(idLista) = 0){...}

Para inicializar una lista, tenemos la función:


ds_list_clear(idLista) Limpia la lista, borrando todos los valores que
contiene pero no la destruye.

Por último, cuando ya no necesitemos la lista tenemos que eliminarla,


ya que si la dejamos en la memoria nos estaría ocupando espacio
inútilmente. Para ellos usamos la función:
ds_list_destroy(idLista) Destruye la lista, liberando la memoria usada.

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).

Con estas funciones podemos hacer muchas cosas de manera mucho


más sencilla y efectiva que usando arrays. Por ejemplo, para ordenar
los valores de la lista de mayor a menor basta con poner esto:
ds_list_sort(idLista, false);

Recorriendo una lista


Para recorrer una lista, lo hacemos igual que como lo haríamos con
un array:
var i;
for (i=0; i[ds_list_size(idLista); i+=1){
miValor = ds_list_find_value(idLista, i);
draw_text(x, y+8*i, miValor);//Imprimimos los valores uno debajo
de otro
}

Pero esta vez tenemos una gran ventaja y es que no necesitamos


conocer el tamaño de la lista. Con un array tendríamos que saber el
tamaño exacto que tiene para poder coger todos los valores, pero con
una lista la función ds_list_find_size(idLista) ya nos da esa
información.

Ejemplo: Programa sencillo de dibujo


Para entender bien el funcionamiento de las listas, vamos a ver un
ejemplo sencillo. Vamos a crear un panel en el que usuario podría
dibujar formas sencillas. Cada vez que haga click con el botón
izquierdo se añadirá un punto y cada vez que haga click con el botón
derecho se borrará el último punto añadido.
Evento create
//Creamos las listas que guardarán las coordenadas de los puntos
listaPuntosX = ds_list_create();
listaPuntosY = ds_list_create();

Al hacer click con el botón izquierdo añadimos un punto:


Evento Mouse Global Left Pressed
//Añadimos el punto al final de la lista
ds_list_add(listaPuntosX, mouse_x);
ds_list_add(listaPuntosY, mouse_y);

Y al hacer click con el botón derecho borramos el último punto


añadido
Evento Mouse Global Right Pressed
//Quitamos el último punto de la lista
ds_list_delete(listaPuntosX, ds_list_size(listaPuntosX)-1);
ds_list_delete(listaPuntosY, ds_list_size(listaPuntosY)-1);

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.

A parte de esto, otra clara ventaja de las listas es la de poder ordenar


todos los valores con una simple llamada a la función
ds_list_sort(idLista, ascend). Con arrays tendríamos que programar
un algoritmo de ordenación que, además, sería mucho menos
eficiente. También podemos saber en qué lugar de la lista se
encuentra un valor con la función ds_list_find_index(idLista, pos),
algo que con arrays sería tremendamente complicado de hacer.
También podemos insertar valores en cualquier posición de la lista,
desplazando los demás con ds_list_insert(idLista, pos, val). De
nuevo, esto sería bastante más trabajoso de hacer con arrays.

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

Extraída del Manual Online de CGM:


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ía el primero en ser atendido y los
demás tendrán que esperar su turno en orden. Los últimos en llegar
se situarán al final de la cola y tendrán que esperar a que sean
atendidos todos los que tengan delante.

Las primeras 5 funciones de las colas para crearlas, eliminarlas,


vaciarlas o ver su tamaño son iguales que las que vimos en las listas.

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.

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.

Las funciones de las colas nos permiten hacer muchísimas cosas de


manera muy sencilla, como veremos a continuación.

Recorriendo una cola


Para recorrer una cola lo podemos hacer de manera muy sencilla
mientras vamos eliminando los valores:
var d;
d = 0;
while (!ds_queue_empty(idCola)){
draw_text(x, y+8*d, ds_queue_dequeue(idCola));//Imprimimos
los valores uno debajo de otro
d += 1;
}

Observad que la variable temporal d sólo la usamos para poder


dibujar los valores de la cola uno debajo de otro. Para cualquier otra
cosa no la necesitaríamos! Es decir, que ni siquiera necesitamos un
bucle for para recorrer una cola! También hay que darse cuenta de
que al final del bucle la cola estaría vacía, ya que al usar la función
ds_queue_dequeue(idCola) vamos borrando los valores de la cola.

¿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.

Ejemplo: Elegir a un enemigo sin repetir


En este ejemplo vamos a hacer que el jugador, pulsando una tecla,
apunte automáticamente a un enemigo. Esto es muy sencillo de
hacer pero, ¿y si tenemos varios enemigos a la vez en pantalla? ¿Y si
además queremos que al volver a pulsar la tecla, el jugador apunte a
otro enemigo distinto? Vamos a ver cómo se hace esto usando colas:

Creamos un objeto y lo llamamos objEnemigo:


Evento Draw:
-Dibujar un círculo rojo
draw_set_color(c_red);
draw_circle(x, y, 16, false);

Muy bien, ahora creamos otro objeto y lo llamamos objJugador:


Evento Create:
-Crear la cola
-Inicializar variables
enemigos = ds_queue_create();
objetivo = 0;

Evento Key Press Control:


-Si la cola está vacía metemos los enemigos en la cola
-Elegimos un enemigo y lo borramos de la cola
if (ds_queue_empty(enemigos)){
with (objEnemigo){
ds_queue_enqueue(other.enemigos, id);
}
}
objetivo = ds_queue_dequeue(enemigos);

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();

Listo! Crear un room, poner uno o varios objetos jugadores, varios


enemigos y probarlo. Cada vez que pulsáis Control los jugadores irán
apuntando a un enemigo diferente sin repetir!

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.

Las colas de prioridad son otra estructura de memoria, muy similares


a las colas pero con algunas diferencias. Es recomendable aunque no
necesario haber leído antes el artículo sobre colas.

Definición

Extraído del Manual Online de CGM:


En una cola de prioridad a cada valor se le asigna una prioridad. Así
puedes buscar los valores con mayor o menor prioridad y controlar
ciertas cosas según su prioridad.

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.

Las primeras 5 funciones de las colas de prioridad para crearlas,


eliminarlas, vaciarlas o ver su tamaño son iguales que las que vimos
en las listas y en las colas:
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.

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.

Como véis, ahora podemos acceder a los valors según su prioridad.


En el caso de que varios valores tengan la misma prioirdad la cola
funionará como una cola normal, devolviendo el primero que
introdujimos en la cola.

Recorriendo una cola de prioridad


Para recorrer una cola de prioridad lo más sencillo es hacerlo por
orden de prioridad. Así, podemos elegir ir sacando primero los valores
con menos prioridad:
var d;
d = 0;
while (!ds_priority_empty(idCola)){
draw_text(x, y+8*d,
ds_priority_delete_min(idCola));//Imprimimos los valores uno debajo
de otro
d += 1;
}

o sacando primero los valores con mayor prioridad:


var d;
d = 0;
while (!ds_priority_empty(idCola)){
draw_text(x, y+8*d,
ds_priority_delete_max(idCola));//Imprimimos los valores uno
debajo de otro
d += 1;
}
Al igual que ocurría con las colas normales la variable temporal d sólo
la usamos para poder dibujar los valores de la cola uno debajo de
otro. Para cualquier otra cosa no la necesitaríamos! Es decir, que ni
siquiera necesitamos un bucle for para recorrer una cola! También
hay que darse cuenta de que al final del bucle la cola estará vacía, ya
que al obtener los valores los vamos borrando de la cola.

Ejemplo: Elegir a un enemigo según la distancia


Para ver la utilidad de las colas de prioridad vamos a modificar el
ejemplo que hicimos para las colas: en esta ocasión haremos que el
personaje vaya apuntando en orden a los enemigos según estén más
cerca o más lejos. Es decir, primero apuntaremos al enemigo más
cercano, luego al segundo más cercano, luego al tercero...y así hasta
que hayamos apuntado a todos, momento en el que volveremos a
apuntar al primero. Para ello sólo necesitaremos editar un poco el
código del objPersonaje:

Evento Create:
-Crear la cola de prioridad
-Inicializar variables
enemigos = ds_priority_create();
objetivo = 0;

Evento Key Press Control:


-Si la cola está vacía metemos los enemigos en la cola, asignándoles
como prioridad su distancia a objPersonaje
-Elegimos un enemigo por oden de cercanía y lo borramos de la cola
if (ds_priority_empty(enemigos)){
with (objEnemigo){
ds_priority_add(other.enemigos, id, distance_to_object(other));
}
}

objetivo = ds_priority_delete_min(enemigos);

Con esta pequeña modificación hemos conseguido que ahora el


personaje elija a los enemigos según su prioridad, que será igual a la
la distancia del enemigo al personaje. Así, eligiendo a los enemigos
en orden de prioridad mínima (objetivo =
ds_priority_delete_min(enemigos)) vamos elegiendo primero a los
más cercanos.
Las colas de prioridad tienen un montón de usos. Por ejemplo, si
añadimos varios valores a la cola y en el momento de añadirlos les
damos una prioridad aleatoria sería como si los estuviéramos
desordenando, ya que no sabemos qué prioridad tendrá cada valor.
Así, al acceder a los valores por prioridad podemos hacer una especie
de sorteo:
Añadiendo un valor con prioridad aleatoria:
ds_priority_add(idCola, valor, random(10));

Eligiendo un valor por sorteo:


resultado = ds_priority_find_min(idCola);

Así podemos el resolver el típico problema de "tengo varios objetos y


quiero coger algunos al azar, pero una vez que elija uno éste no debe
volver a salir".

Mapas de Memorias.

Los mapas de memoria son una estructura que nos puede facilitar
muchísimo la escritura de códigos complicados.

Definición

Extraído del Manual Online de CGM:

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.

Como siempre, las primeras 5 funciones de los mapas sirven para


crearlos, eliminarlos, vaciarlos o ver su tamaño:
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.

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.

Con los mapas de memoria el sistema cambia mucho respecto a lo


que habíamos visto en las listas y colas. Los mapas se usan para
guardar muchos valores y tenerlos de forma ordenada de manera que
sea fácil acceder exactamente al valor que queremos.

Ejemplo: Creando varios tipos de disparos


Vamos a crear un ejemplo en el que el personaje pueda disparar 3
tipos de disparos diferentes. Pulsando + y - cambiaremos el tipo de
disparo y con Espacio dispararemos.

Creamos el objeto objJugador


Evento Create:
-Crear la variable que guarda el tipo de disparo usado
-Crear el mapa de memoria con los tipos
disparo = 0;
disparos = ds_map_create();
ds_map_add(disparos, 0, "normal");
ds_map_add(disparos, 1, "doble");
ds_map_add(disparos, 2, "triple");

Evento Press Keypad +:


-Usar el siguiente tipo de disparo
disparo = ds_map_find_next(disparos, disparo);

Evento Press Keypad -:


-Usar el tipo de disparo anterior
disparo = ds_map_find_previous(disparos, disparo);
Evento Press Space:
-Crear las balas según el tipo de disparo
tipo = ds_map_find_value(disparos, disparo);
with (instance_create(x, y, objBala)){//crear la bala
?tipo = other.tipo;//definir el tipo de la bala
?event_perform(ev_other, ev_user0);//Inicializar características de la
bala
}

Evento Draw:
-Dibujar al jugador
draw_set_color(c_blue);
draw_circle(x, y, 16, false);

Ahora creamos el objeto objBala:


Evento Create:
-Definir los mapas con las características de cada tipo de disparo
/*COLORES*/
colores = ds_map_create();
ds_map_add(colores, "normal", c_black);
ds_map_add(colores, "doble", c_red);
ds_map_add(colores, "triple", c_white);

/*VELOCIDADES*/
velocidades = ds_map_create();
ds_map_add(velocidades, "normal", 4);
ds_map_add(velocidades, "doble", 8);
ds_map_add(velocidades, "triple", 16);

Evento User Defined 0:


-Inicializar las características de la bala
speed = ds_map_find_value(velocidades, tipo);

Evento Draw:
-Dibujar la bala según su tipo
draw_set_color(ds_map_find_value(colores, tipo));
draw_circle(x, y, 4, false);

Las ventajas de este método son muy importantes:


-Podemos identificar facilmente en el código la parte de cada bala, ya
que las variables se llaman "normal", "doble", "triple" o como
queramos.
-El sistema es facilmente ampliable: sólo hay que definir las
características de los nuevos tipos de balas que queramos añadir el
indicarlo en el evento Create del objeto jugador. El resto (aplicar
velocidad a la bala, color, cmabiar de arma...) se hace
automáticamente!
Esto mismo se puede usar para crear nuevos tipos de personajes con
características diferentes, niveles, objetos...

DLL.

vamos a hablar de DLLs.

DLL es el acronimo de "Dinamic Link library" (Biblioteca de enlace


dinamico), para ser breves y claros, podemos definirla como un
peque?o programa hecho en un lenguaje compatible (Delphi, C/C++,
Pascal... ) que contiene funciones que pueden ser llamadas desde GM
y nos sirven para cubrir aquellas necesidades que no pueden ser
satisfechas con GM o que pueden ser optimizadas. (Como reproducir
diferentes archivos de audio, ver animaciones flash, embeber paginas
html en tus juegos... etc)

Como cada DLL contendra diferentes funciones, su uso varia, ya que


el numero de argumentos que recoja cada funcion y el tipo de dato
devuelto sera diferente. En cualquier caso, el uso especifico de cada
DLL en particular siempre debe venir documentado junto con el
archivo.

¿Entonces nos vamos a limitar a explicar que es una DLL y nada


mas?

No, vamos a dar un pasito mas. Vamos a explicar el procedimiento


basico para utilizar dos DLLs ficticias, para que os vayais
familiarizando con la definicion y llamada de DLLs en GML.

Primero es necesario definir la DLL, para ello GM nos proporciona la


siguiente funcion:

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.

Despues de definir la DLL, ya podemos hacer uso de sus funciones,


invocandolas con la siguiente funcion:

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.

Nota: Como habreis observado, GM solo envia y recibe argumentos


del tipo numero real o cadenas de texto. Tened esto en cuenta a la
hora de desarrollar vuestras DLLs.

Ahora voy a exponer dos ejemplos de definicion y llamada de dos


DLLs, una se llama "Mensaje.DLL" y mostraria una ventana con el
titulo y texto especificados en los argumentos de entrada tipo
"string". La segunda DLL trabaja con datos numericos "Divide.DLL"
toma dos numeros reales como argumentos y los divide,
devolviendonos el resultado. Vemos ambos codigos de llamada al
completo:

//Aquí tienes el código de llamada para la DLL “Mensaje” :


{
global.Mensaje=
external_define(Mensaje.DLL','_Mensaje',dll_stdcall,ty_string,
2,ty_string,ty_string);
external_call(global.Mensaje,"Titulo","Texto")
}

//c?digo de llamada para la DLL “divide” :


{
global.divide=
external_define('divide.DLL','divide',dll_stdcall,ty_real,2,ty_re
al,ty_real);
result = external_call(global.divide,12,12)
}

Bueno, con este pequeño articulo "¿Como se hace?" espero que


hayais podido ampliar un poco mas vuestros conocimientos sobre el
uso de DLLs con GM. Si creeis que se ha quedado algo importante en
el tintero, no dudeis en responder y compartirlo con nosotros.
Estamos aqui para aprender juntos.

Taller de Programación con GM

PLANIFICACI?N DEL JUEGO Y CREACI?N DE SPRITES


En este taller aprenderemos a realizar nuestro propio videojuego:
nuestra visi?n del famoso y archiconocido comecocos (PACMAN).
Para ello nos valdremos de dos programas principalmente,
GraphicsGale para la creaci?n de sprites (los personajes del
videojuego) y Gamemaker, la aplicaci?n con la que programaremos y
montaremos nuestro videojuego.

Programar un videojuego no es una tarea sencilla, no se trata de


ponernos a ello y sale s?lo. Es necesaria toda una planificaci?n
detallada, visualizar el videojuego en nuestra mente y cada uno de
los detalles que intervienen en ?l antes de empezar a programarlo y
plasmarlo sobre papel, hacer un gui?n en el que no debemos dejar
nada al azar: Historia, personajes, escenarios, N?mero de fases,
elementos que intervienen, puntuaci?n, vidas… Y lo m?s importante,
las reglas del juego.

Pensemos en nuestro juego.

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.

Reglas del juego:

- Cada vez que pacman toca un coco se lo come sumando 10


puntos a su marcador de puntuaci?n.

- Si pacman toca un fantasma o un fantasma toca a pacman si


que este est? s?per vitaminizado pacman muere y se descuenta
una vida del contador de vidas.

- Si pacman est? s?per vitaminizado y toca a un fantasma o un


fantasma lo toca a ?l el fantasma morir? y pacman sumar? 50
puntos a su marcador de puntuaci?n.

- Cuando pacman muere vuelve a aparecer en el punto de inicio


de pacman.
- Cuando un fantasma muere vuelve a aparecer en el ?rea de
salida de fantasmas.

- Si pacman muere tres veces acaba la partida.

- Si pacman logra comerse todos los cocos de un escenario pasa


al siguiente escenario.

- Si pacman come una fruta especial suma 10 puntos a su


marcador de puntuaci?n.

Escenario y elementos que intervienen:

Esta historia se desarrolla en un escenario y con unos elementos que


vamos a definir.

- Un escenario de estructura laber?ntica cuyos pasillos est?n


sembrados de cocos.

Definimos de cuantos escenarios constar? nuestro juego: 4

- Un punto de inicio de pacman donde aparecer? el personaje


que controla el jugador al inicio de cada pantalla y donde
reaparecer? si este muere.

Definimos donde estar? el punto de inicio: En el centro, en la parte


inferior de la pantalla.

- Un ?rea de salida de fantasmas que ser? el punto de inicio de


los fantasmas. Desde all? empiezan a perseguir al comecocos al
inicio de cada pantalla y all? aparecer?n cuando mueren.
Definimos donde estar? el ?rea de salida de los fantasmas: En la
parte superior de la pantalla, en el centro.

Definimos el n?mero de fantasmas: 4 (uno de cada color: verde,


azul, rojo, lila).

Definimos cuantos supercocos habr? en cada escenario y donde se


encontrar?n: 4, en las esquinas.

Todos estos elementos que hemos visto se encuentran en el ?rea de


juego, que es donde se desarrolla la acci?n.

Hay otra ?rea, el ?rea de marcadores, que se encontrar? en la parte


superior de la pantalla y donde colocaremos los siguientes elementos:

- Marcador de vidas restantes. Nos indica cuantas vidas nos


quedan. Empezamos la partida con 3 vidas.
- Marcador de puntuaci?n. Nos indica en todo momento nuestra
puntuaci?n.

En la creaci?n de un videojuego intervienen tres apartados bien


diferenciados que tendremos que tener en cuenta a la hora de
planificar nuestro videojuego: El apartado gr?fico, el apartado sonoro
y el apartado de programaci?n.

Veamos que necesitaremos para nuestro juego en cada apartado:

APARTADO GR?FICO

- Unos personajes: nuestro amigo el comecocos y los fantasmas


que lo persiguen.

- 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.

- Unas pantallas donde se encuentran el ?rea de juego y el ?rea


de marcadores.

- Un marcador de puntuaci?n que nos indica cuantos puntos


llevamos en la partida.

- Un marcador de vidas que nos indica las vidas restantes.

APARTADO SONORO

- Un tema musical para cada escenario.

- Efectos sonoros para las siguientes acciones: comer coco,


comer fantasma, morir, comer supercoco, acabar partida, pasar
de pantalla.

Para el apartado sonoro no nos complicaremos la vida. Bajaremos


tanto los temas musicales como los efectos sonoros de Internet.

APARTADO DE PROGRAMACI?N

Esta es la parte m?s complicada.

Necesitaremos programar unas rutinas que hagan lo siguiente:


IA de los personajes
- Los fantasmas persigan al comecocos.

- Si el comecocos esta s?per vitaminizado los fantasmas huyen


de ?l.

Rutinas de detecci?n de colisi?n de sprites

- Si el fantasma intercepta al comecocos y este no esta s?per


vitaminizado el comecocos muere.

- Si el comecocos intercepta al fantasma sin estar s?per


vitaminizado el comecocos muere.

- Si el comecocos est? s?per vitaminizado y intercepta al


fantasma este ?ltimo muere.

- Si un fantasma topa con un muro del escenario no se queda


bloqueado y sigue su camino persiguiendo al comecocos.

- Si el comecocos intercepta un coco se lo come y sube 10


puntos el marcador de puntuaci?n.

- Si el comecocos intercepta un supercoco se lo come y sube 50


puntos el marcador de puntuaci?n a la vez que puede comer
fantasmas y estos huyen de ?l.

Otros que ya iremos viendo.

CREACI?N DE SPRITES CON GRAPHICSGALE

Con el programa GraphicsGale procederemos a crear los siguientes


elementos gr?ficos del juego:

- El Comecocos
- Los Fantasmas
- Los cocos
- Los supercocos
- La fruta

Todos los elementos los crearemos en un lienzo con una resoluci?n


de 32 x 32 p?xeles a 8 bits (256 colores) y los guardaremos en
formato tga.

La creaci?n de los personajes en este caso es sencilla.


Hemos de pensar que en el caso del comecocos y el fantasma
debemos de crear una imagen diferente para cada pose:
Cuando suben por un pasillo, cuando baja, cuando van hacia la
derecha, cuando van hacia la izquierda.
??Manos a la obra!!!

INTRODUCCI?N A GAMEMAKER. SPRITES, OBJETOS Y ROOMS.

Game Maker es un programa con un intuitivo y f?cil interfaz que nos


permite crear nuestros propios juegos r?pidamente. Podemos
importar y crear im?genes, los sprites (im?genes animadas) y los
sonidos y utilizarlos. Tambi?n podemos definir f?cilmente los objetos
del juego e indicar su comportamiento, definir pantallas atractivas
con fondos en movimiento. Mediante un sencillo lenguaje de
programaci?n tendremos control completo sobre lo que va ocurriendo
en nuestro juego.
Esta es la pantalla que nos encontramos al ejecutar Game Maker.
Vamos a explicar los diferentes elementos del interfaz.
A la izquierda nos encontramos las carpetas siguientes: Sprites,

Sounds, Backgrounds, Paths, Scripts, Fonts, Time Lines,


Objects,Rooms.

Nos centraremos de momento en las siguientes:

Sprites: Las im?genes (animadas) que se utilizan para representar


los objetos en nuestro juego.

Objetos: Todas las cosas que se ven en el juego (a excepci?n del


fondo) son objetos. Los personajes, los monstruos, las bolas, las
paredes, etc. son todos los objetos. Tambi?n puede haber objetos
que no se ven pero que controlan ciertos aspectos del juego.

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.

Rooms: Las pantallas en las que se desarrolla el juego.

Sounds: Los sonidos que se utilizan, como la m?sica de fondo o los


efectos sonoros.

Backgrounds: Las im?genes usadas como fondo de las pantallas


(Rooms).

En el men? de archivo (file) puedes encontrar las opciones de cargar


y guardar archivos, m?s algunas especiales:
Nuevo. Elije esta opci?n para comenzar a crear un nuevo juego.

Abrir. Abre un archivo de un juego. Los archivos de los juegos tienen


gm6 como extensi?n.

Archivos Recientes. Utiliza este submen? para abrir de nuevo


archivos del juego que se abrieron recientemente.

Guardar. Guarda el dise?o del juego bajo su nombre actual. Si no se


especific? ning?n nombre antes, te pide un nuevo nombre.

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.

Modo Avanzado. Cambia entre el modo simple y avanzado. En modo


avanzado los comandos y los recursos adicionales est?n disponibles.
El modo avanzado s?lo se encuentra disponible en la versi?n de pago
de GameMaker.

Salir. Para salir de GameMaker.

El men? de edici?n (Edit) contiene un n?mero de opciones que se


relacionan con el recurso actualmente seleccionado (objeto, sprite,

sonido, etc.). Dependiendo del tipo de recurso algunos de las


opciones pueden no estar disponibles.

Insertar. Inserta un nuevo elemento del tipo que este actualmente


seleccionado. Se abrir? una ventana en la cual podremos cambiar las
caracter?sticas de este elemento.

Duplicar. Hace una copia del elemento actualmente seleccionado y la


agrega.

Borrar. Suprime el elemento actualmente seleccionado (o el grupo de


elementos).

Renombrar. Para dar al elemento un nuevo nombre.

Propiedades. Utiliza esta opci?n para cambiar las propiedades de


cualquier elemento.

El men? de agregar (Add) nos permite agregar nuevos elementos de


cada uno de los diferentes tipos: Sprites, sonidos, fondos…
El men? ejecutar (Run) se utiliza para hacer funcionar el juego. Hay
dos maneras de hacer funcionar un juego. Funcionamiento normal o
en modo debug para revisar el funcionamiento del juego paso a paso
y corregir errores.

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)

tienen un nombre que tendremos que dar de forma descriptiva. Para


cargar el gr?fico de un sprite, haz click en el bot?n “Load Sprite”.
Una vez cargada la imagen se demuestra a la derecha. La opci?n
“transparente” indica si el fondo rectangular de la imagen del sprite
se debe considerar como transparente. La mayor?a de los sprites son
transparentes. Con el bot?n Editar Sprite puedes corregir el sprite, o
crear uno totalmente nuevo.

DEFINIENDO LOS OBJETOS DEL 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).

CREANDO LAS PANTALLAS (ROOMS)

Elegimos “a?adir room” del men? de a?adir (Add).


Indicaremos el tama?o de las celdas de la rejilla.
Tambi?n podemos indicar si queremos que la rejilla se muestre o si
no, si queremos que el fondo se muestre o que quede oculto, etc. A
veces es ?til ocultar temporalmente ciertos elementos del cuarto para
trabajar mejor.
Deberemos fijar la medida de la pantalla. Esto se hace desde la
pesta?a “Settings”.
Desde la pesta?a “Objects” a?adiremos todos los objetos a la
pantalla.
Desde la pesta?a “Backgrounds” podremos a?adir las im?genes de
fondo para la pantalla.
A la derecha de la ventana vemos la pantalla. Al principio esta vac?a
y
s?lo vemos un fondo gris. Para agregar objetos al cuarto, primero
selecciona la pesta?a de los objetos, despu?s el objeto que desees
agregar. La imagen del objeto aparecer? a la izquierda. Ahora
selecciona con el bot?n izquierdo del rat?n el sitio de la pantalla (a la
derecha) donde quieres que aparezca el objeto. Veras como el objeto
aparece alineado a la rejilla. Puedes borrar objetos de la pantalla

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.

PROPIEDADES DE LOS OBJETOS: EVENTOS Y ACCIONES

A?ADIENDO MUSICA Y SONIDO


SCORE E INDICADOR DE VIDAS

EVENTOS

Game Maker emplea lo que se conoce como programaci?n


orientada a eventos. Esto es, en todo tipo de situaciones las
instancias de los objetos reciben eventos (como mensajes que indican
que algo ha sucedido). Entonces los objetos pueden reaccionar a
estos mensajes ejecutando ciertas acciones. Para cada objeto debes
indicar a qu? eventos responder? y qu? acciones debe realizar. Puede
parecer complicado pero en realidad es bastante sencillo. Primero que
nada, para la mayor?a de los eventos los objetos no tienen que hacer
nada. Para los eventos donde algo suceda puedes usar muy simples
comandos de arrastrar y soltar para indicar las acciones.
En medio de la ventana de propiedades de objeto hay una lista de los
eventos a los cuales el objeto puede reaccionar. Al principio est?
vac?a. Puedes agregar eventos presionando el bot?n Add Event.
Aparecer? un peque?o men? con todos los diferentes tipos de
eventos. Aqu? debes seleccionar el evento que deseas agregar. En
ocasiones se mostrar? un nuevo men? con opciones extra. Por
ejemplo, para el evento del teclado debes seleccionar la tecla. M?s
abajo encontrar?s una completa lista con descripciones de los
eventos. Seleccionaremos un evento de la lista. Este ser? el evento
que modificaremos.
Puedes cambiar el evento seleccionado haciendo clic sobre ?l. A la
derecha est?n todas las acciones representadas por peque?os iconos.
Se encuentran agrupadas en varias p?ginas / fichas de opciones.
Entre los eventos y las acciones se encuentra la lista. Esta lista
contiene las acciones para el evento actual. Para agregar acciones a
la lista, arr?stralas desde la derecha a la lista. Ser?n colocadas una
bajo la otra, con una breve descripci?n. Para cada acci?n se te
pedir?n algunos par?metros (Las acciones). Despu?s de agregar
algunas acciones, tendr?as algo como esto:

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

Las acciones indican lo que sucede en un juego creado con el Game


Maker. Las acciones se colocan en los eventos de los objetos. Cuando
el evento ocurre estas acciones se llevan a cabo, resultando en cierto
comportamiento para las instancias del objeto. Hay una gran cantidad
de acciones disponibles y es importante que entiendas lo que hacen.
Todas las acciones se encuentran en las p?ginas / fichas a la derecha
de la ventana de propiedades de objeto. Hay siete grupos de
acciones. Puedes ver el grupo haciendo clic en la ficha correcta.

A la derecha de la ventana de propiedades de objeto podemos ver las


siete pesta?as correspondientes a los diferentes tipos de acciones.
Cuando mantienes el rat?n sobre una de las acciones, se muestra una
breve descripci?n para recordar su funci?n.
Para colocar una acci?n en un evento, solo arr?strala de las p?ginas /
fichas a la lista de acci?n. Puedes cambiar el orden de la lista,
arrastrando y soltando los iconos. Si presionas la tecla
mientras arrastras puedes hacer una copia de la acci?n. (Puedes
arrastrar y copiar acciones entre las listas en diferentes ventanas de
propiedades de objetos). Usa el bot?n derecho del rat?n para borrar
acciones (o usa la tecla ) o para copiar y pegar acciones.
Cuando colocas una acci?n, la mayor?a de las veces aparece una
ventana de di?logo, en la cual puedes especificar ciertos par?metros
para la acci?n. Dos tipos de par?metros aparecen en muchas
acciones. En la parte superior puedes indicar a qu? instancia aplica la
acci?n. El valor por defecto es self, que es la instancia para la cual se
est? realizando la acci?n. La mayor?a de las veces esto es lo
adecuado. En el caso de un evento collision, puedes especificar
tambi?n si se aplica la acci?n a la otra instancia involucrada en la
colisi?n. De esta forma puedes por ejemplo destruir la otra instancia.
Finalmente, puedes elegir si aplicas la acci?n a todas las instancias de
un objeto. De esta manera puedes por ejemplo cambiar todas las
pelotas rojas por azules. El segundo tipo de par?metro es la casilla
marcada Relative. Al marcar esta casilla, los valores que introduzcas
ser?n relativos a los valores actuales.
Nota del autor: La descripci?n de los eventos y las acciones est?
sacada de la traducci?n del manual original de Game Maker 5.0 del
ingl?s al castellano realizada por ZonaMakers.com
Iremos viendo los eventos y acciones a medida que los vayamos
utilizando.

A?ADIENDO MUSICA Y SONIDO

Deberemos bajar 4 temas musicales en formato mid, uno por cada


nivel de los que consta nuestro juego. Estos temas musicales seran
las m?sicas de fondo de los diferentes niveles.
En la siguiente direcci?n de Internet encontrareis gran variedad de
archivos mid:
http://www.midiworld.com/
Bajaremos tambi?n efectos sonoros para cada una de las siguientes
acciones:
- Para cuando el comecocos come un coco
- Para cuando el comecocos come un supercoco
- Para cuando el comecocos se come a un fantasma
- Para cuando el comecocos se come una cereza
- Para cuando el fantasma se come al comecocos
- Para cuando pasamos de nivel
En la siguiente direcci?n de Internet encontrareis tambi?n multitud de
efectos de sonido que bajaremos en formato wav:
http://www.flashkit.com/soundfx/
Despu?s a?adiremos las m?sicas y las carpetas dentro de la secci?n
Sounds de la misma forma que a?ad?amos las im?genes dentro de la
secci?n Sprites (bot?n derecho del rat?n encima de Sounds y “Add
Sound”). Crearemos una carpeta para los efectos sonoros tal como
vemos en la imagen de abajo (“Add Group”).
Ahora trabajaremos con las m?sicas de fondo, los archivos mid. Los
efectos sonoros se manejan por medio de acciones, por lo que los
dejaremos para m?s adelante.

Para incluir una m?sica de fondo en uno de nuestros niveles


(pantallas) basta con ir a esa pantalla (“Room”) y hacer clic en la
pesta?a “Settings”.
Dentro de esta pantalla haremos clic en el bot?n “Creation code” y
a?adiremos lo siguiente:
Donde “Fase1” es el nombre que di?ramos a el archivo mid que
corresponde a la primera pantalla.
La instrucci?n “sound loop” indica que la m?sica se repita cuando el
tema llegue a su fin de forma que no acabe de sonar hasta que no
indiquemos lo contrario con otra instrucci?n.
Repetiremos la operaci?n con cada una de las pantallas (“rooms”)

SCORE E INDICADOR DE VIDAS

A continuaci?n haremos la parte de la pantalla donde aparecen la


puntuaci?n del jugador (“score”) y las vidas que le quedan
(“indicador de vidas”) para finalizar la partida.
- Crearemos e insertaremos en nuestro juego un sprite que servira de
fondo a la zona donde se mostraran los indicadores de score y vidas.
Se llamar? “controlvidas”.
El sprite no ser? m?s que una imagen igual de ancha que nuestra
room y que no sea m?s alta que la zona que habilitamos cuando
hicimos la habitaci?n para dar cabida a estos indicadores.
En una room de 800 por 600 pixeles la imagen tendr?a unas
dimensiones de 800 pixeles de ancho por 48 de alto
aproximadamente:
Dentro de las propiedades del sprite s?lo debe de estar marcada la
casilla “Preload texture”.
- Crearemos un objeto con el sprite “controlvidas”
F?jate en las propiedades del objeto.
Tienes que quedar igual:
- Visible
- Depth -10000
- Persistente (El objeto no se destruye al
pasar de un nivel a otro, la puntuaci?n
sigue sumando en vez de empezar de
cero en cada pantalla).
Iremos explicando cada opci?n en clase.

A continuaci?n seguiremos los siguientes pasos:


Clic en “Add Event” y luego en “Create”.
Haremos clic en la pesta?a “Score”
Arrastraremos la acci?n “Set the escore” a la
ventana de “Actions”. Haremos doble clic en el
icono y daremos un valor de inicio al score de 0.
Arrastraremos la acci?n “Set the number of lives” a
la ventana de “Actions”. Haremos doble clic en el
icono y fijaremos las vidas del jugador en 3.
Arrastraremos la acci?n “Set the Windows caption
info” a la ventana de “Actions”. Haremos doble clic
en el icono y la editaremos de forma que quede as?:

A?adiremos otro evento (clic en “Add Event” y luego en “Other”)


Dentro del men? que se nos aparece seleccionaremos “No more
lives”. As? le indicaremos al programa qu? tiene que hacer cuando al
jugador no le queden m?s vidas.
Arrastramos a la ventana de “Actions” las siguientes acciones:
“Show the high score table” de la pesta?a “Score”.
“Restart the game” de la pesta?a “main 2”.
De esta forma le indicamos al programa que si el jugador se queda
sin vidas le muestre la pantalla de records y luego vuelva a empezar
otra partida reiniciando el juego.
Ahora a?adimos un nuevo evento: “Draw”
Una vez lo hayamos hecho arrastramos las siguientes acciones a su
ventana de acciones:
“Draw a sprite image” de la pesta?a “Draw”. Lo editamos (doble clic)
de forma que quede as?:
A continuaci?n arrastramos las siguientes acciones (pesta?a “Score”):
“Draw the value of score”
“Draw the lives as image”

As?, mediante las coordenadas (x,y) indicamos la posicion en pantalla


de los indicadores de puntuaci?n y de vidas.
Otras acciones que podemos a?adir para este evento son:
“Set the font”
“Set the color”
De esta forma podremos variar el tama?o y color del indicador de
puntuaci?n(“Score”).
Previamente tendremos que haber a?adido una fuente en “fonts”.
Vamos a probar a?adiendo la fuente “Jokerman” a tama?o 18.
A continuaci?n editamos el primero de nuestros niveles. En las
propiedades de room, “objects” seleccionamos el objeto
“controlvidas” y lo colocamos en alg?n lugar de la pantalla como
vemos en la imagen (es el circulito con el interrogante):
Veamos como va quedando nuestro juego (apretamos tecla F5 para
ejecutarlo).

PREPARADO LAS ROOMS

Colocaremos nuestros objetos en sus posiciones de inicio en las


“rooms”:
Seleccionamos cada objeto desde la pesta?a “Objects” de la ventana
de edici?n de la room, mediante el men? de la parte inferior:
Una vez seleccionado tan solo debemos hacer clic en el lugar de la
“room” donde deseemos colocar dicho objeto.

EVENTOS Y ACCIONES DE NUESTROS OBJETOS

A estas alturas deber?amos haber definido los siguientes objetos:


Veamos las propiedades, eventos y acciones de cada uno:
Ladrillo: Visible. S?lido.
Coco: Visible.
Supercoco: Visible.
Comecocos: Visible, Depth -1
CREATE
Create > Change sprite into comecocos_quiet
Indicamos que el comecocos aparece inicialmente con la forma del
sprite “Comecocos_quiet” y a velocidad 0.5.

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.

COLLISION EVENT WITH OBJECT LADRILLO


Si el comecocos colisiona con un ladrillo:

(con speed 1)

COLLISION EVENT WITH OBJECT COCO

Cuando el comecocos se come un coco que suene el sonido de


comerse un coco.
(Applies to other)
Se destruye el coco.
(Relative)
Se aumenta el marcador de puntos en 10.

COLLISION EVENT WITH OBJECT FANTASMA


Que suene el sonido de muerte del comecocos
Esperar segundo y medio.
Que todos los fantasmas vuelvan a la posici?n de inicio.
Que todos los fantasmas miedosos vuelvan a la posici?n de inicio.

(Relative)
COLLISION EVENT WITH OBJECT FANTASMA_ASUSTADO

COLLISION EVENT WITH OBJECT SUPERCOCO

MOVIMIENTO DEL COMECOCOS


KEYBOARD EVENT FOR LEFT

Obligamos al objeto a estar centrado en la cuadricula de nuestra


room.

… Si el objeto esta alineado con la cuadricula (32*32) y apretamos la


flecha izquierda del teclado el objeto avanza hacia la izquierda a
velocidad 4.

El sprite que representa al objeto pasa a ser “comecocos_izq”


(comecocos avanzando hacia la izquierda).

Procederemos a realizar los mismos pasos con Up, Rigth y Down.

OUTSIDE ROOM

Si el objeto sale fuera de la pantalla ejecutar el script outside_wall (o


como se llame nuestro script).
Fantasmas (para todos)

CREATE

Cambia el sprite que representa al objeto a “normal”, esto es, a


cuando el comecocos no ha comido un supercoco.

STEP
(32*32)

Si el objeto est? alineado con la rejilla ejecutar el script


“adap_direction” que indica al fantasma hacia donde tiene que
moverse.
COLLISION EVENT WITH OBJECT LADRILLO
Si el fantasma colisiona con un ladrillo…

Cambia la direcci?n hacia el lado contrario, tanto horizontal como


verticalmente.

OUTSIDE ROOM

Si el objeto sale fuera de la pantalla ejecutar el script outside_wall (o


como se llame nuestro script).

FANTASMA ASUSTADO
ALARM 0

BONUS

CREATE

ALARM 0

ALARM 1

COLLISION EVENT WITH OBJECT COMECOCOS

[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.

Los valores intermedios se usan para dibujar elementos parcialmente transparentes.

• 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

números son los argumentos de la función.

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

un efecto de profundidad muy vistoso. Esta técnica se llama scroll parallax.

• blending Ver image blending.


• bmp Es un tipo de archivo de gráficos de alta calidad (mapa de bits). Sólo es aconsejable utilizar bmps para
fondos transparentes.

• 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

cuanto menor sea más al frente se situará la instancia.

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).

Con esta técnica se pueden crear efectos visuales muy espectaculares.


J
• jpg Es un tipo de archivo de gráficos de tamaño reducido, pero que no permite transparencias. Ideal para
usarlo en fondos no transparentes o para sprite sheets.

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

coches los participantes sigan la carretera.

• 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

monedas u otros objetos.

• 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

para generalizar código repetido y así ahorrar espacio y ganar velocidad.

• 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).

• strip Ver sprite sheet.

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.

• tile set Ver tile sheet.


• tile sheet Es una imagen muy grande de la que se pueden extraer imágenes más pequeñas para crear
tiles.

• 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.

Este tutorial es cortesía de www.tutoek.tk y Gracias a la gran comunidadgm


www.comunidadgm.org

You might also like