Video Summary and Transcription
Esta charla explora el futuro de React Three Fiber y su potencial para construir mundos inmersivos en 3D y videojuegos en la web. Se discuten los desafíos de construir aplicaciones inmersivas en 3D y las soluciones proporcionadas por React Three Fiber, incluyendo la desvinculación de las actualizaciones del estado de la simulación y la mejora de la programación con referencias estables. Se explora el concepto de programación orientada a datos y el uso del patrón de diseño ECS (Entity-Component-System) para datos componibles en React Three Fiber. La charla concluye enfatizando el enfoque futuro en habilitar flujos de trabajo orientados a datos y la integración de un planificador incorporado en futuras versiones de React Three Fiber.
1. Introducción a React 3 Fiber y su futuro
Hola, mi nombre es Chris Baumgartner. Esta charla trata sobre el futuro de React 3 Fiber y cómo pretendemos abordar los videojuegos en la web. React 3 Fiber es una biblioteca para React que te permite renderizar en 3D de manera declarativa. Se utiliza en todas partes, desde empresas hasta aficionados e incluso algunos juegos. Seguimos tres principios de diseño: mantener la API simple, pragmática y estable. Soy desarrollador web 3D en Chauncey Technologies y miembro de Poimandris, donde mantengo React 3 Fiber. Únete a nosotros en nuestro Discord si quieres obtener más información.
Hola, mi nombre es Chris Baumgartner, y esta es una charla sobre el futuro de React 3 Fiber y cómo pretendemos abordar los juegos de video en la web. Si no conoces React 3 Fiber, es una biblioteca para React que te permite renderizar de manera declarativa en 3D como si fuera una API de navegador. Bajo el capó, es un renderizador de React para 3JS, que a su vez es un renderizador 3D de código abierto. R3F se utiliza en todas partes, desde empresas hasta aficionados e incluso algunos juegos.
Algunos de los adoptantes son Vercel, Zillow, Ready Player Me, por nombrar algunos. Tenemos una base de instalación estimada de 300,000 y representamos una gran parte de las instalaciones de React en este momento. Seguimos tres principios de diseño. Mantenemos la API simple, pragmática y estable. Pero basta de eso, volvamos a mí. Profesionalmente, soy desarrollador web 3D en Chauncey Technologies. También profesionalmente, pero mucho menos lucrativamente, soy miembro de Poimandris, donde mantengo React 3 Fiber junto con Cody Bennett y Paul Henschel. También en Poimandris es donde estoy desarrollando algunas nuevas tecnologías de las que hablaremos hoy. Y si me encuentras en Discord, probablemente me reconocerás por mi avatar a la izquierda.
Poimandris es un colectivo de código abierto iniciado por Paul Henschel en 2017. Si trabajas con React, probablemente hayas oído hablar de nuestras bibliotecas, ya sea Zoos Donned, React Spring o incluso React 3 Fiber en sí. Somos un colectivo completamente horizontal, dedicado a compartir conocimientos y habilitar la creatividad para los desarrolladores. Si te gusta eso, únete a nosotros en nuestro Discord. Tenemos una comunidad bastante grande allí.
Entonces, tal vez seas como yo. Comencé como desarrollador web. Ni siquiera uno muy bueno. Más bien como un diseñador web que se enorgullece de escribir su propio CSS. Nunca pensé que sería lo suficientemente bueno en programación como para hacer un juego. Pero me encantan los juegos, así que soñaba con eso. Mis habilidades de programación crecieron constantemente y estaba comenzando a aprender React cuando me encontré con React 3 Fiber. Había estos hermosos demos, como el que se muestra en la captura de pantalla. Creo que ese fue el primero que vi. Y parecía tan simple. Y por primera vez, al ver algo interactivo en 3D, pensé: puedo hacer esto. Y más tarde me di cuenta de que fue un momento de reconocimiento personal.
2. Building Games with React
React 3 Fiber ha inspirado a muchos desarrolladores web. Queremos llevarlo más lejos y crear mundos inmersivos en 3D, incluso videojuegos. React es una abstracción sobre el DOM y es funcional, componible y declarativo. Permite la mejora progresiva y adopta una sensación similar a la web. Veamos dónde estamos con React 3.5 hoy.
Y esos sueños de repente parecían mucho más reales, y de repente adquirí un nuevo pasatiempo. Pero como pronto aprendí, una demostración no es un juego. Incluso una aplicación empresarial en 3D no es un juego. React 3 Fiber ha inspirado a muchos desarrolladores web, pero ¿cómo podemos llevarlo más lejos? ¿Cómo podemos llegar al momento de poder hacer esto para crear mundos inmersivos en 3D? Incluso videojuegos.
Así que comienza el viaje. Hace dos años comencé por este camino y hoy voy a compartir contigo a dónde me ha llevado. Ripple patrocinó gran parte de la investigación, así que un saludo a ellos, a mi empresa Chauncey y a toda la comunidad que ha ayudado en el camino. Esto comienza con React. Se trata de construir juegos con React. Así que empecemos por ahí. React es una abstracción sobre el DOM. Ups. Casi olvido de qué charla estaba dando. React es una abstracción sobre renderizados similares a documentos. Esto es importante porque un gráfico de escena en 3D se parece mucho a un documento. React también es funcional, componible y declarativo. Y todas las características que conocemos y amamos que lo hacen escalable y testeable.
React también es un framework. Te hace comprar su enfoque declarativo y ciclo de vida, pero de lo contrario te permite traer tu propia pila tecnológica. Esto es importante porque permite la mejora progresiva. Y para que el ecosistema de React avance, ya sabes, necesitamos poder adoptar las nuevas tecnologías que están surgiendo en el mundo de JavaScript en general. En cambio, los motores de juegos casi siempre son monolíticos y te obligan a llevar contigo todo el equipo necesario, incluso si te niegas a usarlo. Por lo tanto, en mi opinión, un motor de juegos de React necesita incorporar todas estas ideas. Necesita renderizar como un documento. Necesita ser funcional, componible, declarativo, adoptar la mejora progresiva y tener una sensación similar a la web. Y la última es realmente importante, pero también imposible de definir si lo sabes, si lo ves como desarrollador web, sabes si algo se siente como una web. Veamos dónde estamos con React 3.5 hoy. Y hey, parece que lo logramos. Pero no puede ser tan simple, ¿verdad? Pero sí hace todas estas cosas. Renderiza como un documento.
3. Desafíos de Construir Aplicaciones Inmersivas en 3D
El modelo de React para código funcional y componible se encuentra con un obstáculo al intentar crear aplicaciones inmersivas en 3D. Estas experiencias en 3D están orientadas a los datos, ya que son una simulación en tiempo real junto con una vista de esa simulación.
Es funcional. Es componible de todas las formas en que React lo es, de esa manera de arriba hacia abajo. Es declarativo. Adopta la mejora progresiva. 3 es solo un renderizado. No te obliga a hacer nada más. Y creo que tiene una sensación muy web. Tenemos eventos de puntero, ya sabes, las props se ven como props de la web. Tiene una etiqueta de imagen. ¿Qué más podrías querer?
Pero en realidad tenemos un problema. El uso del modelo de React para código funcional y componible nos enfrenta a un obstáculo al intentar crear aplicaciones inmersivas en 3D. Esto es de lo que hablaré mucho. Pero ¿por qué? En pocas palabras, React está orientado al renderizado, pero las aplicaciones impulsadas por simulación como los mundos en 3D no lo están. Si no sabes qué significa orientado al renderizado, no te preocupes, lo inventé. En React, el flujo o estado o datos, probablemente usaré estos términos indistintamente, está orientado a renderizar una vista. Lo cual, por cierto, estos tipos de diagramas están en todas partes en Internet. Pero no es del todo cierto. El DOM virtual no se parece exactamente al DOM del navegador. Hay muchos más nodos allí. Y se asume que la mayoría, si no todos, las actualizaciones de estado e incluso la interactividad se digieren para el renderizado. Entonces, lo que realmente quiero decir aquí es que todo se trata de la función de retorno. No estás ejecutando típicamente una aplicación de React solo para procesar algunos datos. Debido a este estado, se asume de manera segura que siempre fluye de arriba hacia abajo a medida que creamos el documento de arriba hacia abajo.
En contraste, estas experiencias en 3D que queremos crear están orientadas a los datos. Ese no lo inventé. Esto se debe a que, en su núcleo, son una simulación en tiempo real junto con una vista de esa simulación. Es la misma razón por la que puedes ejecutar un juego sin interfaz en el servidor para multijugador. La simulación no requiere ningún renderizado en absoluto. Y las simulaciones, por su propia naturaleza, están orientadas a los datos. Simplemente transforman los datos de entrada en lo que estás jugando.
4. Decoupling Simulation State Updates
El mismo flujo que crea una vista no puede ejecutar la simulación. Resolvemos esto elevando el estado para orquestar todo el estado relevante en un solo lugar antes de enviarlo a la vista. Desacoplamos las actualizaciones del estado de la simulación del renderizado utilizando un bucle de fotogramas. Programamos devoluciones de llamada en el bucle basándonos en la dependencia, asegurando que los datos se actualicen en el orden correcto. Directed es nuestra nueva biblioteca de programación de tareas.
No puedes asumir, como lo hace React, que el mismo flujo que crea una vista también puede ejecutar la simulación. Y esta imagen es evidencia de que no lo inventé, porque Python está lleno de personas que odian cualquier cosa que no sea la programación orientada a los datos. Si no estás convencido, veamos este problema clásico que encontré en Internet, donde el hijo tiene un estado que debe establecerse en el nivel del padre. En este caso, para que pueda fluir hacia abajo a otros componentes.
Entonces, ¿cómo resolvemos esto? ¿Qué hacemos? La respuesta canónica es simplemente elevar el estado. Ya sea prop, contexto o controlador específico de dominio, elevamos y ejecutamos toda la lógica en un solo lugar. Así que con una aplicación compleja como una simulación, elevamos y elevamos y elevamos. Y antes de que nos demos cuenta, estamos en la parte superior, orquestando todo el estado relevante en un solo lugar antes de enviarlo a la vista.
Esto me da una idea. ¿Qué tal si desacoplamos todas las actualizaciones del estado de la simulación del renderizado? Ya estamos elevando el estado hasta la parte superior. Así que comencemos con un bucle de fotogramas. Las aplicaciones en tiempo real deben ser en tiempo real. Necesitan actualizarse en cada fotograma, y definitivamente no queremos que React vuelva a renderizar en cada fotograma. Así que inventamos un bucle de fotogramas. React ReFibre ya tiene esto incorporado con el uso del gancho de fotograma. Ahora se ejecuta de forma independiente del propio bucle de React, y nos permite ejecutar código sincronizado con el v-sync que no se detiene ni hace que React digiera nuevos ciclos de vida.
Pero ahora necesitamos programar todas estas devoluciones de llamada, todas estas llamadas de función en este bucle. El enfoque más ingenuo es programar en orden de renderizado, llamadas de renderizado. Pero ya decidimos que esto no va a funcionar para nosotros, incluso si funciona para la mayoría de las aplicaciones que se representan en el DOM. Menos ingenuo pero difícil de usar es ordenar por número de prioridad. Rápidamente te encuentras con un problema donde tienes números mágicos. Es como si alguna vez has jugado con los índices Z en el mundo del CSS, alguien comienza a poner 999999 allí para que siempre se ejecute al final, y ahora necesitas ir después de 99999, así que ahora eres 1001. Es un desastre.
Lo ideal es programar por dependencia. Así que notamos que lo que realmente queremos hacer es tener nuestros datos actualizados en el orden correcto, sin importar cuán relacionados estén los componentes de React que se mueven en el árbol de renderizado. Esto se logra diciéndole a nuestro programador nuestras intenciones, dejando que resuelva el orden por nosotros. Así que decimos que quiero que esta función se ejecute antes que esta otra, quiero que esta función se ejecute después de esa otra, y luego él descubre cómo ordenarlos todos para que eso sea cierto. Así que presentamos Directed. Esta será nuestra nueva biblioteca de programación de tareas. Hace exactamente eso.
5. Publishing and Scheduling Update Functions
Publica un punto mandris. Es componible. Es funcional. Utiliza un grafo acíclico dirigido, o DAG, para resolver el orden de las funciones de actualización basado en la intención que le damos. Funciona tanto con Vanilla como con React. Y lo más importante para nosotros, permite integraciones en el ecosistema, porque no hay números mágicos. Y explicaré cómo hace todas estas cosas.
Publica un punto mandris. Es componible. Es funcional. Utiliza un grafo acíclico dirigido, o DAG, para resolver el orden de las funciones de actualización basado en la intención que le damos. Funciona tanto con Vanilla como con React. Y lo más importante para nosotros, permite integraciones en el ecosistema, porque no hay números mágicos. Y explicaré cómo hace todas estas cosas.
Muy bien. Tenemos las funciones A, B y C que van a ejecutar algunas actualizaciones. Usamos nuestro nuevo y práctico gancho, useSchedule, y las pasamos sin absolutamente nada más. Nuestro pequeño amigo aquí, que nos ayudará a programar, simplemente las coloca en el orden que quiera, porque no le dimos ninguna intención. Pero ahora queremos un orden específico. Queremos que sea A, B, C. Así que le decimos al programador, está bien, B viene después de A, va antes de C, C viene después de B. Y en realidad, ni siquiera tenemos que poner antes de C allí. Solo decimos después de A, después de B, y nuestro pequeño amigo se encargará de determinar el orden en el que debe ir.
Pero ahora hagamos algo un poco más desafiante. Aquí queremos hacer un bucle de juego típico. Y generalmente, lo que estamos acostumbrados a ver en esos casos son etapas. Entrada, física, actualización predeterminada, renderizado. En lugar de etapas, estamos usando etiquetas. Esto nos dará mucha más flexibilidad. Porque ahora queremos insertar la actualización de la cámara. La actualización de la cámara va en la predeterminada, pero también es un poco su propia etapa. Le damos una etiqueta de cámara y una etiqueta predeterminada. Y esto nos permite tener etiquetas, grupos, los llamo grupos internamente, que se superponen. Y también ten en cuenta aquí que estamos apuntando directamente a la función de mover el cuerpo. No estamos apuntando a una etiqueta. Y eso es para que podamos programar linealmente con mover el cuerpo, asegurarnos de que la actualización de la cámara siempre ocurra después de eso. Y luego le damos su propia antes del renderizado, aunque mover el cuerpo ya tiene antes del renderizado, que sería heredado por él a través de la etiqueta predeterminada.
6. Enhancing Scheduling with Stable References
Y de esta manera, si alguna vez llamamos a esto por sí solo, tiene todos los datos de programación que necesita para contenerse a sí mismo, para ejecutar su propia lógica. No necesita depender de otra llamada de otra función programada. Y como una mejora progresiva aquí, puedes ver a la izquierda, esto se parece mucho a usar frame, estamos usando closure, y hacemos una diferencia por referencia.
Y de esta manera, si alguna vez llamamos a esto por sí solo, tiene todos los datos de programación que necesita para contenerse a sí mismo, para ejecutar su propia lógica. No necesita depender de otra llamada de otra función programada. Y como una mejora progresiva aquí, puedes ver a la izquierda, esto se parece mucho a usar frame, estamos usando closure, y hacemos una diferencia por referencia. Entonces, si hay una función de flecha que genera una nueva función cada vez, memoizada, por supuesto, entonces cada instancia de moving box, o en este caso, moving box aparentemente, creará su propia instancia de esta función de actualización. En el lado derecho, sin embargo, usamos una referencia estable, porque una referencia estable, sin importar cuántas veces se monte este componente, esa función de actualización solo se montará una vez. Esto nos permite hacer algo muy poderoso, donde puedes, no necesitas saber qué está haciendo una biblioteca de física, solo necesitas una programación relativa a algo etiquetado con física, o física de Rapier, por ejemplo. Estaremos lanzando esta biblioteca en el próximo mes, creo que cuando este video se publique, estará disponible, así que mantén los ojos bien abiertos.
7. Explorando ECS: Programación Orientada a Datos
Vale, entonces las actualizaciones de datos no son completamente componibles, pero ¿qué pasa con los datos? Estamos hablando de ECS, un patrón de diseño natural para la programación orientada a datos. Tu aplicación se convierte en una base de datos en vivo donde los componentes son almacenes globales de datos componibles, las entidades son identificadores que nos permiten consultar colecciones de componentes y los sistemas son funciones que actualizan los componentes. Creamos una posición en el almacén masivo y la agregamos a una entidad. Podemos consultar el mundo para obtener todos los cuerpos según criterios específicos. Existe una relación entre el cuerpo y su objetivo.
Vale, entonces las actualizaciones de datos no son completamente componibles, pero ¿qué pasa con los datos? Nuestro pequeño amigo tiene una buena pregunta, ¿cómo accedemos a los datos si no están disponibles de inmediato dentro del componente React en sí? Pero espera, no lo digas, sí, llegamos aquí, probablemente sabías que esto iba a suceder. Estamos hablando de ECS. Así que antes de que entres en pánico, recuerda que todo lo que ves es una mejora progresiva para ayudar a facilitar el diseño orientado a datos. Puedes aceptarlo o dejarlo tanto como quieras. Podría llenar toda una charla hablando solo de ECS, pero no será esta. En cambio, lo motivaré brevemente y te animaré a investigarlo más si estás interesado, tal vez en una charla de seguimiento.
ECS es un patrón de diseño natural para la programación orientada a datos. Si sigues por este camino, casi siempre terminarás aquí. En esencia, tu aplicación se convierte en una base de datos en vivo. En realidad, solo se convierte en una base de datos en vivo donde los componentes son almacenes globales componibles de datos, las entidades son identificadores que nos permiten consultar colecciones de componentes y los sistemas son funciones que actualizan los componentes, como ya hemos mencionado. Y los datos son el rey. Pero esto es JavaScript, por lo que los objetos nunca están realmente muy lejos.
Vale. Así que vamos a familiarizarnos un poco más con esto. Normalmente, si creamos un objeto body, lo veríamos como a la izquierda, la clase donde se definen la posición y la masa. A la derecha está la concepción de ECS. Creamos una posición en el almacén de masas aquí llamado componentes, no confundir con los componentes de React, aunque es confuso. Y lo agregamos a una entidad. Lo que esto significa es que los datos de esta entidad son componibles. Podemos agregar otro almacén si queremos, cambiando su firma. Y luego decimos que un cuerpo es cualquier entidad con posición y masa. Y luego lo que hacemos es consultar el mundo. Obtenemos todos los cuerpos diciendo: dame todas las entidades que tienen posición y masa, y luego iteramos sobre esos identificadores, extraemos el almacén, extraemos el almacén de posición y lo movemos. Esto se puede hacer globalmente, en cualquier lugar, en cualquier momento. Y puedes consultar mucho más. Podemos consultar casi cualquier cosa. Podemos obtener todos los cuerpos. Podemos obtener todos los cuerpos que se están moviendo. Podemos obtener todos los cuerpos que se están moviendo hacia un jugador. Así que aquí hay una relación, una relación entre el cuerpo y su objetivo.
8. Construyendo un Ecosistema de Datos Componibles
Podemos obtener todos los jugadores que están casados. Sweet ECS es una biblioteca experimental que permite una integración profunda con React. Los datos componibles y las actualizaciones ayudan a resolver problemas. Se logra una completa componibilidad. React 3 Fiber tendrá un planificador incorporado en futuras versiones. El enfoque futuro se centrará en habilitar flujos de trabajo orientados a datos.
Podemos obtener todos los jugadores que están casados. Esto es ahora una relación de jugador con el jugador. Hemos obtenido todos los jugadores que también son ranas, y ahora tenemos herencia. Podemos preguntar, ¿es el jugador una rana? Y podemos complicarlo tanto como queramos. ¿Todos los jugadores que son miembros del gremio de ranas, enemigos jurados de los gatos, pero no descendientes de ranas, verdad? Así que deja volar tu imaginación. Una vez que permites que tu aplicación sea una base de datos viva, la libertad de poder manipular los datos se vuelve tentadora.
Y ahora te presento una biblioteca muy experimental para que te adentres en el espacio en el que estamos pensando, llamada Sweet ECS. Es un ECS declarativo con una integración profunda con React, y lo que quiero decir con eso es que automáticamente todo lo que se compromete con la vista en React se convierte en una entidad a la que podemos agregar componentes. Está impulsado por la próxima evolución de Bit ECS. He estado trabajando en esto con Nate Bit. Debería estar disponible para el momento de esta charla. A la derecha tienes un ejemplo de la API. No voy a entrar en detalles, pero aquí puedes ver que estamos programando en nuestro componente de cuerpo y agregando todos nuestros componentes que crean el cuerpo a la entidad de forma declarativa.
Veamos cómo tener datos componibles con actualizaciones componibles nos ayuda en un problema que estábamos analizando antes. Tenemos nuestro componente MoveInBox, y antes hacíamos un cierre donde simplemente obteníamos la referencia y movíamos la caja. Aquí, vamos a pasar una función de actualización que está definida de forma estable, y vamos a consultar la caja en función de su posición, y le dimos un componente de posición a continuación, y luego vamos a extraer el almacén de posición y moverlo. Esta es una función que se ejecutará para todas las instancias de MoveInBox. Y luego, a la derecha, y este es un problema clásicamente molesto de hacer en React en este momento, tenemos una cámara de perspectiva. Le damos el componente followCamera. Consultamos para obtener todas las cámaras de seguimiento. Le dimos a la caja un componente isFollowTarget. Consultamos eso. Tomamos el primer resultado porque asumimos que solo hay un objetivo. Iteramos sobre todas las cámaras de seguimiento. Obtenemos sus objetos y luego llamamos a lookAt, y el objeto three aquí, nuevamente, una API experimental, automáticamente todas las entidades, todos los elementos de vista obtienen sus objetos, instancias de la referencia, agregados a un componente de objeto para que podamos extraerlos y usarlos dentro del sistema. Y ahí lo tienes. Hemos logrado una completa componibilidad.
Finalmente, esto nos lleva a un ecosistema de React donde podemos traer un motor de juego justo lo suficiente, y creo que esto es muy único. Es algo que necesitamos como desarrolladores web para pasar al desarrollo de juegos, y es algo que necesitamos en PointMandrius, como comunidad, para construir un ecosistema de bibliotecas de física de primera clase, bibliotecas de UI como UIKit, Rapier, Jolt. Hay todo tipo de otras partes involucradas en esto. Necesitas animaciones de personajes, controladores, controles de cámara. Poder crear todas estas cosas de forma componible de manera orientada a datos nos permitirá construir ese ecosistema. Y lo que es importante, ejecutar nuestros juegos en un servidor web, porque si estás construyendo juegos en la web, necesita ser multijugador.
En resumen, el futuro de React 3 Fiber, eso es donde comenzamos. En el futuro, tendremos el planificador incorporado para la versión 10, la versión 9 será una versión de compatibilidad con React 19, en la versión 10 tendremos un lanzamiento beta prolongado donde implementaremos estas nuevas características. El planificador nos permitirá integrarnos con el ecosistema en cualquier lugar, en cualquier momento. Y luego, nos centraremos en habilitar flujos de trabajo orientados a datos a medida que avanzamos. Gracias. Échale un vistazo a nuestro Discord si quieres aprender más, y nos vemos pronto.
Comments