[Guía] tfm.get - Problemas y soluciones |
1 | ||
Problemas con tfm.get tfm.get es un conjunto de funciones que te permiten obtener información específica sobre los objetos, los ratones, el mapa, entre otras coas. Todo eso suena genial y muy útil pero el problema con tfm.get es que los datos que entregan se actualizan cada cierto tiempo, por lo que hay un retraso y un desfase en la información que obtenemos. Otro problema con tfm.get es que consume una cantidad sorprendentemente alta de tiempo de ejecución, es decir, es lento. Una buena forma de optimizar nuestros scripts es evitando llamar a tfm.get lo más posible. En este hilo demostraré estos problemas en varios casos de ejemplo e intentaré entregar soluciones. Posición del ratón Code Lua 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function main() Este script mostrará un cuadrado alrededor de tu rata cada vez que hagas click en la pantalla. Si haces click sin moverte parece muy preciso y perfecto pero si tu ratón se está moviendo mientras haces click (o si acaba de moverse), el cuadrado ya no aparecerá en el lugar correcto. Solución Una solución práctica es utilizar las teclas en lugar del mouse. ¿Pero eso qué tiene que ver? El problema es que eventMouse solo entrega las coordenadas del puntero entre sus parámetros, mientras que eventKeyboard te entrega las coordenadas de tu ratón (¡el de Transformice!). Estas coordenadas se entregan en tiempo real, o sea que no hay un desfase en la posición del ratón al momento de pulsar la tecla. Por lo tanto nuestro script nuevo quedaría así: Code Lua 1 2 3 4 5 6 7 8 9 10 11 function main() ¿Está vivo? Algo que siempre veo en los scripts de la gente es lo siguiente: Code Lua 1 2 3 4 5 6 7 8 9 10 11 function eventPlayerDied(name) Para empezar el script está bastante mal optimizado porque mientras recorremos el ciclo for nos basta con que un ratón esté vivo para saber que no es momento de terminar el juego aún, por lo que nos ahorraríamos mucho runtime (tiempo de ejecución) haciendo esto: Code Lua 1 2 3 4 5 6 7 8 9 10 11 12 function eventPlayerDied(name) Esto tiene la enorme diferencia de que al momento de encontrar un ratón vivo (not mouse.isDead) inmediatamente detendrá el ciclo (break) en lugar de seguir desperdiciando runtime revisando los demás ratones. Luego de eso simplemente comprueba que se haya marcado el flag "someoneAlive", y si está intacto entonces es porque nadie está vivo, y acabamos el juego. Sin duda optimizamos un montón el script con ese sutil cambio, pero... aún podemos optimizarlo más. La función tfm.get sigue siendo muy costosa para estar llamándola cada vez que se nos muera un ratón y además tener que hacer un ciclo que a medida que más ratones mueran tendrá que revisar más ratones para determinar si la ronda debe seguir o no. Y si estamos en un juego en donde los ratones tienen más de una vida nuestro script además tendrá que revisar las vidas restantes y luego hacer todo lo anterior. Imagina todo el runtime que gastará con todo eso. Solución Afortunadamente hay una forma muy obvia y sencilla de deshacernos completamente de tfm.get en este ejemplo. Y es mantener manualmente un contador con la cantidad de ratones vivos en la sala. Necesitamos simplemente estas cosas: Code Lua 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 local playersInRoom = 0 Cada vez que entre alguien a la sala, sumamos 1 a playersInRoom. Cada vez que alguien salga, le restamos 1. Al iniciar una ronda playersAlive = playersInRoom, porque todos están vivos al comienzo. Al morir alguien, se le resta 1 a playersAlive y luego se comprueba si es que están todos muertos. Así de simple y directo. Esto es TODO lo que se hace cuando alguien muere, no desperdiciamos tiempo llamando a tfm.get ni nada. Una simple suma y una comparación. Utilizando este método maximizas el rendimiento de tu script y además tienes mayor control sobre los datos de tus jugadores. Si necesitas más información sobre los ratones también puedes usar además una tabla isAlive[] que guarde el estado independiente de cada ratón. Así puedes saber si yo estoy vivo usando isAlive["Hinakagiyama"] en cualquier momento. Como se puede ver, hemos optimizado y hecho más flexible nuestro script utilizando dos variables que controlaremos manualmente en lugar de depender de tfm.get. Una vez más, punto menos para tfm.get. Nota: algo que es importante mencionar del script anterior es que si vas a usarlo en una sala debes completarlo con un pequeño ciclo por playerList solamente al comienzo del script, para llamar a eventNewPlayer para los ratones que ya se encuentran en la sala al cargar el script: Code Lua 1 2 3 for n in pairs(tfm.get.room.playerList) do Dirección del ratón Para hacer cualquier tipo de script que involucre disparar cosas al estilo FFA, es necesario conocer la posición del ratón para poder disparar nuestro cañón/caja/pelota/pollo/sandwich de queso hacia la izquierda o hacia la derecha. Mucha gente hace esto usando la propiedad isFacingRight de tfm.get, más o menos así: Code Lua 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function main() Parece perfecto, en teoría, pero en la práctica estamos ante el retraso de tfm.get. Cada vez que disparemos inmediatamente luego de girar, nuestros pollos volarán hacia el otro lado porque tfm.get aún no actualiza isFacingRight. Si mueves a tu rata para ambos lados rapidamente mientras disparas, notarás lo malo y poco preciso que resulta esto para un juego de disparos. Solución No usar tfm.get (a estas alturas creo que esta respuesta ya es obvia). Lo que haremos es una vez más controlar manualmente las cosas. En este caso llevaremos nota de la dirección a la que está mirando cada ratón, guardando este dato en una variable y modificándolo cada vez que se pulsen las teclas de dirección (izquierda y derecha). Code Lua 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 keys = {0,2,32} Mucho mejor. Ahora el desfase es casi nulo ya que no estamos trabajando con tfm.get ni con posiciones. Simplemente detectamos las pulsaciones de tecla por lo que nuestro juego detectará que pulsamos izquierda o derecha antes de que alcancemos a disparar. Además de ser más preciso, hemos optimizado el código una vez más. Está claro que no es una buena idea usar isFacingRight. isShaman, isVampire, hasCheese, etc. Por último también mencionaré brevemente este tipo de propiedades de tfm.get. A primera vista parecen una fácil y conveniente manera de chequear si un ratón específico es chamán o cumple alguno de los otros criterios. Pero solo es una manera lenta y costosa de obtener información. Quizás no sea la gran cosa por sí sola, revisar una vez si un ratón tiene queso o si es chamán. ¿Pero y si tienes que hacer eso cada medio segundo? (eventLoop) ¿O si tienes que hacerlo cada vez que alguien pulse una tecla? (eventKeyboard) ¿Y con 20+ ratones en la sala? tfm.get consume runtime, y en juegos complejos que tienen que hacer varias cosas todo el tiempo, no puedes permitirte gastar tiempo en llamar a la función, especialmente en tareas que realices dentro de eventLoop o de eventKeyboard, porque se ejecutarán muchísimas veces por segundo. Solución Para el caso de isShaman, puedes simplemente usar una variable shaman y guardar ahí al chamán al inicio de la ronda (según tu minijuego incluso puedes escoger manualmente al chamán). isVampire lo puedes reemplazar por una tabla isVampire[name] donde guardes un boolean para cada ratón y lo modifiques dentro de eventPlayerVampire. Y lo mismo puedes hacer con hasCheese y modificarlo en eventPlayerGetCheese. Si haces un juego con varios chamanes también puedes usar una tabla. Al momento de comprobar es donde todo se optimiza. Porque en vez de llamar a tfm.get.room.playerList[name].isShaman, simplemente usarías un simple if name == shaman then y ese es todo el proceso. Conclusión Como podemos ver en todos estos ejemplos. El mejor favor que podemos hacernos para optimizar nuestros scripts es evitar tfm.get. Utilizando variables propias y controlando manualmente el estado del juego y los ratones. No solo maximizaremos el rendimiento, también haremos el código más corto o eficiente y más flexible. Esto se puede extender más e incluso podríamos controlar el puntaje de los ratones en variables separadas (si nuestro juego necesitara por ejemplo revisar constantemente el puntaje de los ratones, podemos guardarlo nosotros en lugar de usar tfm.get y score). Hay casos puntuales en donde usaremos tfm.get. Por ejemplo siempre lo usaremos al comienzo de nuestro script (o en nuestra función main si eres como yo) para llamar a eventNewPlayer para todos esos ratones que ya se encontraban en la sala al momento de cargar el script, como mencionamos antes. También lo usaremos casi sin alternativas para determinar la posición de los objetos (a menos que desees usar la física para calcular la posición de una pelota como hace Makinit en #football) o para obtener datos puntuales en donde no estaremos llamando a la función constantemente, como los datos sobre el mapa al inicio de la ronda (xmlMapInfo). Evitar tfm.get no es la única manera de optimizar un script, en un futuro quizás explique algunos otros asuntos problemáticos con el runtime y otros métodos y algunos de mis trucos para el rendimiento. Por el momento espero que esto ayude y sirva de consejo para tener un mejor código y seguir mejores prácticas para sus futuros minijuegos y proyectos. ¡Buena suerte! Dernière modification le 1481299200000 |
![]() « Citoyen » 1448990820000
| 0 | ||
No entendí algo muy bien, quieres decir que el tfm.get es como el eventLoop > que se actualiza cada X tiempo? Yo pensaba que era como un timer que estaba actualizado siempre... Tema muy útil |
0 | ||
Thetroz a dit : Sí, se va actualizando pero se tarda un poco. |
![]() ![]() « Citoyen » 1449154080000
| 0 | ||
Dios, pedazo de guía! Hace algún tiempo me comentaste que el eventKeyboard se actualiza más rápido que la misma tabla tfm.get y no entendí muy bien eso, ahora que leo esto ya se me aclararon todas las dudas. Cabe destacar también que si quieres tener un código óptimo puedes seguir estos consejos y también evitar la repetición de funciones o básicamente encontrar una manera de hacerlo más sencillo y con menos runtime. __ Aún me queda una pregunta. ¿De qué servirá el lowerSyncDelay entonces? |
0 | ||
Eliaseeg a dit : Honestamente, no lo sé. He estado probando a ver si mejora el retraso de tfm.get o las coordenadas que entrega eventKeyboard pero no hay diferencia práctica. Tengo que preguntar al respecto, es posible que solo se pueda usar en modulos oficiales o algo, o lo estoy usando mal. Quería mencionarlo en el hilo pero hasta que no sepa bien cómo funciona, lo dejaré ahí. |
![]() ![]() « Censeur » 1449161820000
| 0 | ||
Muy buen hilo y bien explicado, espero que sigas con guías y tutoriales sobre lua. Gracias por ayudar a la comunidad :P |
![]() ![]() « Citoyen » 1615653900000
| 0 | ||
Excelente. No tenía idea de esto. Lo aplicaré a mis futuros scripts. |