Scripting

Introducción al scripting.Código básico.

¿Qué es un Script?

Un script o guion es un fichero de texto que contiene una serie de instrucciones (siempre separadas por “;”) que se ejecutarán seguidas una detrás de otra.

Ejemplo:

Hint “Esta es la primera instruccion”;

Sleep 5;

//Esto es un cometario, no una instrucción. La máquina no lo ejecuta.

Hint “Esta es la tercera instruccion”;

Sleep 3;

/*Esto también es un comentario, la máquina tampoco lo ejecuta.*/

Hint “Esta es la ultima instruccion”;

Este pequeño script consta de 5 instrucciones (cada una en una línea sin contar los comentarios) que se ejecutarán una detrás de la otra.

La primera línea lo que hace es sacar un cuadro por pantalla con una nota que dice Esta es la primera instrucción. Una vez ejecutada esta instrucción, la máquina pasa a ejecutar la siguiente que será sleep 5. Esta instrucción lo que hace es decirle a la máquina que se espere 5 segundos antes de ejecutar la siguiente instrucción (simplemente es para que nos diera tiempo a leer el primer mensaje antes de que saltara el segundo). Y la tercera, cuarta y quinta instrucción funcionarían de una manera idéntica a las anteriores.

Los comentarios: son líneas introducidas en el script que sirven para explicar cómo funciona una parte del código. La máquina cuando llega a ellos, se los salta y pasa a ejecutar la siguiente instrucción. Esto es muy útil, ya que puedes crear un script, y al cabo de mucho tiempo quieres retocar o cambiar algo, pero al abrirlo de nuevo no recuerdas que hace cada cosa. Los comentarios te ayudarían a recordar o facilitarían al resto de personas que abrieran y leyeran tu script a entenderlo de forma más fácil y rápida.

Estos comentarios en el lenguaje de ArmA pueden ser de dos tipos:

//Tipo 1 Al poner “//” indicas que todo lo que se encuentre a la derecha de estas barras es un comentario. Pero NO la siguiente línea (a menos que también lleve “//”).

/*Tipo2*/ El comentario es todo lo que se encuentre entre “/*” (comienzo de comentario) y “*/” (cierre de comentario)

Consejo 1:

En realidad al ordenador se la suda cómo escribas el código, siempre que respetes la sintaxis y separes las instrucciones con “;” Podríamos haber escrito el script anterior de la siguiente forma y funcionaría igualmente:

Hint “Esta es la primera instruccion”;

Sleep 5;

Hint “Esta es la tercera instruccion”;

Sleep 3;

Hint “Esta es la ultima instruccion”;

Sin embargo es una buena práctica dejar el código escrito separado por líneas y párrafos para la mejor lectura del mismo por parte del usuario que lo está escribiendo o leyendo.

Consejo 2:

En ArmA 3, parece ser que si escribes código con caracteres especiales como “ú” o “ñ” en los activadores funciona y los reconoce. Sin embargo, si los escribes en un script externo (el típico que se coloca en tu carpeta raíz de la misión) no funcionan y deja un espacio en blanco. Por ejemplo: Hint “Albañil”; al estar jugando la partida saldría “Alba il”.

Consejo 3:

Los comentarios también sirven para meter trozos de código que no quieres que se ejecuten. Por ejemplo tienes una intro de misión con una pantalla en negro que dura unos segundos, y no quieres ver la intro cada vez que le das a vista previa para testear. Puedes meter el código de la intro dentro de comentarios, de esta forma no se ejecutará y no veras la intro cada vez que cargues. Cuando ya tienes todo testado y ves que funciona, quitas el comienzo y cierre de comentario y la intro volverá a ejecutarse.

¿Cómo se ejecuta un script? Llamadas a Scripts

A grandes rasgos, en ArmA3 puedes ejecutar scripts de dos formas diferentes.

La primera de ellas, es escribir el código directamente en un activador. Este código se ejecutará cuando la condición del activador se cumpla. Esto es aceptable siempre que el código no sea muy complicado ni muy largo.

Sin embargo para código más complejo o para una mejor organización del mismo, se deben escribir los scripts en un fichero de texto externo. Este fichero, irá guardado en la carpeta raíz de la misión. Se puede escribir y editar con el block de notas, el notepad++, etc… Pero para que el juego lo reconozca y funcione, deberá tener la extensión “sqf”.

Por ejemplo:

Tengo una misión en mis documentos/Arma3 other profiles/Pepito, llamada [COOP]_OPERACION_RESACA y dentro de esa carpeta, aparte del mission.sqm el description.ext y el init.sqf (que de hecho también es un script) tengo un archivo llamado “mi_script.sqf” que es un script personalizado que realiza cierta acción dentro del juego (por ejemplo una charla con un civil).

Pero… ¿cómo le digo al juego que ejecute el script externo en un momento dado?

Esto puede hacerse de varias formas, pero la típica y general de hacerlo es mediante el “comando”:

[] execVM “mi_script.sqf”;

Esto es lo que denominamos en términos de programación “hacer una llamada a un script”

Los corchetes [] no hace falta que sepáis que significan de momento. Aunque ya os adelanto que es un mecanismo para pasar datos desde el juego al propio script, para que luego el script pueda operar con esos datos internamente.

Pero lo que sí que tenéis que saber es que hay que escribir execVM y entre comillas la ruta al script en cuestión. Si está en la carpeta raíz de la misión bastaría con “mi_script.sqf”, por el contrario, si el script estuviera en una carpeta llamada scripts (por ejemplo), se escribiría la ruta completa:

[] execVM “scripts\mi_script.sqf”;

OJO: ¡Nunca nos olvidemos del punto y coma!

Elementos

Variables:

Es una “cajita” que almacena un valor de cierto tipo que puede ser modificado mientras se ejecuta el script. (En verdad es una posición de memoria que almacena un valor de cierto tipo).

Por ejemplo:

x=5; //La “cajita” llamada X contiene el número entero 5

comprobacion=false; //La “cajita” llamada “comprobacion” contiene el valor lógico falso

Tipos de variables

Como habrás apreciado en los ejemplos anteriores estas cajitas pueden almacenar valores de diferentes tipos. “X” almacena un número y “comprobacion” almacena un valor lógico. A continuación vamos a explicar los tipos de valores que estas “cajitas” llamadas variables pueden almacenar:

Un número: valores numéricos sin o con puntos decimales (5) (90.4) (-54) (-3.6).

Un valor lógico (Boolean): los boolean o boleanos, son un tipo de dato que tiene dos estados, true (o valor cierto) y false (o valor falso). Estas variables son utilizadas para comprobar condiciones, por ejemplo en activadores.

Una cadena de letras (Strings): las variables también pueden almacenar cadenas de letras o caracteres.

Arrays o vectores: Esto es algo más avanzado y requiere de una amplia explicación que se dará más adelante si veo que sea necesario. Pero para que te vayas haciendo a la idea de lo que es, se podría decir que más que una “cajita” es un “cajón” lleno de “cajitas”.

Ejemplo:

mi_array=[“Genomas_degenerado”,”Mauricio”,”Peace_el_picao”,”Fort_el_nino_rata”,”Sativa_GTA_exploiter”];

mi_array sería un “cajón” en cuyo interior habría 5 “cajitas”, y en cada cajita habría una cadena de letras. En la primera cajita del cajón estaría “Genomas_degenerado” en la segunda “Mauricio”, y así hasta la última “cajita” del “cajón” en la que estaría la cadena de letras “Sativa_GTA_exploiter”.

Un bando (Side): esta es una particularidad exclusiva del lenguaje de ArmA, una variable de tipo “Side” es una cajita que almacena un bando.

Bandos:

WEST ó blufor

EAST ó opfor

Independent ó resistence

Civilian

Ejemplo:

mi_bando=WEST;

La cajita “mi_bando” tendría almacenado el valor WEST o blufor;

Un grupo (Group): esta es una particularidad exclusiva del lenguaje de ArmA, una variable de tipo “Group” es una cajita que almacena un grupo. Todas las unidades tienen un grupo.

Hay más tipos de variables, pero en general estas son las que siempre vas a utilizar en scripts sencillos.

La magia de estas variables es que no almacenan valores fijos o estáticos, a medida que el juego avanza o la ejecución del script avanza, el interior de esas cajitas (el valor) puede ser modificado.

Podríamos tener una variable x con valor 5 y mediante operaciones ir sumándole 1 a x hasta que acabase valiendo 7.

Y=3; //A Y le asignamos el valor 3

X=5; //A X le asignamos el valor 5

X=X+1; //En este momento X vale 6 (X=5+1)

X=X+1; //En este momento X vale 7 (X=6+1)

Y=X; //En este momento X sigue valiendo 7 pero el valor 3 de Y se ha borrado y ahora Y vale 7 (Y=7)

//La “cajita” Y que al principio contenía un 3 ahora contiene un 7

Pero las operaciones y todo lo que se puede hacer con variables lo vamos a explicar en el siguiente apartado

Operaciones con Variables

Básicamente se pueden hacer tres tipos de operaciones con variables, asignar un valor, una operación aritmética y una comparación y comparación lógica.

  1. Asignación: es la operación básica y en resumidas cuentas lo que hace es darle un valor a una variable, es decir, meter contenido en una cajita.

Ejemplo:

Voy a poner un pequeño ejemplo en el que mediante únicamente el uso de la operación de asignación, intercambio los valores almacenados en dos variables distintas. Es decir, cojo el contenido de las dos cajitas y se los intercambio.

X=8; //A X le asigno el valor 8

Y=5; //A Y le asigno el valor 5

Auxiliar=0; //Esta es una variable que me ayudará para intercambiar los valores de X e Y (en realidad da igual que sea 0, 7 o 23, su contenido no me importa en este caso).

Auxiliar =X; //Guardo el antiguo valor de X (8) en Auxiliar

X=Y; //A X le asigno el valor que tiene Y (5). Ahora X=5

Y=Auxiliar; //A Y le asigno el valor que guarde en Auxiliar (8). Ahora Y=8

  1. Operación Aritmética: lo mismo que hacías en el cole, sumamos, restamos, multiplicamos y dividimos las variables a nuestro antojo.

Ejemplo:

X=8; //A X le asigno el valor 8

X=X*4; //A X la multiplico por 4. Ahora X vale 32

Y=2; //A Y le asigno el valor 2

X=X-Y; //A X que valía 32 le resto Y que vale 2, por tanto ahora mismo X vale 30.

Resultado=X/Y //Una nueva variable llamada resultado le asigno el valor que queda de dividir X entre Y. Resultado valdría 15.

TABLA DE OPERADORES ARITMÉTICOS:

Operador (Símbolo) Nombre Ejemplo
+ Suma a+b
Resta a-b
* Multiplicación a*b
/ División (Cociente) a/b
% Módulo (Resto de división) a%b
^ Elevado a (Potencia) a^b

Comparación: como su propio nombre indica sirven para comparar dos variables entre sí o una variable y un valor.

TABLA DE OPERADORES DE COMPARACIÓN:

Operador (Símbolo) Nombre Ejemplo
== Igual a==b

¿Es el contenido de a igual al de b?

!= No Igual a!=b

¿Es el contenido de a distinto al de b?

< Menor que a<b

¿Es el contenido de a menor que el de b?

> Mayor que a>b

¿Es el contenido de a mayor que el de b?

<= Menor o Igual que a<=b

¿Es el contenido de a menor o igual que el de b?

>= Mayor o igual que a>=b

¿Es el contenido de a mayor o igual que el de b?

TABLA DE OPERADORES LÓGICOS:

Operador (Símbolo) Nombre Ejemplo
! Negación !a

Si a vale true entonces false

Si a vale false entonces true

&& And (y) a&&b

¿Es tanto a como b true?

|| Or (o) a||b

¿Es a ó b true?

Antes de continuar hay que explicar un concepto muy sencillo de matemática discreta (lógica), para poder hacer un buen uso de los operadores de comparación y lógicos sin llevarnos a errores, y que los activadores nos funcionen con las condiciones que nosotros queremos.

Si a=true entonces !a=false

Si a=false entonces !a=true

A&&B

True && True = True

True &&False = False

False && True =False

False && False = False

A||B

True || True = True

True ||False = True

False|| True =True

False|| False = False

Ejemplo:

Si tenemos dos variables X=5 e Y=8 y hacemos la siguiente pregunta ¿Es X<6 y X>Y? Nos dirá: False. ¿Por qué?

(X<6) true Esta sería la primera condición

(X>Y) false Esta sería la segunda condición

La condición global en código de script sería: (X<6)&&(X>Y) es decir, true&&false por lo tanto la condición es false, no se cumple.

Nota importante:

Hay una serie de palabras llamadas “palabras reservadas” que no podemos utilizar como variables a modificar. Por ejemplo las palabras time, player, this, if, then, while,do, etc… ¿Por qué no podemos hacer la operación de asignación time=70;?

La palabra reservada time es una variable que almacena el tiempo en segundos que han pasado desde el inicio de misión. Es decir en el primer minuto de misión valdrá 60 y a los 5 minutos de misión time valdrá 60*5=300.

Por lo tanto no tiene sentido que en tu script hagas time=120; ¡Eso sería como intentar viajar al futuro o al pasado! Ahora fuera de coñas, tú puedes hacer comparaciones con esa variable, pero nada más. Por ejemplo un script que pasados 10 minutos Pablo Escobar muriese.

Si (time>600) entonces (¡Mata a Pablo Escobar!!!!!)

Tampoco podemos usar una variable que se llame “then” porque es una palabra reservada al control de flujo:

If(time>600)then{

Pablo_Escobar setDamage 1.0;

}

No pasa nada si no entiendes este código, esto del control de flujo lo explicaremos más adelante.

Si escribiéramos la operación de asignación:

then=”me lo paso por el forro”;

Al ejecutar nos saltaría un error y nos diría que no podemos usar esa palabra.

Objetos

Los objetos en programación son representación de cosas tangibles de la vida real. Por ejemplo un soldado. Estos objetos tienen características o atributos que los definen. Olvidémonos de ArmA ¿Qué atributos le pondrías a un soldado real?

  1. Nombre
  2. Edad
  3. Nacionalidad
  4. Especialización
  5. Constitución física
  6. Inteligencia
  7. Personalidad
  8. Años de experiencia

Por así decirlo un objeto es un contenedor de atributos (variables o cajitas) que lo modelan y le dan forma.

¿Qué es un objeto en ArmA?

Cualquier cosa que coloques en el editor es un objeto, desde un coche, un IED, o un activador. La diferencia está en sus atributos internos que lo moldean.

Un coche tendrá como atributos, su modelo, su combustible, su armadura o salud, su bando.

Un IED tendrá como atributos , su forma, su manera de desactivación, su tiempo antes de explosión, etc..

Un activador tendrá como atributos, su condición de activación, su código que ejecuta cuando se activa, su radio, etc…

Estos atributos no dejan de ser las variables que antes hemos explicado. Por lo tanto podemos cambiar los estados de los objetos, cambiando el valor de sus variables atributo.

Por ejemplo, tenemos un objeto en el mapa que es una pickup que hemos colocado y le hemos dado el nombre de variable “coche”.

Sabemos que las pickups (y en general todos los objetos de tipo vehículo) tienen un atributo que es la gasolina. Gasolina entonces es una variable contenida en el objeto “coche” la cual podemos modificar para cambiar su estado.

coche setFuel 0.0;

Sabemos que coche es el objeto, y fuel es una variable de tipo numérica (atributo de coche) que nos dice cuanto combustible queda en el vehículo. ¿Qué cojones es setFuel? ¿Y qué cojones es el 0.0?

SetFuel es un comando (o función) predefinido en el lenguaje de ArmA 3 y 0.0 es el valor que le paso a este comando (argumentos). Pero todo esto lo veremos en el siguiente apartado.

Comandos y Parámetros (o Argumentos)

Los comandos son mini-scripts ya definidos en el propio ArmA. Los cuales no tienes que saber cómo funcionan internamente. Lo único que necesitas saber para utilizarlos es qué datos necesitan que les pases para que ellos operen internamente (parámetros o argumentos), y qué datos o resultados te van a devolver (si es que devuelven algo). ¡Ojo! Estos comandos también son palabras reservadas.

Cada uno es distinto y tendrá sus peculiaridades, obviamente es imposible sabérselos todos (ni siquiera los propios frikis de bohemia se acordarán de todos), por ello debes de tener esta página a mano en la que se explica cómo funciona cada uno (description), la sintaxis correcta de escritura para que la máquina lo entienda (syntax), los parámetros (o argumentos) que ha de tener (parameters) y los datos que te va a devolver si es que te devuelve algo (return):

https://community.bistudio.com/wiki/Category:Scripting_Commands_Arma_3

Veamos un ejemplo para que luego puedas ver tu cada uno de ellos en el enlace anterior:

El comando “getStamina”: https://community.bistudio.com/wiki/getStamina

La descripción dice: Obtiene la estamina actual (~número de segundos antes de acabarse).

La sintaxis necesaria: getStamina unit; Es decir, cómo se tiene que escribir.

Los parámetros dice: unit. Que es un objeto, por ejemplo b1_1 (la estamina que le queda al soldado en punta del primer pelotón): getStamina b1_1;

Nota: podría haber más de un parámetro, en este caso no es así, pero volvamos al setFuel:

https://community.bistudio.com/wiki/setFuel

En este caso tenemos dos parámetros, el objeto (heli1 por ejemplo) y la cantidad (1.0 por ejemplo).

El valor que nos devuelve( Return) dice: Número (este número según hemos leído en la descripción, es el número de segundos que le quedan de estamina).

Nota: en el caso del setFuel no nos devolvería nada (Nothing).

Argumentos o parámetros de scripts:

¿Te acuerdas cuando te dije en el apartado de cómo ejecutar scripts que de momento no importaba que supieras lo que significaban los corchetes de la llamada:

[]execVM “mi_script”;

¿Pues ahora es el momento de averiguar que son esos [].

Básicamente si no pones nada dentro de esos corchetes, significa que al script no le estas pasando ningún parámetro (por qué no lo necesite). Es como un comando sin parámetros (argumentos). Imaginemos que no existiera el comando setFuel, y que quisiéramos mediante un script externo respostar el vehículo. Podríamos en un activador llamar a nuestro script de repostaje con la siguiente llamada:

[cantidad]execVM “mi_script”;

Cantidad” en este caso es el parámetro (argumento) que sería una variable (cajita) que almacena la cantidad deseada de fuel que le queremos meter al vehículo, por ejemplo 0.5 (la mitad del depósito). El script, ahora podrá hacer sus operaciones internas con este dato, y llenar el depósito con la cantidad que le has dicho.

El Objeto THIS como parámetro:

Muchos me comentáis cosas de si hay que poner this aquí o allá y realmente no entendéis muy bien por qué.

Sin meternos en temas de programación profundos, vamos a decir entre comillas que this es un parámetro o argumento, que es el propio objeto que está llamando al comando que sea. Es decir, si yo tengo un helicóptero con nombre de variable heli1 y le quiero hacer setFuel, podemos hacerlo de dos formas:

this setFuel 1.0; //en el init del propio heli1

heli1 setFuel 1.0;

¿Pero cuando es una y cuando otra?

This solo puede utilizarse en inits de objetos para referirse al propio objeto (recordad que un activador también es un objeto). O sea, podría poner this en el init del objeto heli1, pero no en ningún otro sitio. Es decir no puedo hacer un “this setFuel 1.0;” en un activador o en el init de una misión por qué no tiene sentido. Lo que estarías haciendo es decir que quieres meterle gasolina al activador, no al helicóptero.

Lo que si qué podría poner es heli1 setFuel 1.0;

¿Puedes ver cómo van encajando todas las piezas del puzle?

Control de flujo

Existen una serie de estructuras de control que nos permiten modificar el flujo de ejecución (el rumbo) de un script.

Con las estructuras de control se puede:

  1. De acuerdo a una condición, ejecutar un grupo u otro de sentencias (If-Then-Else)
  2. De acuerdo al valor de una variable, ejecutar un grupo u otro de sentencias (Switch-Case)
  3. Ejecutar un grupo de sentencias mientras se cumpla una condición (While-Do)
  4. Ejecutar un grupo de sentencias un número determinado de veces (For-Foreach)

Vamos a explicar cada una de estas a continuación:

Estructura de Control Condicional IF-THEN-ELSE:

Sirve para comprobar una condición o condiciones y en base a si se cumplen o no se cumplen ejecutar un trozo de código u otro. La estructura tiene esta forma:

If (condicion) then {

Instruccion1;

Instruccion2;

Instrucción n;

} else {

Otra instrucción_1;

Otra instrucción_n;

};

¿Cómo funciona o que ocurriría cuando se ejecutase esta estructura?

Lo primero que haría la máquina al llegar a esta estructura sería preguntarse lo siguiente: “¿Se cumple la condición?” Más concretamente ¿condicion==true? Y a partir de aquí podrían ocurrir dos cosas:

1. Que condición==true: si la condición se cumple entonces entraría en el then (o sea en las llaves que van después de la palabra then) y ejecutaría las instrucciones 1,2 … hasta la n. Y acto seguido se saldría de la estructura, sin ejecutar else, es decir, no se ejecutarían las instrucciones: Otra_instruccion_1; Otra_instrucción_2…. Hasta Otra_instruccion_n;

Y se terminaría de ejecutar esta estructura continuando con el trascurso normal de ejecución (seguirán ejecutándose las instrucción que habrían a continuación por debajo).

2. Que condición==fasle: si la condición no se cumple entonces entraría en el else, es decir, no se ejecutarían las instrucciones del then: instruccion1; instrucción2…. Hasta instruccio_n; Y sí que se ejecutarían en cambio las instrucciones: Otra instrucción_1; hasta Otra instrucción_n;

Y se terminaría de ejecutar esta estructura continuando con el trascurso normal de ejecución (seguirán ejecutándose las instrucción que habrían a continuación por debajo).

NOTA: Cuando decimos if(condicion), condición no tiene por qué ser solo una condición, condición puede estar compuesta por una o más condiciones.

Ejemplo:

If ((time>60) &&(!alive Pablo_Escobar)) then {

Hint “Han pasado más de 60 segundos de misión”;

Sleep 5;

Hint “Y además Pablo Escobar está muerto”;

} else {

Hint “Podría ser que todavía no hayan pasado 60 segundos de misión”;

Sleep 5;

Hint “O podría ser que Pablo Escobar siguiese vivo”;

Sleep 5;

Hint “O podrían ser las dos cosas”;

};

Estructura de control de valor de variable SWITCH-CASE:

Es similar a la anterior pero en lugar de comprobar si la condición es true o false lo que comprobamos es qué valor tiene una variable dentro de un rango de valores. Y dependiendo del valor de este, ejecutaremos un trozo de código u otro.

La estructura tiene la siguiente forma:

Switch (valor) do {

case 1: {instruccion1};

case 2: {instruccion2};

case …

case N: {instruccionN};

default: {instrucción_pordefecto};

};

¿Cómo funciona o que ocurriría cuando se ejecutase esta estructura?

Aclaremos una cosa, “valor” en este caso es el nombre de una variable. Y esta variable (cajita) contendrá un valor previamente asignado (ejemplo: valor=5; valor=valor+2; ahora mismo “valor” valdría 7).

Lo primero que haría la máquina al llegar a esta estructura sería preguntarse ¿Cuánto vale “valor”? Y diría: valor vale 7. Buscaría una línea donde pusiera “case 7” y ejecutaría esa sección de código, saltándose el resto de instrucciones de los demás “cases”. Y acto seguido proseguiría la ejecución normal línea a línea del script.

¿Qué ocurre si valor==7, pero no existe ningún “case 7”?

Entonces se ejecutaría la sección de código escrita en “default”

Estructura Bucle de Repetición por Condición WHILE-DO:

Como su propio nombre indica sirve para repetir código mientras una condición se cumpla. ¿Cómo harías que durante toda la misión apareciese un mensaje que dijera “Soy un pesado”? De momento con lo que sabes la única manera sería escribir cientos de millones de líneas con la sentencia: Hint “Soy un pesado”; y confiar en terminar la misión antes de que se terminara de ejecutar el script. Pero esto suena a locura.

La forma lógica es utilizar esta estructura que tiene esta forma:

While {condición}do{

Instrucción1;

Instruccion2;

Intrucción_N;

};

¿Cómo funciona o que ocurriría cuando se ejecutase esta estructura?

Lo primero que haría la máquina al llegar a esta estructura sería preguntarse lo siguiente: “¿Se cumple la condición?” Más concretamente ¿condicion==true? Y a partir de aquí podrían ocurrir dos cosas:

1. Que condición==false: Si la condición no se cumple nunca se ejecutaría el código interno, o sea no se ejecutaría intruccion1, 2,…N.

2. Que condición==true: Si la condición sí que se cumple, entraríamos dentro del bucle y empezaría a dar vueltas de la siguiente forma:

Se ejecutarían todas las instrucciones y al final de cada vuelta se volvería a preguntar ¿condición==true? Si ha cambiado y ahora condición==false, se saldría de este bucle y se seguiría la ejecución normal del código. Pero si condición==true volvería a dar otra vuelta. Y al final se volvería a preguntar otra vez ¿condicion==true?

Ejemplo:

X=1;

While{X<10}do{

Hint “Soy un pesado”;

X=X+1;

Sleep 5;

};

Hint “Por fin! ya no soy un pesado!”;

Antes de leer la solución que viene a continuación te invito a que te hagas un dibujo y vayas escribiendo los valores de X en cada vuelta y los mensajes que saldrían en la partida, ¿cuantas vueltas da el bucle? ¿10 o 9 vueltas? ¿Y si la condición fuese {X<=10}, daría 10 o 9 vueltas?

Solución:

Básicamente este código haría lo siguiente; al principio del todo X vale 1 por lo tanto la condición X<10 es “true” así que entraríamos a la primera vuelta del bucle. En esta primera vuelta sacaría en pantalla “soy un pesado” y le sumaria 1 a la X y se detendría 5 segundos.

Ahora X vale 2 y volvería a hacer lo mismo puesto que X<10==true, sacando el mensajito, sumándole 1 a la X (que ahora valdría 3) y esperando 5 segundos.

Y así continuamente hasta que X valiera 10, ¿Qué pasa cuando X vale 10? ¿10<10? Falso, así que la vuelta 10 no entraría en el bucle y ejecutaría el “por fin! ya no soy un pesado!”.

Por lo tanto al final del todo habríamos visto 9 mensajitos de “soy un pesado” y 1 de “por fin ya no soy un pesado”. Y la variable X tendría un valor de 10.

Y para nuestro ejemplo del principio en el que queremos que nunca se deje de ejecutar el código utilizaríamos:

While{true}do{

Hint “Soy un pesado”;

};

Esto es lo que se llama un bucle infinito. Nunca se saldría del bucle porque true siempre es igual a true.

Estructura Bucle de Repetición FOR:

Sirve para repetir código un número determinado de veces, y tiene la estructura siguiente:

for “i” from 1 to N do {

instruccio1;

instrucción2;

Instrucción_M;

};

Lo que hace este código es dar “N” vueltas ejecutando lo que hay dentro, pongamos un ejemplo para verlo más claro.

for “i” from 1 to 5 do {

hint “Esta es la vuelta numero: ”+i;

};

NOTA: +i lo que hace es añadirle al final el valor de la i.

Lo que hace este bucle es:

Primero crea una variable llamada “i” y le asigna el valor inicial 1.

1º Vuelta:

i” vale 1 así que en esta vuelta escribirá un mensaje en pantalla que dirá:

Esta es la vuelta numero: 1

2º Vuelta:

i” vale 2 así que en esta vuelta escribirá un mensaje en pantalla que dirá:

Esta es la vuelta numero: 2

3º Vuelta:

i” vale 3 así que en esta vuelta escribirá un mensaje en pantalla que dirá:

Esta es la vuelta numero: 3

4º Vuelta:

i” vale 4 así que en esta vuelta escribirá un mensaje en pantalla que dirá:

Esta es la vuelta numero: 4

5º Vuelta:

i” vale 5 así que en esta vuelta escribirá un mensaje en pantalla que dirá:

Esta es la vuelta numero: 5

Aquí termina la ejecución de este bucle, y seguiría el transcurso normal del código del script línea por línea.

Estructura que recorre los elementos de un Array FOREACH:

Antes de explicar la estructura vamos a comentar algo más sobre los arrays. Ya hemos dicho que este tipo de variable, más que una cajita es un cajón que almacena cajitas. Pero ¿qué utilidad tiene esto?

Nosotros podemos crear nuestros cajones y llenarlo de lo que queramos, por ejemplo de nombres de nuestras unidades:

Array_Peloton1= [b1_1,b1_2,b1_3,b1_4,… … … … ,b1_12];

//Recordad que b1_1 era el nombre de variable que le dábamos al hombre en punta del 1º.

Pero también hay arrays que los crea el propio juego, algunos os sonarán, por ejemplo

¿Qué es AllUnits?

AllUnits es básicamente un array que contiene todas las unidades que hay en el mapa.

La estructura de un FOREACH tiene esta pinta:

{

_x instruccion1;

_x instruccion2;

_x …

_x Instrucción_N;

}foreach mi_Array;

X será en cada vuelta un elemento diferente de mi_Array, hasta recorrerlos todos. Vamos a poner un ejemplo:

mi_Array=[b1_1,b1_2,b1_3]; //Este array contendría al primer trinomio

Y como queremos gastarles una broma al primer trinomio, vamos a quitarles todas las armas de golpe y también el mapa. Vamos a ser buenos y vamos a dejarles la brújula ¿Os suena esto de algo? xD

{

_x removeAllWeapons;

_x removeItemMap;

} foreach mi_Array;

En la primera vuelta, le quitará las armas y el mapa al primer elemento, o sea a b1_1 (al hombre en punta).

En la segunda vuelta, le quitará las armas y el mapa al segundo elemento, o sea a b1_2 (al numeral 2).

En la tercera vuelta, le quitará las armas y el mapa al tercer elemento, o sea a b1_3 (al numeral 3).

Ya no habría más elementos en el array así que aquí terminaría la ejecución de la estructura.

Básicamente traducido lo que hace es: “A cada elemento de mi_Array: quítale las armas y quítale el mapa”

Te recuerdo que la máquina es capaz de ejecutar cientos de instrucciones por segundo, por lo tanto a ojo humano, al trinomio se le quitará el material a la vez.

EJEMPLO PARA ENGLOBAR TODO:

Hasta ahora hemos visto cada estructura de control de flujo y cada definición de las cosas por separados. Pero al principio del manual te dije que programar es como pintar un cuadro, y que sabiendo como coger el pincel, lo que es una circunferencia, una recta y un radio de circunferencia se puede pintar una teta. ¡Vamos a pintar esa teta!

Imaginemos que se nos plantea el siguiente dilema:

Pablo Escobar (el gran narcotraficante de Lingor) se ha puesto un marcapasos en el corazón, y sus esbirros (dado su fanatismo hacía su patriarca) han aceptado ponerse un electro-collarín conectado por satélite al marcapasos de Escobar. Yen el momento en que su amado líder palmase, los collarines electrocutarían a todos y los mataría. Porque ¿qué son sus míseras vidas sin su líder? ¡Recordemos que esto debe poder suceder en cualquier momento de la partida!

Antes de seguir leyendo, párate a pensar. Con lo que has leído y visto en este manual ¿ cómo podrías hacer que esto ocurriese en la partida? No importa que no llegues a la solución o no lo resuelvas todo (de hecho ningún alumno primerizo sería capaz de resolver esto), pero es importante que le des vueltas y se te ocurran ideas para cada cosa… Un nombre de variable por aquí, una estructura if-then-else por allá, un bucle por otro lado…

Probablemente cuando veas la solución tu mente explote, pero créeme que cuando leas un montón de scripts de otra gente y sobretodo te animes a escribir unas cuantas líneas de código, las soluciones salen casi sin pensar. Y verás que este script en realidad es de lo más simplón y fácil. Es cuestión de practicar.

Lo que voy a hacer ahora es desmenuzar el problema y utilizando todas las herramientas que te he enseñado, combinarlas entre sí para resolverlo.

Habría dos formas de resolver esto (mediante un activador o mediante un script externo). Por darle más chicha, lo voy a hacer mediante un script externo que se ejecutaría en el init.sqf de la misión. Mi script externo se llamará “electrocollarin.sqf” y al final del init.sqf de mi misión habría una llamada a este script de este estilo:

[]execVM “electrocollarin.sqf”;

Algo sumamente importante es que la unidad (el objeto) que representa a Pablo Escobar tenga un nombre de variable. Yo en el editor le llamaría Pablo_Escobar.

Veamos que escribiríamos en dicho script:

1º En primer lugar me han dicho que este acontecimiento debe poder suceden en cualquier momento de la partida. Por lo tanto, eso me suena a que tendré un bucle infinito y dentro de este, todo el tinglado que quiero montar:

While {true}do{ //Recordamos que true==true por lo tanto siempre se ejecuta

//Todo el tinglado

};

2º También sé, que quiero que suceda algo cuando Pablo muera. Y que el comando (alive Pablo_Escobar) me devuelve “true” si está vivo, y “fasle” si está muerto. No sotros lo queremos a la inversa, por lo que haremos una negación (!alive). Por lo tanto el resto del tinglado irá dentro de una comprobación de este tipo:

While{true}do{

If(!alive Pablo_Escobar)then{

//El resto del tinglado

}else{

//En realidad aquí no quiero que se ejecute nada ya que me da igual lo qué //suceda cuando esté vivo, por lo tanto no me hace falta escribir el else. En el //siguiente ejemplo borraré este trozo.

};

};

3º Sé (y si no lo sé, lo busco en la wiki o pregunto en foros) que AllUnits es un array automático de ArmA que guarda todas las unidades (de todos los bandos) que hay puestas en el mapa. Y sé que con la estructura foreach puedo recorrer una a una todas las unidades (elementos) que hay guardados en este array y con el comando “setDamage” matarlos. ¿Pero, es realmente esto lo que quiero? ¡ No, pero se acerca!

Podría hacer:

{

_x setDamage 1.0;

} foreach AllUnits;

¡Pero entonces estaría matando a todo el mundo! Y sólo los fanáticos de Pablo Escobar (Opfor) llevan el collarín, así que tendré que añadir algo… La respuesta es otro if-then-else.

Investigando la wiki de bohemia un poco, descubro que el comando “side variable;” te dice el bando al que pertenece dicha variable. Aprovechémonos de esto:

{

If(side _x ==EAST)then{

_x setDamage 1.0;

}; //Una vez más no escribo el else porque no tiene que hacer nada

}foreach AllUnits;

Imaginemos que cuando muere Pablo en el mapa hay cuatro unidades: 1 blufor, 2 opfor y 1 civil.

AllUnits contendrá:

AllUnits=[blufor1,opfor1,civil1,opfor2]

Cuando se ejecute el último código que hemos escrito, cogerá el primer elemento de AllUnits y dirá:

_x=blufor1;

¿es _x del bando EAST (opfor)? ¿(side _x==EAST)? No(porque el comando side _x devolverá WEST) ¿(WEST==EAST)?

La condición es falsa (false), no ejecutes el then, ejecuta el else. Como no hay else porque no nos importa que suceda a las unidades que no son opfor, en esta vuelta, el bucle no hará nada. No matará a nadie.

Ahora pasará al segundo elemento de AllUnits y dirá:

_x=opfor1;

¿es _x del bando EAST (opfor)? ¿(side _x==EAST)?

Si(porque el comando side _x devolverá EAST), ¿EAST==EAST? La condición es verdadera (true), no ejecutes el else, ejecuta el then.:

_x setDamage 1.0; // En este caso estoy haciendo “opfor1 setDamage 1.0;”

*Acabo de electrocutar a uno de los dos esbirros de Pablo.

Ahora pasará al tercer elemento de AllUnits y dirá:

_x=civil1;

¿es _x del bando EAST (opfor)? ¿(side _x==EAST)?

No(porque el comando side _x devolverá CIVILIAN)

¿CIVILIAN==EAST?

La condición es falsa (false), no ejecutes el then, ejecuta el else. Como no hay else porque no nos importa que suceda a las unidades que no son opfor, en esta vuelta, el bucle no hará nada. No matará a nadie.

Ahora pasará al cuarto y último elemento de AllUnits y dirá:

_x=opfor2;

¿es _x del bando EAST( opfor)? ¿(side _x==EAST)?

Si(porque el comando side _x devolverá EAST) ¿EAST==EAST? La condición es verdadera (true), no ejecutes el else, ejecuta el then:

_x setDamage 1.0; // En este caso estoy haciendo “opfor2 setDamage 1.0;”

Ya no quedan elementos que recorrer en AllUnits, por lo tanto se termina de ejecutar el bucle.

He matado a los dos esbirros de Pablo con el electrocollarín, pero he dejado intactos a las unidades blufor (WEST) y civiles (civilian) que había por el mapa, y si también había independientes (resistence), también les habría dejado vivos.

Juntando todo el código en el script final ¿cómo quedaría?

While{true}do{

If(!alive Pablo_Escobar)then{

{

If(side _x ==EAST)then{

_x setDamage 1.0;

};

}foreach AllUnits;

};

};

En un activador sería:

Condicion: (!alive Pablo_Escobar)

Al Activarse:

{

If(side _x ==EAST)then{

_x setDamage 1.0;

};

}foreach AllUnits;

Algo que quedaría mucho más chulo sería buscar una animación, si es que existe (que lo dudo), en la que al soldado le dé un tabardillo o parezca que se electrocuta y poner un:

_x switchmove “tabardillo_electrocucion”; //comando para animaciones

sleep 4;

antes de la instrucción “_x setDamage 1.0;” de esta forma antes de morir haría una animación de muerte por electrocución que quedaría muy chula, pero esto ya depende de cómo quieras complicarte o de las limitaciones que tenga el propio ArmA.

Obviamente, esto es un ejemplo, espero no ver ahora cientos de misiones en las que cuando matemos al líder o al comandante, a todos los enemigos les dé un tabardillo y mueran =P

ASI QUE RECUERDA:

La imaginación y tu creatividad son las barreras del scripting. Cuanta más imaginación tengas, más scripts podrás escribir. Los resultados son más o menos fáciles de alcanzar dependiendo de la complejidad de tu idea y del nivel de detalle que le quieras incluir, además de la práctica y soltura que tengas para escribirlos.

3.Ámbitos de ejecución de scripts

Lado del servidor :

if (isserver) then { código; };

Se ejecuta todo lo del servidor, lo que se debe ejecutarse sólo una vez y las variables globales

Por ejemplo , los scripts de respaneo.

Muy importante indicar en los activadores en el editor, que se ejecute en el lado del servidor , marcando la casilla “server” o se ejecutará el script tantas veces como jugadores existan en la partida.

Ámbito local: Es aquello que sólo se ejecuta en cada cliente, en cada ordenador.

Ejemplos: Las presentaciones de las misiones , el briefing, variables globales

Lado del multiplayer : Se puede ejecutar las presentaciones de las misiones y el briefing

if (isMultiplayer) then { código ; };

Se puede ejecutar en el ámbito del servidor y multiplayer a la vez.

Se puede ejecutar en el ámbito del servidor y local.

4. Variables locales y globales

Hay que hacer dos diferencias importantes en el ámbito de las variables que se usan en el scripting.

Variables locales:

Aquellas que sólo actúan dentro de un script determinado. Para ello deben definirse previamente con la declaración private[“_variable1”,“_variable2”,“_variable3”];

Se diferencian de las globales dentro de un script a veces porque llevan un guión bajo antes del nombre. _NombreDeLaVariable

private[“_i”,”_j”,”_text”,”_building”,”_nuevaposicion”,”_housePos”,”_patrullon”,”_allpositions”];

_housePos = _this select 0;

_patrullon= _this select 1;

_j=ceil(random 10000);

_text = formatText [“grpmalo%1”,_j];

_text= [position _housePos ,east, _patrullon,[],[],[],[],[],getDir _housePos+180] call BIS_fnc_spawnGroup ;

Variables globales:

Aquellas que actúan en cualquier ámbito , fuera y dentro de todos los scripts. Se define una variable asignándole un valor por ejemplo VariableGlobal1= false y luego para hacer que su valor sea el mismo en todos los ámbitos locales de los jugadores y scripts que lo usan publicvariable “VariableGlobal1”.

Las variables globales son buenas para usar scripts que dependen de los resultados de otros pero no debe usarse mucho porque su uso exagerado puede causar disminución de recursos del servidor.

Por ejemplo, se puede usar para contar el número de jugadores y según el número de jugadores unas variables globales como número de patrullas, número de integrantes por patrulla , habilidad del enemigo, extensión de un marcador etc…. Estas variables globales mediante publicvariable “variableGlobal” podrán usarse en scripts de respawn de enemigos haciendo que la misión se adapte al número de jugadores.

95,158 total views, 23 views today