Cacharreando Jobs


Este asunto de los Jobs requería un poco de estudio, así que acudí a San Google para orientarme al respecto y me bajé un tutorial de Youtube. Resulta que estaba dando palos de ciego, como suele suceder cuando uno se mete en cosas sin estudiar de antemano todos los elementos implicados.
Comienzo con respuesta a mi duda: los Jobs no son hilos. En realidad, son operaciones (o kernels) que se ejecutan en hilos de trabajo. Aparentemente, no son una solución mágica para todo, su verdadero valor se revela cuando necesitas descargar operaciones pesadas del hilo principal, para que éste haga otras cosas. Es recomendable realizar pruebas de rendimiento para ver si en verdad, convertir algo en Job es más rentable que hacerlo en tu Update(), a la manera tradicional.
Existen un montón de tipos diferentes de Jobs. Para mí ahora solo son importantes dos: el normal y el paralelo (IJob/IJobParallelFor). El primero ejecuta una operación, el segundo, trabaja sobre un arreglo, ejecutando una operación en paralelo sobre cada elemento del mismo. O sea, podemos crear un IJobParallelFor que tome un arreglo de conexiones de clientes y envíe o lea datos de cada una de ellas, en paralelo, o hasta donde los permitan los  worker threads de Unity, que supongo dependan de los núcleos disponibles en el CPU.
De los componentes del famoso Data Oriented Technology Stack, quizás los Jobs sean los más fáciles de comprender, aunque no carecen de detalles conflictivos. Estamos hablando de código que se ejecuta concurrentemente, con posibilidades de causar todo tipo de desastres si escribimos en un mismo lugar desde dos operaciones diferentes. Hay margen de sobra para meter la pata, causar race conditions y jodernos la vida con facilidad. Veamos un ejemplo, a modo de mini tutorial:

struct Prueba: IJob
{
  public int ttt;
 
  public void Execute()
  {
     /*  Hacer algo muy complicado con ttt */
  }
}

public class LoQueSea: MonoBehaviour
{
  private JobHandle handle;

  void Update()
  {
    int t;
    var job = new Prueba() {
      ttt = t
    }
    //programar su ejecucion
    handle= job.Schedule(handle);
    /* Hacer otras cosas */
    /* Importante!! Aqui no podemos modificar las variables que el job esta usando */
    handle.Complete(); //completamos el Job
   
  }
}

Sin embargo, si creen que usar Jobs aumenta el rendimiento, esperen a combinarlos con el Burst Compiler. El resultado me hubiera hecho caerme de culo de no haber estado sentado. Basta con agregar el atributo [BurstCompile]:

[BurstCompile]
struct Prueba: IJob


Como ven, nada que un programador no pueda entender con un tutorial básico y una hora de estudio. Usarlo adecuadamente ya es otra cosa.
Para concluir, les recuerdo que pueden comprar la novela en Amazon.  Arriba, que no he logrado una venta este mes y así no podré comprarme el yate.

Cacharreando Transport 2

Ante todo, les recuerdo que mi primera novela está disponible en Amazon (desde hace años, y también la segunda), en digital y físico, incluyendo Kindle Unlimited. Los ingresos serán utilizados para financiar el desarrollo de un RPG, del cual he hablado muchísimo durante los últimos años. Considerada una de las diez mejores novelas cubanas de fantasía, me han dicho que no está tan mala. No dejen de comprarlo, o sugerir a sus amigos lectores que lo hagan. Pueden tomarlo como una especie de campaña de crowdfunding.
Entrando en el tema de hoy, al fin he conseguido un progreso apreciable en mis pruebas de multijugador. Ayer, luego de lidiar durante unos días con errores derivados de los Jobs y cosas así, logré corregir el último problema y enviar un paquete complejo con las coordenadas de un click al servidor. Aún me falta por validar si los valores interpretados en el servidor son correctos, pero no me alcanzó el tiempo para eso, pues también tenía que escribir. Supongo que algunos dirán que el avance no es tan significativo, pero si con el país paralizado, nuestro gobierno dice que creceremos, no veo por qué yo no puedo considerar esto como un gran salto para la humanidad gamer.
Queda por delante reorganziar el servidor para lidiar con múltiples clientes, beneficiándose al mismo tiempo del sistema de Jobs de Unity. Y ya que mencionamos el tema, resulta que todos estos días lo he estado considerando como un sistema de programación multihilos, pero ahora mismo no me queda muy claro. El caso es que los Jobs de Unity se inician y se concluyen con Schedule y Complete. Necesitas hacer un Complete para detenerlos y poder asignarle nuevos valores a sus variables, luego de lo cual puedes volver a echarlos a andar. Si alguien me aclara algo en este asunto le estaría agradecido. La etapa siguiente sería pasar a Netcode, que es el API de alto nvel que saldrá en algún momento de este año.
Y como tema bonus de hoy, fuera de programa, les hablaré de otro problema que me estuvo atormentando durante días. Ni en el foro oficial, ni en reddit logré encontrar la solución, que vino a mí el sábado mientras le mostraba el proyecto al resto del equipo. El lío en cuestión era que los árboles solo se veían correctamente en el centro y borde inferior de la pantalla, el resto de los árboles en el área visible se veía borroso y estático. Suponía que algún parámetro de calidad era el culpable, esto, pero no lo encontraba ni en la cámara, ni en la definición del árbol, ni en las opciones del terreno. Hasta el sábado. El responsable sí estaba en las opciones del terreno, y se trataba de la distancia a la cual los árboles son sustituidos por billboards, los cuales no se animan y se ven horribles. Incrementar el valor resolvió el asunto, y ahora todos los árboles visibles se muestran correctamente y se animan.

Cacharreando Transport

Luego de varios días de estudio y pruebas con Unity Transport, puedo decir que estoy pasando de la fase "No sé nada", a "Ahora sé mucho menos". A eso, en programación, se le llama progreso.
Como había mencionado anteriormente, Unity Transport es una API de redes de muy bajo nivel. Mis experiencias con juegos multijugador son casi nulas, así que no puedo hablar acerca de las diferencias entre una API de bajo nivel y una de alto nivel. De tanto leer por ahí, creo que una de alto nivel debería ofrecer cosas como compresión y predicción, entre otras. Y volviendo a lo mencionado anteriormente, esta nueva API de alto nivel se llama Netcode, y está al caer, pero podemos llevarnos una idea de cómo será si miramos dentro del FPS Sample. 
En conclusión, ¿qué he logrado hacer? Pues bastante poco: conectar el cliente al servidor y enviar números enteros en paquetes separados. El próximo paso es enviar paquetes de datos más complejos, compuestos por diferentes valores, como tres floats (un vector), cadenas, etc.
Técnicamente, Unity Transport no es tan complejo de usar, debido principalmente a que es muy simple. Una vez que logras rastrear los principios básicos dentro de los ejemplos, puedes implementar el envío de paquetes. Lo complicado es armar todo un protocolo a partir de ahí. Ya que hablo de ejemplos, olviden por completo los que están a la vista. Los que de verdad ayudan son los que vienen incluidos en el repo, junto con el código del paquete.

La Guardia de Mundodisco llegará a la TV

Traída por la BBC y Narrativia, esta serie estará basada en los libros de la Guardia de la Ciudad de Ankh-Morpork. El cast también trae sorpresas muy buenas, como Richard Dormer (lo recordarán de Game of Thrones) en el papel de Sam Vimes.


Una elección que me parece excelente. Sin embargo, Lara Rossi no me da la Lady Sibyll de los libros: una solterona grande y gorda.

Juegos conectados con Unity: explicando la situación actual

Hace unos días un amigo me hizo una propuesta de trabajar en un juego multijugador. Yo mismo había estado dándole vueltas a la idea de echarle un vistazo a este asunto, o sea, una masturbación mental en toda regla, y con las dos manos. La propuesta acabó por decidirme a estudiar el tema en serio.
¿Qué podía salir mal? Unity es un motor maduro, con soporte de red desde hace tiempo, con todo lo que debe llevar y esas cosas. Para mi decepción, en este momento solo hay una palabra para definir el estado de la API de conectividad en Unity: cagazón. O mejor dicho, dos palabras: gran cagazón. Vayamos por partes y empecemos con un poco de historia.
En un principio fue Unet. Y vieron los usuarios que Unet era una cagazón que no sería para nada, así que se inventaron Mirror, Photon y otras muchas soluciones que solucionaban, y de paso, le permitían a los desarrolladores alimentar a su familia (y por familia me refiero a cualquier combinacion posible de personas de ambos sexos, animales incluidos, para que no se me ofenda ningún susceptible). Y como ya esto daba un poquito de vergüenza, en Unity decidieron ponerse las pilas y darle la patada a Unet, reemplazándola con una API más joven, más bonita y más potente.
Hasta ahí y en papeles, todo bien. Pero el caso es que la patada a Unet fue demasiado rápida, la declararon obsoleta en el 2018.x, dejando el 2019.x con... nada. En el 2019 solo nos queda Photon Bolt, sin soporte para LAN (hay que pasar por caja y pagar su servicio en la nube), Forge (lo mismo), o fajarnos con Unity Transport Package, que es experimental y de muy bajo nivel. Para el tercer trimestre saldrá la famosa API más bonita y potente, llamada Netcode, también experimental.
Déjenme que les cuente algo sobre las funciones "experimentales" en Unity. Es como un ciego tirándole piedras a un gorrión. Las cosas pueden funcionar o no, o simplemente cambiar de un día para otro. Por algo son experimentales, ¿no? Y ahora les diré algo sobre los trimestres de Unity: son como los plazos que da ETECSA para cumplir sus planes. O sea, que el tercer trimestre es muy largo y Netcode podría presentarse en sociedad a finales de septiembre. O a principios de octubre. O algo así. La respuesta de Unity es que vayan resolviendo con UTP o Photon Bolt.  
Yo, por suerte, no tengo apuro. Voy a sentarme a esperar a Netcode.

Coliseum llega a los $40000

En una rara muestra de transparencia, el grupo de desarrollo detrás de Coliseum, ha revelado que sus ingresos han superado los $40 mil CUC, equivalente en cierta forma a 40 mil USD, según como lo mires. Coliseum es una especie de MOBA para móviles, el cual no he podido disfrutar apropiadamente por ser, como todo MOBA, multiplayer. Las ventas dirias han promediado unos  $162 CUC, lo cual es todo un éxito en un país acostumbrado a no pagar por el software y jugar juegos pirateados (aunque algunos sí suelen comprar estos títulos piratas en negocios que se dedican a  venderlos).
En otras circunstancias felicitaría a Vertex y todas las empresas detrás del juego, pero el caso es que tuve la imp[ertinencia de hacer una pregunta clave: de estos miles de dólares, ¿cuánto han percibido los desarrolladores? ¿Esos que están al pie del cañón, picando código? La respuesta de uno que afirma (o por lo menos da a entender) ser desarrollador, es que cero. Y hasta el momento nadie ha desmentido su afirmación.
Supongo que alguno me dirá que ninguna empresa del mundo comparte con sus trabajadores la ganancia de sus juegos, pero aquí el caso es que esos desarrolladores y artistas no ganan más de 100 dólares al mes, y quizás exagero. Si consideramos que la empresa está haciendo unos 3000 dólares al mes, y está pagando, quizás unos 600-700 dólares en salarios, ¿no creen que deberían premiar a sus empleados con algo?

Ha llegado UIElements, mis primeras experiencias

Ayer por fin hice el intento de revisar con seriedad UIElements, el nuevo sistema de interfaz gráfica de Unity que podemos utilizar para personalizar el editor. También es posible que más adelante podams usar en nuestros juegos, así que no está de más echarle un vistazo. En concreto, me urge crear un editor de diálogos para mis RPGs, y hacerlo con el sistema anterior, que data de los inicios de Unity, era una tortura.
UIElements utiliza XML, llamado UXML, y un subconjunto de CSS, llamado USS, para definir elementos y sus propiedades. Eso de por sí es una gran comodidad, pero además podemos utilizar código para crear más elementos visuales o acceder a los creados mediante XML. Dicho así, todo suena muy bien y muy lógico. El gran problema es que la documentación es más escasa que la carne de vacuno en Cuba. Recién ahora hay un par de tutoriales en Youtube que aclaran muy poco, apenas lo mínimo para entender algunas cosas y crear un par de botones.
A pesar de eso, mis inicios no fueron tan difíciles, una vez que me tomé el tiempo para revisar los dos tutoriales. Lo difícil será salir de los inicios, porque mi objetivo es algo tremendamente complejo. De hecho, tan complejo, que me conformaría con solo lograr que editar el diálogo sea un poco más cómodo que hacerlo en el Inspector de Unity.
En este caso, no se trata solo de crear una ventana, sino de personalizar el Inspector de acuerdo al elemento seleccionado para así desplazar el grueso de la edición (texto, audio, restricciones y acciones de cada nodo de diálogo) al panel del Inspector. Aún no sé cómo hacerlo, pero tengo la impresión de que es factible, con un poco de experimentación y mucho tiempo.

Más anuncios del cast de La Rueda del Tiempo

Han sido anunciados varios actores más de la serie basada en La Rueda del Tiempo. En caso de que seas un lector moderno y no tengas idea de qué estoy hablando, La Rueda del Tiempo de Robert Jordan (completada por Brandon Sanderson) era en los 90 la novela que todos los aspirantes a escritores queríamos escribir. Más o menos como Canción de Hielo y Fuego en la actualidad.
Inicialmente se anunció que Rosamund Pike interpretaría a Moiraine y si la memoria no me engaña, creo que daría el tipo. Los nuevos actores seleccionados son estos:
Josha Stradowski, como Rand al'Thor. Si tenía una idea del aspecto de Rand, era esa precisamente. Este tipo y no otro es el Rand que quiero ver, si logra imprimirle al personaje la oscuridad que lo caracteriza en las etapas finales de la historia.

Marcus Rutherford, como Perrin Aybara. Marcus es justo el Perrin que yo me imaginaba. Lo mismo, si logra caracterizar al personaje adecuadamente, no hay que pedir más.

Barney Harris será Matrin Cauthon. Al contrario de lo que dice la noticia original, edfinitivamente este no es el Mat que yo imagino. Demasiado bonitillo. El Mat que tengo en la mente es más personalidad que apariencia.

Zoë Robins interpreta a Nynaeve al'Meara. Poco puedo decir aquí, aunque en realidad, no me da el tipo, y luego veremos por qué.

Madeleine Madden, como Egwene al'Vere, otro de los ta'veren de esta historia. Esta muchacha parece haber estado esforzándose seriamente en su dieta, yo diría que demasiado. Francamente, no tiene el aspecto de una joven aldeana, al igual que la anterior. Mi idea de Egwene es una joven un poco más gordita, al igual que Nynaeve, que quizás sería un poco más baja. Tal vez yo esté equivocado, pero así son las imágenes que se crea un lector. Y  espero que eso me sirva de lección a mí mismo, para ser un poco más detallado en mis descripciones de personajes.

Rememorando viejos tiempos

Mi época de juegos libres pasó hace algún tiempo (unos años o algo así), pero he encontrado una lista increíble que me ha hecho recordar esa época. Unos 500 juegos muy bien clasificados, para que los disfruten.

La solución al problema de ayer era...

Habíamos dejado esta historia más o menos lista para el final de temporada. El protagonista (o sea, yo) se enfrentaba a un terrible enemigo: ¡el error de detección de colisiones!, que se las había arreglado para colarse en mi sistema de IA. Burlándose continuamente de todos y cada uno ed mis esfuerzos, casi había conseguido su objetivo de dominar el mundo. O algo así. Entonces, el héroe (yo mismo), acudió a su intelecto superior, previamente remojado en cerveza de los carnavales, algo que viene a ser como un agua fría, amarillenta y ligeramente amarga. Y como suele suceder en los finales de temporada, el bueno ganó.
Técnicamente, lo que hice fue comparar qué había en el proyecto que funcionaba que no hubiese en el proyecto que no funcionaba. Entonces encontré lo opuesto: algo que no había en el viejo y que sí había en el nuevo. En concreto, un Rigid Body. Ni idea de por qué está interfiriendo en las colisiones, pero así es. Una vez que eliminé el Rigid Body del GameObject del personaje, las colisiones funcionaron todo el tiempo. Tengo pendiente investigar por qué razón sucede esto, y les contaré luego si encuentro alguna respuesta.

Encabronado de verdad verdadera

Pues eso. Tego un encabronamiento, cabreo, empingue, o como quieran llamarlo, de tres pares de cojones. Qué tres pares, de tres pares y medio. He tenido que reescribir todo el sistema de IA para volver a lo mismo, que se dice fácil, pero no lo es.
La cosa es que ayer, luego de terminar de pulir detallitos, me puse a probar la nueva implementación. Para mi sorpresa, el error anterior persistía: un rato después de detectar la primera colisión, simplemente el BoxCast deja de detectar más colisiones. Vuelvo a las aplicación de prueba básica y resulta que funciona todo ok; mientras solo tenga un par de cubos en la escena, el cast detecta o no según corresponda, todo el tiempo necesario. Por tanto, puedo descartar un bug de Unity. Cuando las cosas se ponen un poco más serias, se jode el asunto bien jodido. Digan ustedes si no es como para coger un mosqueo olímpico y cagarse en los 300 mil dioses de la India.
Sería lógico pensar que tengo algún timer o algo que causa el problema, pero no. Eliminé todo lo relacionado con temporizadores y degradé el sistema a lo más básico y cercano a la aplicación de prueba, un simple "te estoy viendo/no te estoy viendo". Aún así, el error sigue.
Conclusión, que estoy metido en un lío que parece no tener solución, sin nadie a quien acudir. Ni en reddit, ni en el foro oficial he podido encontrar respuesta acerca de esto. No es un bug, o al menos no puedo probar que lo sea. Tampoco se me ocurre otra variante para detectar los personajes en el rango "visible", excepto calcular distancias y lanzar rayos a montones hacia los personajes cercanos. En este momento, solo sé a ciencia cierta unas pocas cosas: el cast funciona, lo hace en la aplicación de prueba y lo hace en un proyecto viejo. No es un temporizador en mal lugar, en el proyecto viejo los temporizadores funcionan perfectamente y en el nuevo los eliminé, pero eso no solucionó la cuestión. ¿Podrían ser los modelos? Lo único diferente entre el proyecto viejo, la aplicación de prueba y el proyecto nuevo son los modelos usados. Hoy tendré que hacer pruebas adicionales desactivando animaciones y verificando a fondo los colliders incluidos en cada objeto. A ver si tengo suerte... 

Nuevo sistema de IA


Han sido unos días de terrible calor (récord de temperatura a escasos 100km de aquí, para que luego no digan que el calentamiento global es un cuento) y largos apagones, por lo que tendrán que disculpar mi ausencia y poquísimos deseos de hacer algo. Mayormente he dedicado el tiempo a jugar, corregir la última novela, que no salió del todo buena (más bien mala), y pensar un poco en cómo arreglar el sistema de inteligencia artificial.
Era algo que llegaría tarde o temprano. La IA que implementé basada en e tutorial oficial de Unity se quedaba corta, tenía que arreglar sus carencias en algún momento. Y qué mejor momento que ahora, cuando descubrí que un error casi imposible de rastrear provocaba que los casts dejasen de funcionar un minuto después de detectada la primera colisión. No tenía otra opción.
Cuando puse manos a la obra me percaté de que no había pensado tanto como creía, sino que más bien había estado justificando mi inactividad con la excusa de que "estaba rediseñando el sistema". En mi idea habían muchas cosas sacadas del sistema anterior que ahora no funcionarían, a menos que copiase lo viejo casi por completo. Y en principio, eso no era lo que pretendía. En fin, que toca pensar un poco más, para que el nuevo diseño sea flexible y potente.
En esencia, sigo utilizando ScriptableObjects, pero ahora el gestor de estados tendrá una lista completa de lodos los estados del personaje y podrá cambiar de uno a otro. El subsistema de sensores (porque en teoría, podría tener un sensor para la vista y otro para el oído, por ejemplo) se desplaza del estado como tal a la lógica. Y cada estado podrá contener una o varias lógicas, que serían como los ladrillos que forman la IA como tal, encargándose de la toma de decisiones. Suena un poco confuso porque en realidad es confuso y no pasa de ser una idea sin probar y sin implementar. Supongo que hablar de ella hace que me percate de todos los fallos. Pero creo que una vez que resuelva todos los pequeños detalles, debería funcionar.