Serie Tuttorials - Tutoriales de Hina |
3 | ||||||||||||||||||||||||||||||||||
Serie Tuttorials Bienvenido a la Serie Tuttorials de Hina. Esta será una serie de tutoriales enfocados a enseñarte a programar scripts, entender el lenguaje Lua y últimamente ayudarte a desarrollar tus propios minijuegos. Todo esto se intentará lograr por medio de un método práctico de guías que te harán crear distintos scripts explicando todo lo que está ocurriendo, un paso a la vez. Si eres un completo principiante en Lua o no sabes nada, este es un buen lugar para comenzar. También habrá tutoriales intermedios o avanzados con diferentes temáticas para que todos podamos aprender algo. ¿A quién está dirigida esta guía?
NOTA para iniciados: Aprender a programar requiere tiempo y dedicación. No esperes aprender todo en dos semanas ni hacer un minijuego en dos días. Por último recuerda que también debes poner de tu parte y practicar o aprender por tu cuenta. Contenido La idea de este proyecto es crear una guía que vaya desde cero hasta el punto en que eres capaz de desarrollar tus propias ideas y seguir por tu cuenta. Es por eso que en los primeros capítulos se verán cosas muy básicas y lo más interesante quizás venga después. No obligo a nadie a comenzar por ningún lugar y si quieres puedes echar un vistazo a los capítulos de más adelante, sin embargo lo recomendable sería que leyeras las cosas básicas y llegues a entenderlas. Nótese que los nuevos capítulos que vaya sacando seguirán el orden pero también haré algunos más adelantados para que la gente que sabe un poco más pueda aprovechar también la guía. (Y para los impacientes). Ten en cuenta que este índice está sujeto a cambios, puesto que algunas cosas las tengo planeadas muy anticipadamente.
Capítulo 0 En este capítulo veremos algunos requisitos o pasos previos que debemos cumplir antes de empezar a trabajar con Lua y programar nuestros scripts. Los preparativos que veremos son los siguientes:
Permisos para usar /lua Para usar el comando /lua primero necesitas estar en una tribu, tener permisos para cargar mapa y también puede que necesites el permiso de tus compañeros de tribu para cargar scripts. A nadie le gustaría que pusieran un script cuando están viendo una película o se cambie el mapa inesperadamente. Además un requisito para usar /lua es tener 100 quesos en el perfil y no haber usado hack anteriormente. Si has usado hacks en una cuenta es probable que no tengas derecho a usar el comando con esa cuenta. Editor de código Para usar /lua simplemente debes pegar o escribir directamente el código en la ventana. Pero esto es poco práctico, incómodo y no es lo ideal. Lo recomendable es usar un editor externo. Puedes usar cualquier editor de texto, incluyendo el bloc de notas de Windows, pero para hacer las cosas más prácticas y sencillas usaremos un editor más sofisticado. Yo recomiendo Notepad++ del cual tengo un hilo con más información: http://atelier801.com/topic?f=6&t=573436 Prueba de /lua Cuanto tengas todo listo haremos una primera prueba para comprobar si puedes usar el comando en tu casa de tribu. Simplemente entra a tu tribu, abre la ventana de /lua y pega el siguiente código: print("Hola ratas") Deberías ver en el chat una pestaña Lua y en ella el mensaje "Hola ratas". Si todo ha funcionado correctamente entonces ya estás listo para comenzar a trabajar con /lua. Recuerda no cerrar nunca la pestaña Lua o no se volverá a abrir. Aquí aparecen los mensajes del sistema al ejecutar scripts y también los mensajes de error, además de los mensajes que mostremos con print. Fiel acompañante Para varios ejemplos a lo largo de esta serie necesitarás de otro ratón en la sala que te sirva para hacer las pruebas. Lo indicaré al comienzo del capítulo. El mejor acompañante que puedes tener es otra cuenta tuya. Si no dispones de otra cuenta simplemente puedes crear una cuenta nueva e invitarlo a tu tribu; ni si quiera es necesario reclutarlo a la tribu, basta con usar el comando /inv nombre_del_ratón para que pueda entrar a la casa. También será necesario que uses dos ventanas/pestañas con Transformice para esto. Capítulo 1 En este capítulo conoceremos algunas de las funciones más sencillas de la API de TFM. Funciones para entregar queso, matar al jugador, convertirlo en chamán, en vampiro, etc. Y aprenderás de manera práctica qué es una variable y cómo se usa. Nota: de ahora en adelante siempre que en alguna parte veas "Hinakagiyama" reemplázalo por tu nombre de usuario, entre comillas. Para empezar probaremos la siguiente línea de código: tfm.exec.giveCheese("Hinakagiyama") Al ejecutar ese código en tu tribu, tu ratón instantáneamente obtendrá el queso. Ahora que tienes queso intentemos esto: tfm.exec.playerVictory("Hinakagiyama") Tu ratón automáticamente entrará a la madriguera (¡aún si no hay ninguna en el mapa!). Esto solo funciona cuando el ratón tiene queso. Ahora usaremos la siguiente función para convertir a tu ratón en chamán: tfm.exec.setShaman("Hinakagiyama") Intenta poner el nombre de tu acompañante en lugar del tuyo y verás que también puedes convertirlo en chamán. tfm.exec.setShaman("Violetanose") Y ahora comenzaremos a usar variables. Primero comenzemos probando la función para matar a un ratón: tfm.exec.killPlayer("Hinakagiyama") Esto matará al instante a tu ratón. Puedes cambiar el nombre por el de tu acompañante pero en lugar de eso haremos lo siguiente: mouse = "Hinakagiyama" tfm.exec.killPlayer(mouse) Lo que hemos hecho aquí es crear una variable llamada mouse, dentro de ella hemos guardado el nombre de nuestro ratón. Y luego hemos usando la función para matar a nuestro ratón, pero en lugar de entregarle el nombre escrito, le hemos entregado la variable anterior. ¿Suena complicado? Una variable es simplemente un baúl que almacena cualquier dato. Puede ser un número, un texto como en este caso, o una tabla de datos. Intenta cambiar el contenido de la variable usando el nombre de tu acompañante: mouse = "Violetanose" tfm.exec.killPlayer(mouse) Como puedes ver solo hemos cambiado la primera línea, guardando el otro nombre en lugar del nuestro en la variable mouse. Algo importante que debes tener en cuenta es que los textos en el código, como "Violetanose", siempre van con comillas. Estos textos se llaman literales. Los nombres de las variables en cambio no son literales, son nombres que identifican a la variable en el código, y debes escribirlo siempre igual. No es lo mismo mouse que mOUSe o Mouse. Si hubieras escrito en el código lo siguiente: mouse = "Violetanose" tfm.exec.killPlayer(Mouse) Daría error porque en la segunda línea estás usando una variable que no existe (Mouse) y al no existir no contiene ningún nombre válido de ratón. También debes tener en cuenta que las variables pueden tomar el nombre que tú quieras, pero hay algunas reglas:
Por lo tanto algunos nombres válidos de variable serían: mouse Rata cajaGrande _TEXTO3 Mientras que algunos nombres inválidos de variable serían: 123mouse caja-grande ratón (las letras con tilde no se reconocen como un caracter válido en el código, pero sí puedes usar tilde en los textos) and (palabra clave en Lua) Ahora que ya sabes más sobre variables intentaremos hacer otra cosa, usando dos variables: mouse1 = "Hinakagiyama" mouse2 = "Violetanose" tfm.exec.setVampirePlayer(mouse1) tfm.exec.giveMeep(mouse2) El código anterior convertirá a mouse1 en un vampiro y a mouse2 le otorgará el poder de usar Meep. Primer guardamos los nombres de los jugadores dentro de las variables mouse1 y mouse2. Puedes cambiar los nombres al comienzo si quieres usar a otros jugadores, o puedes cambiar los números de las variables abajo si quieres intercambiar los roles. Intenta practicar con estas y otras funciones de la API usando variables. Aquí tienes una lista con las funciones simples que puedes probar de la misma forma que hemos hecho a lo largo de este capítulo: tfm.exec.killPlayer tfm.exec.respawnPlayer tfm.exec.setShaman tfm.exec.setVampirePlayer tfm.exec.giveCheese tfm.exec.playerVictory tfm.exec.giveMeep En el siguiente capítulo usaremos variables para guardar números y veremos algunas otras funciones de TFM que utilicen valores numéricos. Capítulo 1 En este capítulo usaremos valores numéricos en las nuevas funciones que vamos a ver. Y usaremos variables para almacenar estos datos numéricos. También miraremos brevemente las operaciones aritméticas. Comenzaremos usando movePlayer, una función para mover a un jugador al punto especificado: tfm.exec.movePlayer("Hinakagiyama", 700, 60) Esto moverá a nuestro ratón a las coordenadas (700,60) en el mapa. Más adelante veremos más sobre las coordenadas pero en este ejemplo nos mandaría a la esquina superior derecha del mapa. Ahora usaremos variables: name = "Hinakagiyama" x = 700 y = 60 tfm.exec.movePlayer(name, x, y) Este ejemplo hará exactamente lo mismo que el anterior, pero esta vez estamos usando variables para almacenar el nombre del jugador y ambas coordenadas a donde lo enviaremos. Quizás llegado a este punto ya te habrás preguntado qué ventajas puede tener usar variables. Pues imagina que no solo quieres mover al jugador a ese sitio; también quieres convertir al mismo jugador en chamán e invocar una pelota en la misma posición. Y además crear un efecto visual de teletransporte en ese mismo lugar. Si hacemos esto sin usar variables, nos quedaría lo siguiente: tfm.exec.movePlayer("Hinakagiyama", 700, 60) tfm.exec.setShaman("Hinakagiyama") tfm.exec.addShamanObject(6, 700, 60) tfm.exec.displayParticle(36, 700, 60, 0, 0, 0, 0) Imagina ahora que decides cambiar de jugador o modificar la posición (700,60). Tendrás que editar estos tres valores en cada línea. Imagina que no son cuatro líneas sino 300, y que en lugar de esos 3 valores tienes 8. En cambio si usamos variables para almacenar estos valores, tendríamos: name = "Hinakagiyama" x = 700 y = 60 tfm.exec.movePlayer(name, x, y) tfm.exec.setShaman(name) tfm.exec.addShamanObject(6, x, y) tfm.exec.displayParticle(36, x, y, 0, 0, 0, 0) Y ahora es muchísimo más fácil modificar los valores. Simplemente editamos las tres primeras líneas en donde declaramos nuestras variables. Cambiando el nombre "Hinakagiyama" y cambiando los números con las coordenadas. No te preocupes si por ahora no sabes qué es lo que hacen esas funciones abajo. Más adelante las iremos viendo en detalle. Por ahora basta con saber que hacen simplemente las cuatro cosas que nombramos antes: mover al jugador, convertirlo en chamán, añadir un objeto y mostrar un efecto visual (partícula). En el siguiente capítulo veremos algunas de las cosas interesantes que podemos hacer con las variables y con los números o textos en general. Capítulo 1 Ahora manipularemos las variables que ya aprendimos a usar, utilizando lo que llamamos operadores. Los operadores son símbolos que nos permiten indicarle al script que se haga una operación aritmética (matemática) o de otro tipo. Operadores aritméticos
Como puedes ver en los ejemplos, no solo utilizamos números. También usamos variables que deberían contener valores numéricos. Uno de los ejemplos es uno que aplicaremos en la práctica a la hora de hacer minijuegos donde nuestros ratones tengan vidas: vidas = vidas - 1 El símbolo = no es un operador, sino un símbolo de asignación. Nos permite asignar un valor a una variable, en otras palabras guardar un valor como hemos hecho en los capítulos anteriores. Nótese que en este caso estamos usando la misma variable a la que le estamos asignando un valor. La idea es que queremos reducir el número de vidas en 1 (cuando un ratón ha muerto), entonces el nuevo valor de vidas será igual a ese valor menos 1. Otro de los ejemplos, en cambio, será uno que nunca usaremos en la práctica: variable = 10 + 5 Simplemente podemos guardar directamente el número 15 dentro de nuestra variable, en lugar de decirle al script que haga la suma. Esto es importante porque incluso una pequeña suma consumirá tiempo y recursos a la API, por lo que idealmente querremos poner la menor cantidad de operaciones posibles y asignar directamente el número que ya sabemos que vamos a guardar (10 + 5, o sea 15). Asociatividad, agrupamiento y prioridad En primaria probablemente habrás escuchado de la propiedad asociativa de la suma y la multiplicación. Esas cosas también se aplican aquí a la hora de hacer operaciones aritméticas. El uso de paréntesis también te permite agrupar números y darles prioridad a esas operaciones, al igual que ocurre en la aritmética o álgebra convencional. Veamos el siguiente ejemplo: a = 2 * (4 * 3) b = (2 * 4) * 3 Ambos casos entregan el mismo resultado (24). Sin embargo con la resta y división no ocurre lo mismo. a = (4 - 2) - 1 b = 4 - (2 - 1) El resultado para a será 1 y para b será 3. Aquí es donde hablamos de la prioridad de las operaciones. Siempre aquellas operaciones que ocurren entre paréntesis tendrán prioridad sobre las que están más afuera. También algunas operaciones tienen prioridad sobre otras: la división y multiplicación siempre se calcularán antes que la suma y la resta. Por último, las operaciones siempre se harán de izquierda a derecha cuando tengan la misma prioridad. a = 6 - 2 - 1 * 2 + 9 / (2 + 1) Si puedes ver claramente el resultado de esa operación, entonces vamos por buen camino. No trabajarás con tantos números generalmente en TFM pero sí con muchas operaciones y variables. Es importante que tengas en mente siempre la prioridad o el orden de las operaciones. Operador de concatenación En casi todos los lenguajes de programación encontrarás un operador de concatenación. Concatenar es unir dos o más variables para formar una sola variable de texto (cadena). Las variables que vas a unir no necesariamente tienen que ser dos cadenas, puede ser una cadena y un número o incluso dos números. Pero vamos a los ejemplos para que veas lo que hace la concatenación. En Lua se concatena usando como símbolo dos puntos seguidos: .. texto = "Tigrounette tiene " .. 45 .. " quesos." -- resultado: "Tigrounette tiene 45 quesos." El símbolo .. ha unido tres valores en una sola variable de texto. El primero es una cadena ("Tigrounette tiene "), el segundo es un valor numérico (45) y luego otro texto ("quesos."). ¿Pero y no habría sido más fácil y rápido guardar directamente el texto completo dentro de la variable? Sí. Es por eso que ahora pondremos un ejemplo mucho más realista y práctico para los minijuegos: texto = name .. " tiene " .. quesos .. " quesos." -- resultado: "Hinakagiyama tiene 234 quesos." Aquí ya no estamos usando números, sino que usamos la variable numérica quesos en la que deberíamos tener guardado un número de quesos (por ejemplo, 234). Y también usamos una variable para poner el nombre del jugador, name. El símbolo de concatenación está uniendo una variable de texto con otro texto, una variable numérica y luego otro texto. Como puedes darte cuenta, la concatenación es especialmente útil para mostrar mensajes, aunque le daremos varios usos más a lo largo de nuestra experiencia con lua y la API. Existen más operadores además de los mencionados aquí. Hay operadores lógicos y operadores relacionales, pero los veremos más adelante cuando estudiemos las sentencias condicionales. Capítulo 1 Hasta ahora a lo largo del tutorial hemos trabajado con varias funciones de la API, como setShaman, giveCheese o killPlayer. A todas estas funciones le hemos entregado ciertas variables, a las anteriores tres por ejemplo le entregamos el nombre de un ratón. En este capítulo aprenderemos más sobre esas variables, cómo funcionan y cómo las usaremos. Esos valores que entregamos a una función se llaman argumentos. Cada función en TFM y en lua tiene definida una serie de parámetros, que indican la clase de valores que debemos entregarles como argumentos. Analicemos la función killPlayer, según la documentación:
La función solo tiene un parámetro, playerName. Los parámetros de una función siempre nos indicarán algo específico que tenemos que entregarle a la hora de utilizarla. En este caso es el nombre del jugador a matar. Ahora, cuando llamamos a la función: tfm.exec.killPlayer("Hinakagiyama") Estamos entregándole como argumento el nombre de mi rata. Básicamente para entender la diferencia entre parámetro y argumento: una función tiene una serie de parámetros que te indica los argumentos que tienes que entregarle, qué son, cómo deben ser y para qué sirven. Para conocer los parámetros de una función, el programador debe dirigirse a la documentación de la API y buscar la función para informarse sobre su uso. En el caso de las funciones propias del lenguaje Lua, tendría que dirigirse al manual de referencia de Lua. Ahora has aprendido a entender la documentación, por lo que quizás puedas aventurarte a probar por tu cuenta algunas funciones, leyendo sus parámetros para saber qué argumentos tienes que entregarle. Los parámetros suelen ser de un tipo de datos. Los tipos de datos no los hemos mencionado pero hemos estado trabajando con ellos: los números son de tipo Int, los textos son String. También tenemos el tipo Boolean que consiste en valores verdadero o falso (true y false) y también está el tipo Table. Más adelante analizaremos mejor estos tipos. La idea de los tipos en los parámetros es que te indican qué tipo de argumento debes entregarle a la función. En el ejemplo anterior, killPlayer tiene un parámetro de tipo String (cadena). Por lo que no le puedes entregar un argumento de tipo Int, como tfm.exec.killPlayer(5). En otros lenguajes de programación las variables suelen tener un tipo fijo que no se puede cambiar. Por lo que si tienes una variable quesos = 5, no puedes decidir de pronto guardar un texto en la variable quesos. Sin embargo en Lua técnicamente no existen los tipos y todas las variables pueden poseer cualquier tipo de valor. Por lo que puedes tener una variable en la que almacenes distintos tipos de datos a lo largo del script. Esta es una de las cosas que hacen de Lua un lenguaje muy versatil y flexible. Ahora veremos un ejemplo con varios parámetros. La función addShamanObject que vimos algunos capítulos atrás: tfm.exec.addShamanObject(33, 400, 200) La sentencia anterior pondrá un pollo (el objeto de chamán usado como consumible) en medio de la pantalla (coordenadas 400,200 es más o menos el centro de un mapa de tamaño normal). Si analizamos los parámetros que usa la función mirando la documentación, verás que addShamanObject recibe muchos más argumentos de los que nosotros hemos puesto. Tiene 7 parámetros y solo le hemos pasado 3 argumentos. Cuando hacemos esto, lo que hace la API es recibir esos 3 argumentos, y el resto los deja con su valor por defecto (default). En este caso, los parámetros que nos han faltado son angle, xSpeed, ySpeed y ghost, que tienen valores por defecto 0, 0, 0, false, como puedes ver en la documentación. Siempre que una función tenga valores por defecto, estos los puedes omitir si no necesitas cambiarlos. Así te ahorras escribir la función completa, que sería esto, lo cual es equivalente a la línea anterior: tfm.exec.addShamanObject(33, 400, 200, 0, 0, 0, false) Te ahorrarás unos cuantos caracteres de código enviando solo los argumentos que necesites, siempre y cuando los parámetros tengan valor por defecto. Ten en cuenta que los parámetros tienen un orden y no puedes enviar los argumentos en el orden que quieras ni saltarte parámetros, solamente puedes omitir los que estén al final. En otros lenguajes existen formas alternativas de saltarse parámetros porque tienen tipos de datos, pero en Lua no existen estos tipos así que no puedes saltarte argumentos. Ahora bien, ¿qué hacen estos parámetros? ¡Puedes verlo tú mismo en la documentación! El primer parámetro es el id del objeto de chamán que queremos invocar, en este caso el 33 corresponde a un pollo. Luego vienen las coordenadas donde quieres poner el objeto, como dijimos antes, 400,200 es el centro del mapa. Luego viene el ángulo en que quieres que aparezca el objeto (0 equivale a sin rotación), velocidades x e y del objeto (0 hará que el objeto aparezca estático, aunque luego se pueda mover por efecto de gravedad, viento o fuerza), y ghost nos permite invocar el objeto fantasma/transparente (poniendo true como argumento). Puedes experimentar más con addShamanObject ahora que conoces mejor sus parámetros. Ejemplo popup Algunas funciones tienen parámetros que aceptan ciertos valores para hacer ciertas cosas. A veces es un número que ayuda a escoger cierta configuración o a veces podemos entregar un valor específico para que haga algo. En la API de TFM uno de estos casos muy común es el de entregar nil a ciertas funciones para que la acción se realice para todos los jugadores. A continuación se explicará mejor esta idea. ui.addPopup ( id, type, text, targetPlayer, x, y, width, fixedPos ) addPopup nos permite mostrar una pequeña ventana o cuadro 'popup' en la pantalla. Existen 3 tipos de popups: cuadros de mensaje simples con un botón, cuadros con botones "sí" y "no" y cuadros de entrada de texto donde el jugador podrá escribir un texto y enviarlo. Para escoger entre esos tres tipos, la función usa el parámetro type, donde podemos ingresar como argumento los valores 0, 1 o 2. Cada valor corresponde a los tres tipos antes mencionados. El cuadro popup se mostrará en la pantalla del jugador que indiquemos en el parámetro targetPlayer. Pero si queremos enviar el cuadro a la pantalla de todos los jugadores, podemos entregar nil como argumento.
Para poner en práctica lo anterior, intenta cargar este script en una sala con varios ratones: ui.addPopup(0, 1, "¿Te gusta el queso?", "Hinakagiyama", 300, 100, 200, true) Recuerda cambiar el nombre de mi rata por el tuyo. El script enviará el cuadro a tu pantalla pero el resto de jugadores no podrá verlo. Si quisieras enviar el cuadro a todos, podrías repetir la sentencia varias veces con el nombre de cada jugador, pero para eso podemos usar la utilidad que nos entrega la función de pasarle el valor nil para enviar el mensaje a todos: ui.addPopup(0, 1, "¿Te gusta el queso?", nil, 300, 100, 200, true) Solo con esa línea de código el mensaje se mostrará a todos los jugadores. Son varias las funciones en TFM que te permiten entregar nil para usar la función con todos los jugadores, como addTextArea, displayParticle, showColorPicker, entre otras. En unos capítulos más se explicará cómo darle una utilidad a estos mensajes de "Sí" o "No", haciendo que ocurra algo cuando el jugador conteste a la pregunta. Ahora ya sabes qué son los parámetros de una función y cómo saber qué argumentos debes entregarle. También deberías ser capaz de entender la documentación por tu cuenta (recuerda que Thetroz tiene la documentación traducida al español para tu comodidad). Además debes saber omitir los últimos argumentos que no necesites y saber utilizar ciertos parámetros que reciben argumentos especiales como el anterior targetPlayer que acepta el argumento nil. Entender el uso de las funciones de la API es una de las cosas más importantes para poder hacer todo lo que tenga que ver con TFM. Y saber entender por tu cuenta la documentación te ayudará muchísimo. Capítulo 2 Pero qué son todas esas palabras raras. Primero, una sentencia es una instrucción que le damos a un programa, generalmente una línea de código en nuestros scripts es una sentencia, o una llamda a una función. Las estructuras de control son herramientas que te permiten controlar el flujo de un programa. En nuestros scripts lua las instrucciones se van ejecutando por orden desde la primera línea hasta el final, pero para controlar el orden o el flujo de las cosas, usamos estas herramientas. Expresión condicional La sentencia IF básicamente te permite hacer determinadas acciones según se cumpla una cierta condición. Esta condición puede ser que el jugador sea chamán, que el tiempo sea igual a cero, o simplemente que un número sea mayor que otro. if cheese == 0 then print("¡Se acabaron los quesos!") end Para entender esto vamos por partes. La sentencia IF tiene la siguiente estructura: if <condición> then <bloque de instrucciones> end Primero tenemos una <condición>. Una condición es una expresión que Lua evalua como verdadera o falsa. Si la condición se cumple (verdadera), se ejecutará el código que hay en el bloque de instrucciones. El bloque comienza luego de la palabra clave then y termina con el cierre end. Si volvemos a mirar el ejemplo anterior, veremos que nuestra condición es la siguiente: quesos == 0 Esto quiere decir que la condición es que los quesos sean iguales a cero. Aquí tenemos un operador nuevo y es el de doble signo igual. En Lua == es un operador de comparación, que verifica que las variables o valores a ambos lados del signo sean iguales. Si son iguales, devolverá verdadero, en caso contrario el resultado será falso.
Luego de eso se ejecuta nuestro bloque de instrucciones que solo consiste en una línea para imprimir el mensaje. Ahora, ¿qué pasa si quieremos imprimir otro mensaje en caso de que sí queden quesos? IF, ELSE, ELSEIF Retomemos el ejemplo anterior y modifiquémoslo para mostrar el segundo mensaje: if cheese == 0 then print("¡Se acabaron los quesos!") else print("Aún quedan quesos.") end Ahora hemos expandido la sentencia IF añadiendo la palabra clave ELSE, que nos dirige a otro bloque de instrucciones que se ejecutará cuando la condición no se cumpla. Es decir que podríamos escribir lo anterior como pseudo-código de la siguiente forma: SI hay cero quesos ENTONCES <bloque> SI NO <bloque> Nótese que todo lo que está "dentro" de IF o de sus secciones ELSE lo estamos espaciando un poco agregando espacios o tabs al comienzo de la línea, esto se llama indentación y es sumamente importante para mantener el orden en el código y saber dentro de qué bloque está cada cosa. En otro capítulo hablaremos más sobre la indentación. Hay una última pieza del bloque IF, y son los bloques opcionales ELSEIF que nos permiten evaluar más de una condición. Iremos directo a un ejemplo: if time == 60 then print("¡Queda 1 minuto!") elseif time == 10 then print("¡Se acaba el tiempo!") elseif time == 0 then print("¡Tiempo! Se ha terminado la ronda") end Ahora el bloque cuenta con varios ELSEIF además del IF inicial. Estos cumplen básicamente la misma función pero se evalúan solo cuando la condición anterior no se ha cumplido. Es decir, si la condición en el primer IF es falsa, se salta al siguiente ELSEIF, y si esta condición tampoco se cumple, se salta al siguiente, y así. Nótese que en cada condición hemos evaluado el valor de time, pero podríamos evaluar cosas completamente diferentes en cada condición. Por último, podemos añadir también un ELSE al final de nuestro bloque IF...ELSEIF: if key == _up then print("Has pulsado la tecla arriba.") elseif key == _down then print("Has pulsado la tecla abajo.") elseif key == _space then print("Has pulsado la tecla espacio.") else print("Has pulsado otra tecla.") end El ejemplo se debería entender por sí mismo. Este bloque IF está comprobando la pulsación de teclas. Si detecta que se ha pulsado la tecla arriba, mostrará el mensaje correspondiente. Si no, se salta al siguiente elseif y comprueba si se ha pulsado la tecla abajo, si no vuelve a repetir el proceso con espacio. Por último, si no se ha cumplido ninguna de las condiciones anteriores, entonces se ejecuta el bloque ELSE, mostrando el mensaje de que se ha pulsado otra tecla. El bloque IF es quizás la herramienta más fundamental de todas en el control del flujo de nuestros programas. Es lo que nos ayuda a que el script tome decisiones y ejecute unas instrucciones en lugar de otras. Nos permite evaluar condiciones y analizar lo que ocurre con nuestro juego. Un ejemplo de ello es lo que acabamos de ver, pulsación de teclas. Operadores relacionales Si estuviste en el capítulo 1 recordarás que mencionamos estos operadores. En los ejemplos anteriores trabajamos con el operador ==. Este es un operador relacional o de comparación que en este caso evalúa igualdad. Ahora hablaremos sobre otros operadores. Estos son todos los operadores de comparación en Lua:
Todos estos operadores comparan el valor a su izquierda con el valor a su derecha y, siguiendo el criterio mostrado en la tabla, devuelven verdadero o falso según se cumpla. Ahora hablaremos un poco sobre lo que significa verdadero y falso. En capítulos atrás hablamos sobre los tipos de datos. Los valores o variables pueden ser de tipo numérico, decimal, cadenas o incluso tablas. Y también están los valores booleanos (Boolean). Un booleano solo puede tener dos valores: true o false. Los operadores de la tabla reciben dos valores cuales quiera (a ambos lados del signo) y como resultado entregan un boolean que será true si la comparación se cumple y false en caso contrario. Puedes ver y comprobar todo esto usando un print para mostrar directamente los valores booleanos: bool = 5 > 2 print(bool) Dentro de la variable bool guardaremos el resultado de la comparación (5 > 2). Cinco es mayor que dos por lo tanto el operador devolverá true. Si intentamos imprimir nuestro boolean se mostrará el texto true. Lua es inteligente para imprimir textos en pantalla y mostrará los booleans escritos como true o false. Ahora intenta el mismo ejemplo poniendo una condición que no se cumpla: bool = 40 < 1 print(bool) bool = 7 == 6 print(bool) bool = (3+1) ~= 4 print(bool) Todos estos ejemplos devolverán false. El último ejemplo primero hace una suma (está entre paréntesis y además casi todas las operaciones tienen prioridad por encima de las comparaciones), y luego compara el resultado de esa suma con el valor al otro lado del símbolo. Este es el símbolo de desigualdad, por lo tanto los valores tienen que ser distintos para que se cumpla la condición. 3+1 es igual a 4 por lo tanto el resultado es falso. Hasta ahora ya debes tener una idea más o menos clara de cómo se escriben expresiones condicionales usando estos operadores, pero hay más cosas que pueden haber en una condición que solo estos símbolos. Operadores lógicos ¿Qué pasa si quiero comparar más de un número o evaluar más de una condición dentro de una misma expresión? ¿O si quiero comprobar que una condición se cumpla y otra no? Aquí es donde entra la magia de los operadores lógicos. Solo existen tres operadores lógicos: and or not Estos operadores hacen lo que su nombre dice. Al igual que los operadores anteriores, siempre devuelven true o false. AND devolverá true cuando ambas expresiones a sus lados sean verdaderas OR devolverá true cuando al menos una de las expresiones a sus lados sea verdadera NOT devolverá true cuando la expresión a su derecha sea falsa Todo esto se entiende mejor con ejemplos y teniendo en cuenta el significado de los operadores: bool = 3 < 5 and 20 < 30 print(bool) -- resultado: true bool = 3 < 5 and 2 == 5 print(bool) -- resultado: false En el último caso, and devolverá false porque la segunda expresión es falsa, y deben cumplirse ambas condiciones para que and devuelva verdadero. bool = 5 == 6 or 5 == 5 print(bool) -- resultado: true bool = 22 > 0 or 41 > 0 print(bool) -- resultado: true bool = 2 > 30 or 1 ~= 1 print(bool) -- resultado: false Esta vez solo hace falta que una de las expresiones sea cierta. En el primer la segunda condición es verdadera por lo tanto devuelve true. En el segundo caso ambas son verdaderas lo cual también devuelve true. En el último caso ambas son falsas por lo tanto el resultado es false. bool = not 4 == 5 print(bool) -- resultado: true bool = not true print(bool) -- resultado: false Primero tenemos 4 == 5, lo cual es falso. El operador not invierte el resultado de la evaluación, convirtiéndolo en true. En el segundo caso tenemos true, y el operador not lo convierte el false. El operador not trabaja con solo 1 expresión a su derecha. Puedes decir que es como el signo menos de los operadores lógicos. Como viste en el último ejemplo, podemos usar directamente las palabras true y false dentro de nuestras expresiones. La magia de los operadores lógicos no termina aquí. Puedes utilizar varios operadores en una misma expresión, creando condiciones como las siguientes y usarlas en bloques IF. Espero que se autoentiendan: if time == 0 or mice == 1 then print("Se ha terminado la ronda.") end if vidas > 0 and (key == _espacio or key == _s) then disparar(name, x, y) end if comando == "ban" and not admin[name] then print("Debes ser admin para poder usar este comando.") end Quizás veas algo nuevo en el último ejemplo. Más adelante veremos qué es esto, cuando hablemos sobre las tablas, una de las cosas más útiles, versátiles y quizás también complicadas de Lua. Si has entendido todo hasta aquí en este largo capítulo, ya tendrás una idea bastante buena de como se manejan los operadores lógicos y relacionales, y de cómo armar condiciones con ellos. Además de usarlas en bloques IFs que es donde más las usarás. En el siguiente capítulo haremos un ejemplo práctico donde usaremos todo lo que hemos visto en este capítulo, o casi todo, creando algo jugable. (spoilers: ratas voladoras que explotan) Cosas que se quedaron fuera y veremos más adelante:
Capítulo 10 Una de las cosas más esenciales para todo minijuego que pretendamos hacer es un sistema de rotación de mapas, que vaya poniendo un nuevo mapa al finalizar una ronda según unos criterios definidos por nosotros. Algunas cosas que encontramos normalmente en una rotación de mapas:
Cambio de mapa al no quedar ratones vivos Los mapas no se repiten dos veces seguidas Comenzaremos haciendo la base de nuestro script con una función main y una función startGame: function main()
startGame() function startGame() end main() Primero desactivamos el cambio automático de mapa, porque queremos controlar la manera en que se escoge el siguiente mapa. Si no hacemos esto los mapas cambiarán automáticamente y no nos permitirá tener nuestra propia lista de mapas a cargar. Luego llamamos a una función startGame() que usaremos para iniciar una nueva ronda. En esta función es donde controlaremos la carga del mapa. Ahora viene lo importante, los mapas. Para este tutorial haremos un minijuego que solo cargue mapas de Pokémon. Lo primero que haremos será añadir una lista de mapas dentro de una tabla, luego añadiremos un poco de código a nuestra función startGame para que cargue un mapa de esa lista: local maps = {1810386,1864223,1729924,2372033,1700658,1484328,2706423,1738989,3295997} function main()
startGame() function startGame()
newMap = math.random(#maps) tfm.exec.newGame(maps[newMap]) main() Nótese que en los mapas no hace falta añadir el @ ni usar comillas puesto que son valores numéricos. Además también pueden usarse números de mapas vanilla como 0, 1, 200, 15, 61, etc. En nuestra función startGame primero generaremos un número al azar entre 1 y el número de mapas (#maps). Y luego usaremos tfm.exec.newGame para cargar el mapa. Para entregarle a newGame el código del mapa simplemente utilizamos la tabla maps y le entregamos como índice el número que acabamos de generar. Por ejemplo si tenemos 9 mapas, nuestra función generará un número al azar entre 1 y 9. Si escogiera el 6, luego cargaría el mapa maps[6], que en este caso es 1484328. Si pruebas ahora el script verás como se carga un mapa al azar entre nuestra lista de mapas de Pokémon. Sin embargo ese será el único mapa que verás porque aún no hemos hecho que se termine la ronda y se cargue el siguiente mapa. Para eso ahora crearemos una función endGame: local maps = {1810386,1864223,1729924,2372033,1700658,1484328,2706423,1738989,3295997} function main()
startGame() function startGame()
newMap = math.random(#maps) tfm.exec.newGame(maps[newMap]) function endGame()
function eventLoop(t, tr)
main() Nuestra función endGame simplemente llamará a startGame para iniciar una nueva ronda. La razón para tener una función endGame es que en algún momento del desarrollo de cualquier minijuego vas a necesitar hacer cosas adicionales a la hora de finalizar la ronda (mostrar un mensaje con el ganador, borrar variables, aumentar stats, etc.). También añadimos eventLoop para controlar el tiempo. Como vimos al comienzo una de las cosas que buscamos es que se cambie el mapa cuando se acabe el tiempo. Para esto simplemente comprobamos que el tiempo restante (tr) sea menor o igual a cero, si es así llamamos a endGame para que finalice la ronda y se repita el ciclo. Ahora tienes un script que continua poniendo mapas de Pokémon en cada ronda. Pero solo se cambia el mapa al acabarse el tiempo, también debe cambiar cuando se acaben los jugadores vivos. Para controlar la cantidad de jugadores vivos, crearemos una variable en donde guardaremos este valor. Debemos contar la cantidad de jugadores al inicio de la ronda y restarle 1 cada vez que alguien muera o entre a la madriguera: local maps = {1810386,1864223,1729924,2372033,1700658,1484328,2706423,1738989,3295997} local playersAlive function main()
startGame() function startGame()
newMap = math.random(#maps) tfm.exec.newGame(maps[newMap]) function endGame()
function eventNewGame()
for _ in pairs(tfm.get.room.playerList) do
function eventPlayerRespawn(name)
function eventPlayerDied(name)
function eventPlayerWon(name)
function eventLoop(t, tr)
main() Primero definimos la variable. Luego dentro de eventNewGame la inicializamos a cero y contamos los jugadores, para que se haga al inicio de cada ronda. Le restamos 1 dentro de eventPlayerDied y eventPlayerWon, para que se reduzca cuando los jugadores mueran o entren a la madriguera. No hace falta hacer esto para los jugadores que dejen la sala (eventPlayerLeft) puesto que al hacerlo automáticamente morirán. También tenemos que sumarle 1 cuando alguien reviva (eventPlayerRespawn), en nuestro caso esto es necesario porque el chamán puede revivir usando una habilidad. Ahora añadiremos una función que revise si quedan cero jugadores para que termine la ronda: local maps = {1810386,1864223,1729924,2372033,1700658,1484328,2706423,1738989,3295997} local playersAlive function main()
startGame() function startGame()
newMap = math.random(#maps) tfm.exec.newGame(maps[newMap]) function endGame()
function eventNewGame()
for _ in pairs(tfm.get.room.playerList) do
function eventPlayerRespawn(name)
function eventPlayerDied(name)
checkPlayers() function eventPlayerWon(name)
checkPlayers() function checkPlayers()
function eventLoop(t, tr)
main() Esta función la llamaremos dentro de eventPlayerDied y eventPlayerWon, para que se compruebe cada vez que baje la cantidad de jugadores vivos. La función simplemente comprueba que el valor sea igual a cero (nunca debería bajar más abajo de cero). Llegado a este punto ya tienes una rotación de mapas funcionando. ¡Felicidades! Pero aún falta un pequeño detalle. Si tienes suficiente suerte (o mala suerte) te encontrarás con que tu script carga el mismo mapa consecutivamente. Esto es posible debido a que solo le indicamos que cogiera un mapa al azar, pero no le dijimos que escogiera uno distinto al actual. Para lograr esto crearemos una variable en donde guardaremos el índice del mapa actual (en este caso, un número del 1 al 9). Y a la hora de escoger uno nuevo al azar, comprobaremos que no sea igual al mapa actual que guardamos anteriormente. Solo modificaremos startGame y añadiremos la variable al comienzo: local maps = {1810386,1864223,1729924,2372033,1700658,1484328,2706423,1738989,3295997} local currentMap local playersAlive function startGame()
repeat
currentMap = newMap tfm.exec.newGame(maps[newMap]) Lo que hará startGame ahora será básicamente seguir generando números al azar hasta que salga uno diferente al índice del mapa actual. Luego de eso guardará ese número dentro de currentMap para poder repetir el proceso en la ronda siguiente. Con eso ha terminado el tutorial para una rotación de mapas básica. Ahora tienes un minijuego estilo tradicional de Transformice con mapas de Pokémon solamente. A continuación tienes el código resultante completo: Rotación de mapas Espero que te haya servido. A partir de esta rotación base puedes elaborar todo tipo de minijuegos. Más adelante intentaremos modificarlo para crear un script de bootcamp y también un racing. Dernière modification le 1458688560000 |
1 | ||
*pwet* (reservo) |
![]() ![]() « Citoyen » 1410201120000
| 0 | ||
Muy buena idea, espero que les sirva a muchos usuarios (incluyendome) Hinakagiyama a dit : Me encantó el lema Suerte :) Dernière modification le 1410201540000 |
![]() ![]() « Citoyen » 1410279300000
| 1 | ||
Este hilo me esta ayudando mucho a entender lua. Muchas gracias. |
0 | ||
Por fin un buen tutorial de lua. Ya que todo el resto de tutorial, bueno "tutorial" eran solo copy y paste. Con copy paste no se aprende mucho. Capaz ahora si me ponga a hacer cositas con lua. |
![]() ![]() « Citoyen » 1410543060000
| 2 | ||
El pueblo pide las siguientes partes :( |
![]() ![]() « Censeur » 1410565740000
| 0 | ||
Leer todo y sentir que nunca has aprendido lo necesario sobre Lua. Pues, ¡felicitaciones por el tema! Esperaré los próximos temas. |
![]() ![]() « Consul » 1410566100000
| 0 | ||
Muy buen hilo, aunque no pueda usar lua, me parece genial un tutorial asi :] |
![]() ![]() « Citoyen » 1410895440000
| 0 | ||
Mis dieses al creador. |
![]() ![]() « Citoyen » 1411102680000
| 0 | ||
Genial tema, esperaré las siguientes partes! |
![]() « Citoyen » 1412890080000
| 0 | ||
Buen Hilo ,Siempre Sorprendiendo xD |
0 | ||
Apenas repaso el 1 por que no entiendo tanto el lua ... pero cuándo tenga un acompañante, lo podré hacer sin ningún problema ^^. Trataré de entender el capítulo 1 y luego empiezo con el 2. Gran tema |
![]() ![]() « Citoyen » 1413571140000
| 0 | ||
Muchas gracias, estaba buscando un tutorial simple y bien explicado y pues lo he encontrado. Avanza con los capítulos, adoro la manera que explicas. Felicidades ^^ |
![]() ![]() « Citoyen » 1436641320000
| 0 | ||
Esto me ayuda a hacer un script, o a ver pokemons? .-. |
![]() ![]() « Citoyen » 1436641920000
| 0 | ||
¡Excelente tutorial! Espero los próximos cápitulos. Barohe a dit : +1 |
![]() ![]() « Censeur » 1439410380000
| 0 | ||
creo que es muy basico deberias continuar el tutorial :v |
![]() ![]() « Citoyen » 1443223440000
| 0 | ||
Continua el tutorial :D, aprendi mucho |
![]() ![]() « Citoyen » 1447352040000
| 0 | ||
Muchas gracias, estoy desarrollando #VampireRace y la verdad este hilo me ha servido mucho. |
![]() « Citoyen » 1448116740000
| 0 | ||
exijo la siguiente parte. Explicas bien ~ |
![]() ![]() « Citoyen » 1448991360000
| 0 | ||
Se los comandos ,tambien como modificarlos pero nose como ordenarlos o terminarlos!!! e empieze un nuevo codigo |