Introducción a GML Parte V

Scripts

Construcción With

Comentarios en GML

Orden de evaluación



Scripts

Un script es un bloque de código identificado mediante un nombre. Son muy útiles cuando reiteradamente se desea realizar una serie de operaciones específicas en una o más instancias. Los scripts permiten reusar un bloque de código sin tener que escribir una y otra vez las operaciones que conforman dicho bloque. Aunque no es necesario, generalmente el script trabaja sobre una serie de parámetros que le son proporcionados al momento de llamarlo. Estos parámetros o argumentos constituyen los datos de entrada para el script. Cuando se le suministran parámetros de entrada al script, se dice que se le pasan argumentos al script.

Una funcionalidad importante de un script es poder acceder (dentro del script) a los argumentos que se le pasan. Estos argumentos se almacenan en variables predefinidas denominadas sucesivamente argument0, argument1, ..., etc... hasta argument15. Esto quiere decir que pueden pasarse hasta 16 argumentos a un script siempre y cuando se llame al script mediante código. En caso de que se utilice la acción de script D&D, tan sólo podrás especificar los 5 primeros argumentos.

Cuando usas los argumentos argument0...15 para un script, debes asegurarte de que el script cumple los requisitos adicionales que exija. Por ejemplo, si tienes un script que requiere dos argumentos diferentes y sólo indicas uno de ellos, obtendrás un código de error al ejecutarlo. Lo mismo sucede si indicas más argumentos de los inicialmente exigidos. Sin embargo, puedes aportar un número variable de argumentos para un script usando el formato de argumentos predefinido, argument[0 ... 15]. Para implementar una serie de argumentos como una cadena de valores puedes usar argument_count para fijar su número exacto y adaptar el script para que únicamente use dichos argumentos que han sido fijados.
NOTA: No puedes mezclar estos dos tipos de variables de argumentos cuando llamas un script. Debes usar o bien argument0...15 o bien argument[0...15].

Los Scripts pueden igualmente retornar un valor, de forma que este pueda usarse en otras funciones. Con este fin puedes usar la sentencia return:

return <expression>


Observa que la ejecución del script finaliza en la sentencia return, lo que significa que todo código situado inmediatamente después de que la sentencia return haya sido llamada no será ejecutado. Aquí tienes un pequeño ejemplo de un script llamado "scr_sqr" y que nos calcula la raíz cuadrada de cualquier valor que le indiquemos, incluyendo un mensaje de error en caso de que el argumento aportado no sea un número real:

{
if !is_real(argument0)
   {
   return 0;
   }
else
   {
   return (argument0 * argument0);
   }
}


Para llamar un script desde una pieza de código, haz lo mismo que cuando llamas funciones - es decir, escribe el nombre del script con los valores de argumento entre paréntesis. De esta forma, el script situado más abajo sería llamado de esta forma:

if keyboard_check_pressed(vk_enter)
   {
   val = scr_sqr(amount);
   }

En ocasiones puede que necesites saber cuántos argumentos incluidos en un script han sido transferidos desde el código que lo llama, y GameMaker:Studio también permite la utilización de la siguiente variable de sólo-lectura para ello: argument_count

Si deseas saber más información sobre scripts, por favor visita Uso avanzado de Scripts.




La Construcción "With"

Como se indicó en la sección Accediendo a variables desde otras instancias, es posible leer y cambiar el valor de variables en otras instancias, pero en determinados casos quizás necesites hacer algo más que cambiar una simple variable en esas otras instancias. Por ejemplo, imagina que quieres mover todos los objetos ball en tu juego 8 píxeles. Posiblemente pienses que se solucionará simplemente con el siguiente código:

obj_ball.y = obj_ball.y + 8


Pero esto no es correcto, lo que sucede es que el lado derecho de la asignación obtiene el valor de la coordenada y de la primera pelota y le suma 8. Después este nuevo valor se establece para la coordenada y de todas las pelotas, así que el resultado es que todas las bolas tienen la misma coordinada y, incluso, la siguiente instrucción:

obj_ball.y+=8


tendrá que mismo efecto ya que simplemente es una abreviación de la primera sentencia. Así que, ¿cómo mover cada pelota 8 pixeles en y? Para esto existe la sentencia with en GML. Su forma global es:

with (<expresión>) <sentencia>


<expresión> se refiere a una o más instancias, y como se vió antes, se puede usar un identificador de instancia, el nombre del objeto (el cual indicaría que todas las instancias de ese objeto ejecutarían el bloque de código) o uno de los objetos especiales (all, self, other). <sentencia> se ejecuta para cada una de las instancias indicadas, como si esa instancia fuese actualmente la propia instancia self. Así que para mover todas las instancias del objeto de la pelota 8 pixeles abajo, puedes usar:

with (obj.ball)y+=8;


Si se quiere ejecutar múltiples sentencias, se usan llaves para encerrarlas. Así que, por ejemplo, para mover todas las bolas a una posición aleatoria, puedes usar:

with (obj_ball)
{
x=random(room_width);
y=random(room_height);
}


Observa que, para la(s) sentencia(s), la instancia indicada entre paréntesis se ha convertido en la instancia alctual self desde donde se ejecuta el bloque de código, lo cual significa que para la(s) misma(s) sentencia(s), la instancia original ( la que contiene la función "with" y el bloque de código) se ha convertido en la otra (other) instancia. Así que, por ejemplo, para mover todas las pelotas hacia la posición de la instancia actual, puedes usar esto:

with (obj_ball) { x = other.x; y = other.y; }


La sentencia "with" es una herramienta muy poderosa y muy útil en muchas, muchas circunstancias, así que es importante que entiendas claramente cómo puede ser usada. Para ayudarte, incluímos más ejemplos de su uso a continuación:

with (instance_create(x, y, obj_Ball))
{
speed = other.speed;
direction = other.direction
}


El código anterior creará la instancia de obj_Ball y le asignará la velocidad y dirección de la instancia que ejecuta el bloque de código.

with (instance_nearest(x, y, obj_Ball))
{
instance_destroy();
}


El código de arriba destruirá la instancia del objeto obj_Ball más cercana a la instancia que ejecuta el código.

var inst;
inst = none;
with (obj_ball)
   {
   if str > other.str inst = id;
   }
if inst != noone target = inst;


El código de arriba es algo más complejo que los anteriores debido a que estamos usando una variable local. Esta variable es local al script y no a la instancia, así que puede ser usada y leída por todas las instancias referidas en el bloque de código. Comenzamos por declararla como la palabra reservada noone, después usamos la construcción "with" para que cada instancia del objeto obj_ball compare su variable "str" contra la variable str de la instancia que ejecuta el bloque de código. Si el valor de la variable (en obj_ball) es mayor, entonces ESA instancia de obj_ball almacenan su id en la variable local "inst", resultando que al final del código, solo la instancia (de obj_ball) con un valor mayor que la instancia original será almacenada en la variable local inst (o el objet especial noone si ninguna resultó mayor). Para más información sobre variables locales consulta la sección Variables y su alcance.


Commentarios en el código

Cuando se trabaja en colaboración con otras personas, cuando se desarrolla un proyecto grande, o símplemente para propósitos de referencia personal y depuración, comentar el código y los scripts es muy importante. En GameMaker:Studio hay varias maneras para dejar notas y comentar secciones de código, incluso comentarios para bloques de código completo que pueden verse en la lista de eventos del objeto actual, muy útil para dejar notas a la vista de cualquiera, explicar un código complejo en particular o dejar un recordatorio indicando la función de una sección de código.

Lo primero que se puede hacer es dejar un comentario corto usando // antes del texto. por ejemplo:

//inicializar variables
sr = 10;
hp = 100;


También se puede usar comentarios de más de una línea para indicar c´reditos, imotir una sección que sólo sirve para depuración, o explicar los argumentos de un script. Para eso se usa /* ... */ de la siguiente manera:

/*
uso:
diff = angle_difference(angle1,angle2);

angle1 primera dirección en grados, tipo real
angle2 segunda dirección en grados, tipo real

devuelve: La diferencia de los ángulos especificados, en un rango de -180 a 180

GMLscripts.com
*/
{
return ((((argument0 - argument1) mod 360) + 540) mod 360) - 180;
}


Por último, cuando se usan bloques de código en un objeto, tener muchos bloques distintos puede resultar confuso, sobre todo cuando vhay varios en un mismo evento, algo similar a esto:: Pero, si en la primera línea de tu código hay un comentario comenzando con ///, ese comentario aparecerá como el título de ese bloque de código. Por ejemplo, al tener esto:

///Check position


Entonces el bloque de código tendrá su ropia descripción, como se muestra a continuación:




Orden de evaluación

Cuando se programa en GML hay que tener en cuenta que no hay garantía en el orden en que los argumentos se evalúan en las llamadas a funciones. Esto significa que el orden en que se colocan las funciones dentro del código puede cambiar de plataforma en plataforma, por lo que es necesario codificar el orden de una manera explicita. Esto es debido a las diferentes optimizaciones que hay según la plataforma que se elija, por ejemplo si se elije Windows como plataforma las funciones podrían ser evaluadas de derecha a izquierda, pero en el caso de la plataforma HTML5, las funciones podrían ser evaluadas de izquierda a derecha. Por lo tanto, para evitar algún error, lo mejor es no llamar a funciones múltiples en los argumentos de una llamada a función, pues se podría estar creando código dependiente del orden de evaluación.

Para ver un ejemplo de lo que esto significa, considera el siguiente código que llama varias funciones al tiempo que las usa como argumentos de un script:

buffer_seek(buff, buffer_seek_start, 0);
scr_buffer_get_info(buffer_read(buff, buffer_s8), buffer_read(buff, buffer_s16),buffer_read(buff, buffer_s16));


El problema radica en que en ciertas plataformas, la ultima función buffer_read será llamada primero, por lo que todos los argumentos del script serán incorrectos ya que los datos estarán siendo leídos desde el buffer en "orden inverso". Esto desde luego impacta de manera negativa al código, pues todos los subsecuentes valores que se pasen al script mediante la función buffer_read estarán equivocados!

Para evitar esto se debe llamar a las funciones explícitamente en el orden requerido y guardar el valor retornado en variables antes de pasar argumentos al script, por ejemplo, en el caso anterior:

var val[0] = buffer_read(buff, buffer_s8);
var val[1] = buffer_read(buff, buffer_s16);
var val[2] = buffer_read(buff, buffer_s16);
scr_buffer_get_info(val[0], val[1], val[2]);


Mientras que por un lado esto pudiera parecer una solución más verbosa, por otro mantiene la claridad en el código y evita cualquier posible problema con el orden de evaluación.