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 dex/y
redondeado en la dirección de cero al número entero más cercano. El operadormod
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 bitComo 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:
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 globalesPor 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 >
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 >
}
EjemploEl 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
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 DoLa 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 BreakLa 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();
}
ComentariosPuedes 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.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_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_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
ey
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 variablesx
ey
. 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 loque 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
yvspeed
). 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 elid
de una instancia, o la palabra especialother
.place_snapped(hsnap,vsnap)
Devuelve si la instancia está alineada con los valores de snaphsnap
ymove_random(hsnap,vsnap)
Mueve la instancia a una posición libre, y la alinea con los valoreshsnap
yvsnap
, 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 yvert
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, unaid
de una instancia, o las palabras claveself
,other
oall
.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ónpath_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ámetrostepsize
. Si la instancia ya ha llegado a esa posición no se moverá. Sicheckall
es true la instancia se parará cuando choque con una instancia de cualquierobjeto. 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 objetoobj
.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 objetoobj
.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 valorahead
(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ñorotstep
(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ámetromaxrot
(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 deonspot
. Sionspot
es true la instancia rotará en su posición la cantidad indicada pormaxrot
. 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 enstepsize
. Usa pasos como en la funciónmp_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 objetoobj
.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 enstepsize
e intentando evitar colisionar con los obstáculos. Utiliza pasos potenciales como la funciónmp_potential_step()
y los parámetros se pueden configurar conmp_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 unfactor
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 porfactor
. Unfactor
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 objetoobj
.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
ytop
indican la posición de la esquina superior izquierda de la rejilla yhcells
yvcells
indican el número de celdas horizontales y verticales respectivamente. Finalmente,cellwidth
ycellheight
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 claveall
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
eystart
indican el comienzo del path yxgoal
eygoal
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 claveall
, o la id de una instancia. El argumentoprec
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 argumentonotme
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 objetoobj
. 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 objetoobj
. 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 objetoobj
.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 objetoobj
. Esta función es muy poderosa. Puedes usarla para comprobar si una instancia puede ver a otra chequeando si entre ellas hay una paredInstancias
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 tipoobj
.obj
puede ser un objeto o la palabra claveall
. Si no existe se devuelve el objeto especialnoone
. 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 objetoobj
.obj
puede ser un objeto, la id de una instancia o la palabra claveall
.instance_number(obj)
Devuelve el número de instancias de tipoobj
.obj
puede ser un objeto o la palabra claveall
.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 claveall
. Si no existe se devuelve el objeto especialnoone
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 claveall
.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 claveall
.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 claveall
. Si no existe se devuelve el objeto especialnoone.
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 tipoobj
.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 tipoobj
.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. Sinotme
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 indicarall
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). Siinside
es igual a false las instancias completamente fuera de la región son desactivadas. Sinotme
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 indicarall
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. Siinside
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);
}
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. Puedesestablecerlo 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).