Sistemas

Aprende cómo se usan los systems para actualizar el estado de la escena

Las escenas de Decentraland dependen de systems para actualizar cualquier dato a lo largo del tiempo, incluida la información almacenada en el componentsarrow-up-right.



systems son lo que hace que las escenas sean dinámicas, son funciones que se ejecutan periódicamente en cada tick del bucle de juego de la escena, cambiando lo que se renderizará.

El siguiente ejemplo muestra una declaración básica de un sistema:

La función en un sistema puede realizar cualquier cosa que desees. Típicamente, actuará sobre todas las entidades que cumplan ciertos queryarrow-up-right, siguiendo cierta lógica para cambiar los valores almacenados en los componentes de la entidad.

En el ejemplo anterior, el sistema MoveSystem es una función que se ejecuta en cada tick del bucle de juego, cambiando la posición de cada entidad en la escena que tiene un Transform.



Puedes tener múltiples sistemas en tu escena para desacoplar distintos comportamientos, haciendo tu código más limpio y más fácil de escalar y reutilizar. Por ejemplo, un sistema podría encargarse de la física, otro podría hacer que una entidad obstáculo se mueva de un lado a otro continuamente, y otro podría encargarse de la IA de los personajes.

Varios sistemas pueden actuar sobre una sola entidad. Por ejemplo, un personaje no jugador podría moverse por sí mismo según una IA, pero también verse afectado por la gravedad al caminar accidentalmente fuera de un acantilado. En ese escenario, los sistemas de física y de IA ni siquiera necesitan conocerse entre sí. Cada uno reevalúa independientemente su estado actual en cada tick del bucle de juego e implementa su propia lógica separada.

La función del sistema

La función de un sistema se ejecuta periódicamente, una vez por cada tick del bucle de juego. Esto ocurre automáticamente, no necesitas llamar explícitamente a esta función desde ninguna parte de tu código.

En una escena de Decentraland, puedes pensar en el bucle de juego como la agregación de todas las funciones de sistema en tu escena.

circle-exclamation

Manejar entidades por referencia

Algunos componentes y sistemas están pensados para usarse solo en una entidad en la escena. Por ejemplo, en una entidad que almacena la puntuación de un juego o quizás una puerta principal que es única en la escena. Para acceder a una de esas entidades dentro de un sistema, puedes simplemente referirte a la entidad o a sus componentes por nombre en las funciones del sistema.

Para proyectos más grandes, recomendamos que mantengas las definiciones de los sistemas en archivos separados de la instanciación de entidades y componentes.

Iterar sobre una query de componentes

Muchas veces, tu escena tendrá múltiples entidades del mismo tipo que tendrán comportamientos similares. Por ejemplo muchas puertas que se pueden abrir, o muchos enemigos que pueden atacar al jugador. Tiene sentido manejar todas estas entidades similares en un solo sistema, iterando sobre la lista y realizando las mismas comprobaciones en cada una.

No quieres que la función de un sistema itere sobre todo el conjunto de entidades en la escena, ya que esto podría ser muy costoso en términos de potencia de procesamiento. Para evitar esto, puedes consultar componentesarrow-up-right, para iterar solo sobre las entidades relevantes.

Por ejemplo, tu escena puede tener un PhysicsSystem que calcula el efecto de la gravedad sobre las entidades de tu escena. Algunas entidades en tu escena, como los árboles, no están pensadas para moverse jamás; por lo que sería inteligente evitar calcular los efectos de la gravedad sobre estas. Puedes definir un HasPhysics componente para marcar las entidades que podrían verse afectadas por la gravedad, y luego hacer que PhysicsSystem solo trate con las entidades devueltas por esta query.

Delta de tiempo entre frames

La función en un sistema puede opcionalmente incluir un argumento llamado dt, de tipo número (representando delta time).

delta time representa el tiempo que ha pasado desde el último tick del bucle de juego, en segundos.

Las escenas de Decentraland se actualizan por defecto a 30 ticks por segundo. Esto significa que el dt argumento pasado a todos los sistemas tenderá a ser igual a 1/30 (0.0333...).

Si el procesamiento de un frame toma menos tiempo que este intervalo, entonces el engine esperará el tiempo restante para mantener las actualizaciones con un ritmo regular y dt permanecerá igual a 1/30 .



Si el procesamiento de un frame toma más tiempo que 1/30 segundos, el renderizado de ese frame se retrasa. El engine entonces intenta terminar ese frame y mostrarlo lo antes posible. Luego procede al siguiente frame e intenta mostrarlo 1/30 segundos después del último frame. No compensa por el retraso anterior.



Idealmente, debes evitar que tu escena pierda frames, ya que esto impacta la calidad de la experiencia del jugador. Dado que esto depende de la potencia de procesamiento de la máquina del jugador, siempre existe la posibilidad de que tu escena deba estar preparada para manejarlo de forma elegante.

El dt la variable es útil cuando el procesamiento de frames excede el tiempo por defecto. Asumiendo que el frame actual tomará tanto tiempo como el anterior, esta información puede usarse para calcular cuánto ajustar un cambio gradual, de modo que la tasa de cambio parezca constante y en proporción al lag entre frames.

Ver posicionamiento de entidadesarrow-up-right para ejemplos de cómo usar dt para hacer el movimiento más suave.

Bucle en un intervalo de tiempo

Si quieres que un sistema ejecute algo a un intervalo de tiempo regular, puedes hacerlo combinando el dt argumento con un temporizador.

También existe una función abreviada setInterval y clearInterval que usa Systems en segundo plano. Esto permite una versión más fácil y corta cuando la intención es ejecutar cierta función cada X cantidad de tiempo.

Donde el primer argumento, callback, es la función que se ejecutará (en este caso, el console.log()), y el segundo argumento, ms(1000 en este caso), son los milisegundos a esperar entre cada ejecución de la función.

Para detener el setInterval function, clearInterval se usa.

Donde intervalId es la referencia al setInterval return definido antes.

Para casos de uso más complejos, donde puede haber múltiples retrasos y bucles creados dinámicamente, puede valer la pena definir un componente personalizado para almacenar un valor de temporizador individual para cada entidad. Ver Custom componentsarrow-up-right.

Orden de ejecución de los Systems

En algunos casos, cuando tienes múltiples sistemas en ejecución, podría importarte qué sistema se ejecuta primero en tu escena.

Por ejemplo, podrías tener un physics sistema que actualiza la posición de las entidades en la escena, y otro boundaries sistema que garantiza que ninguna de las entidades esté posicionada fuera de los límites de la escena. En este caso, querrás asegurarte de que el boundaries sistema se ejecute al final. De lo contrario, el physics sistema podría mover entidades fuera de los límites de la escena pero el boundaries sistema no lo descubrirá hasta que se ejecute nuevamente en el siguiente frame.

Al añadir un sistema al engine, establece un priority opcional para determinar cuándo se ejecuta el sistema en relación con otros sistemas.

Los Systems con un número de prioridad más bajo se ejecutan primero, así que un sistema con una prioridad de 1 se ejecuta antes que uno con prioridad 5.

Los Systems a los que no se les da una prioridad explícita tienen una prioridad por defecto de 0, por lo que estos se ejecutan primero.

Si dos sistemas tienen el mismo número de prioridad, no hay forma de saber con certeza cuál de ellos se ejecutará primero.

Eliminar un system

Una instancia de un system puede añadirse o eliminarse del engine para activarlo o desactivarlo.

Si un sistema está definido pero no se añade al engine, su función no es llamada por el engine.

Para eliminar un system, primero debes darle un nombre cuando lo añades al engine, para que puedas referirte al sistema más tarde.

Una escena puede potencialmente tener múltiples instancias del mismo sistema ejecutándose juntas, por lo que necesitas decirle al engine cuál de esas eliminar.

Otra forma de eliminar un sistema es declarar un puntero al sistema, y luego pasar ese puntero al engine.removeSystem() método.

Ten en cuenta que el puntero es a la instance del sistema, no a la clase del sistema. En el ejemplo anterior, engine.removeSystem() no se está pasando mySystem (la declaración de la clase del sistema). Se está pasando mySystemInstance (la instancia que fue añadida al engine).

Puedes usar el método a continuación para hacer que un sistema se autodestruya cuando su propósito esté completo.

Última actualización