Entities y Components
Aprende lo esencial sobre entities y components en una escena de Decentraland
Las escenas de Decentraland se construyen alrededor de entities, components y systems. Este es un patrón común utilizado en la arquitectura de varios motores de juego, que permite una fácil composabilidad y escalabilidad.

Visión general
Entities son la unidad básica para construir todo en las escenas de Decentraland. Todos los objetos 3D visibles e invisibles y los reproductores de audio en tu escena serán cada uno una entity. Una entity no es más que un id, que puede ser referenciado por components. La entity en sí no tiene propiedades ni métodos propios, simplemente sirve para agrupar varios components.
Components definen los rasgos de una entity. Por ejemplo, un Transform component almacena las coordenadas, rotación y escala de la entity. Un MeshRenderer component le da a la entity una forma visible (como un cubo o una esfera) cuando se renderiza en la escena, un Material component le da a la entity un color o textura. También puedes crear components personalizados para contener los datos que requiera tu escena, por ejemplo un custom health podría almacenar el valor de vida restante de una entity, y añadirlo a las entities que representan enemigos no jugadores en un juego.
Si estás familiarizado con el desarrollo web, piensa en las entities como el equivalente a Elements en un DOM tree, y en los components como attributes de esos elementos.
En el Scene Editor in Creator Hub, puedes ver los components que pertenecen a una entity seleccionándola.

📔 Nota: En versiones anteriores del SDK, las Entities eran objects que se instanciaban y podían extenderse para añadir funciones. A partir de la versión 7.0 del SDK, las entities son solo un ID. Esta estructura se ajusta mejor a los principios de data oriented programming y puede ayudar al rendimiento de la escena.

Components como Transform, Material o cualquiera de los shape components están estrechamente vinculados con el renderizado de la escena. Si los valores en estos components cambian, eso por sí solo es suficiente para que el engine cambie cómo se renderiza la escena en el siguiente frame.
El engine es la parte de la escena que se sitúa en el medio y gestiona todas las demás partes. Determina qué entities se renderizan y cómo los jugadores interactúan con ellas. También coordina qué funciones de systems se ejecutan y cuándo.
Los components están pensados para almacenar datos sobre su entity referenciada. Solo pueden almacenar estos datos; no pueden modificar esos datos por sí mismos. Todos los cambios en los valores de los components son realizados por Systems. Los Systems están completamente desacoplados de los components y las entities. Las entities y los components son agnósticos respecto a qué systems están actuando sobre ellos.
Sintaxis para entities y components
El ejemplo siguiente muestra algunas operaciones básicas para declarar y configurar entities y components básicos.
📔 Nota: En versiones anteriores del SDK, era necesario añadir manualmente una entity al engine para comenzar a renderizarla. A partir de la versión 7 del SDK, las entities se añaden implícitamente al engine tan pronto como se les asigna un component.
Cuando se crea un component, siempre se asigna a una entity padre. Los valores del component entonces afectan a la entity.
Eliminar entities
Para eliminar una entity del engine, usa engine.removeEntity()
Si una entity eliminada tiene entities hijas, estas cambian su padre de nuevo a la entity por defecto engine.RootEntity , que está posicionada en la posición base de la escena, con una escala de 1.
Para eliminar una entity y también todos sus hijos (y cualquier hijo de sus hijos, recursivamente), usa el removeEntityWithChildren() helper.
💡 Consejo: En lugar de eliminar una entity del engine, en algunos casos puede ser mejor hacerla invisible, en caso de que quieras poder cargarla de nuevo sin demora. Ver Make invisible
Eliminando entities detrás de las escenas
Una entity es solo un id que es referenciado por sus components. Así que al eliminar una entity en realidad estás eliminando cada uno de los components que referencian esa entity. Esto significa que si eliminas manualmente todos los components de una entity, tendrá el mismo efecto que hacer engine.removeEntity().
Una vez que se eliminan los components de la entity, el id de esa entity queda libre para ser referenciado por nuevos components como una entity completamente nueva.
Entities anidadas
Una entity puede tener otras entities como hijas. Gracias a esto, podemos organizar entities en árboles, igual que el HTML de una página web.

Para establecer una entity como padre de otra, la entity hija debe tener un Transform component. Entonces puedes establecer el campo parent con una referencia a la entity padre.
Una vez que se asigna un padre, puede leerse desde la entity hija a través del campo parent en su Transform component.
Si una entity padre tiene un Transform component que afecta su posición, escala o rotación, sus entities hijas también se ven afectadas. Cualquier valor de posición o rotación se suma, cualquier valor de escala se multiplica.
Si ni la entity padre ni la hija tienen un Transform component, se usan los siguientes valores predeterminados.
Para position, el centro del padre es 0, 0, 0
Para rotation la rotación del padre es el quaternion 0, 0, 0, 1 (equivalente a los ángulos de Euler 0, 0, 0)
Para scale, el padre se considera que tiene un tamaño de 1. Cualquier cambio de tamaño del padre afecta la escala y la posición en proporción.
Las entities sin component de shape son invisibles en la escena. Estas se pueden usar como contenedores para manejar y posicionar múltiples entities como un grupo.
Para separar una entity hija de su padre, puedes asignar el padre de la entity a engine.RootEntity.
📔 Nota: Al trabajar con entities anidadas que están sincronizadas con otros jugadores, usa la parentEntity() función en lugar de la parent entity en el Transform. Ver Parented entities
En el Scene Editor, puedes ver toda la jerarquía de entities anidadas de tu escena en el panel izquierdo.

Obtener una entity por ID
Cada entity en tu escena tiene un número único id. Puedes recuperar un component que refiere a una entity específica desde el engine basándote en este ID.
📔 Nota: Los ids de entity entre 0 y 511 están reservados por el engine para entities fijas, como el avatar del jugador, la base de la escena, etc.
Por ejemplo, si el clic de un jugador o un raycast golpea una entity, esto devolverá el id de la entity impactada, y puedes usar el comando anterior para obtener el Transform component de la entity que coincide con ese id. También puedes obtener cualquier otro component de esa entity de la misma manera.
Obtener una entity por nombre
Al añadir entities mediante arrastrar y soltar en el Scene Editor, cada entity tiene un nombre único. Usa la función engine.getEntityOrNullByName() para referenciar una de estas entities desde tu código. Pasa el nombre de la entity como una cadena, tal como aparece en la UI del Scene Editor, en la vista de árbol a la izquierda.
📔 Nota: Asegúrate de usar engine.getEntityOrNullByName() solo dentro de la main() function, en funciones que se ejecuten después de main(), o en un system. Si se usa fuera de uno de esos contextos, las entities creadas en la UI del Scene Editor pueden no estar todavía instanciadas.
Puedes realizar cualquier acción en una entity obtenida mediante este método, como añadir o eliminar components, modificar valores de components existentes o eliminar la entity del engine.
Todas las entities añadidas vía la UI del Scene Editor tienen un Name component, puedes iterar sobre todas ellas así:
Añadir o reemplazar un component
Cada entity solo puede tener un component de un tipo dado. Por ejemplo, si intentas asignar un Transform a una entity que ya tiene uno, esto provocará un error.
Para evitar este error, puedes usar .createOrReplace en lugar de .create. Este comando sobrescribe cualquier component existente del mismo tipo si existe, de lo contrario crea un nuevo component igual que .create.
📔 Nota: Dado que .createOrReplace realiza una verificación adicional antes de crear el component, siempre es más eficiente usar .create. Si estás seguro de que la entity no tiene ya un component como el que estás añadiendo, usa .create.
Acceder a un component desde una entity
Puedes acceder a los components de una entity usando el .get() de la entity o las funciones getMutable() .
La función get() obtiene una referencia de solo lectura al component. No puedes cambiar ningún valor desde esta referencia del component.
Si deseas cambiar los valores del component, usa la función getMutable() en su lugar. Si cambias los valores en la versión mutable del component, estás afectando directamente a la entity a la que pertenece ese component.
Ver mutable data para más detalles.
📔 Nota: Usa getMutable() solo si realmente vas a hacer cambios en los valores del component. De lo contrario, usa siempre get(). Esta práctica sigue los principios de data oriented programming, y puede ayudar significativamente al rendimiento de la escena.
El ejemplo anterior modifica directamente el valor de la x escala en el Transform component.
Si no estás completamente seguro de si la entity tiene el component que intentas obtener, usa getOrNull() o getMutableOrNull().
📔 Nota: Evita usar getOrNull() o getMutableOrNull() cuando sea posible, ya que estas funciones implican comprobaciones adicionales y por tanto son menos eficientes que .get() y getMutable().
Si el component que intentas obtener no existe en la entity:
get()ygetMutable()devuelve un error.getOrNull()ygetMutableOrNull()devuelveNull.
Eliminar un component de una entity
Para eliminar un component de una entity, usa el método deleteFrom() del tipo de component.
Si intentas eliminar un component que no existe en la entity, esta acción no lanzará ningún error.
📔 Nota: Para eliminar todos los components de una entity a la vez, ver esta sección
Comprobar si existe un component
Puedes comprobar si una entity posee una instancia de cierto component usando la función has() . Esta función devuelve true si el component está presente, y false si no lo está. Esto puede ser muy útil para usar en la lógica condicional de tu escena.
💡 Consejo: También puedes query components para obtener una lista completa de entities que poseen un component específico, o un conjunto específico de components. No itures sobre todas las entities en la escena manualmente para comprobar cada una con un has(), ese enfoque es mucho menos eficiente.
Comprobar cambios en un component
Usa la función onChange para ejecutar una función callback cada vez que los valores del component cambien para una entity dada. Esto funciona en cualquier component y es un gran atajo para mantener tu código legible.
La función callback puede incluir un parámetro de entrada que contiene el nuevo estado del component.
Si el component se elimina de la entity, entonces la función es llamada con una entrada de undefined.
📔 Nota: La función .onChange() actualmente solo funciona con components nativos del SDK, no funciona con custom comopnents definidos por el creador.
Obtener todas las entities descendientes
Al trabajar con jerarquías de entities anidadas, puede que necesites acceder a todas las entities que son descendientes de una entity padre, sin importar cuán profundamente anidadas estén. Para eso puedes usar getEntitiesWithParent. Toma como argumentos el engine y la parent entity y devuelve una lista de todas las entities que tienen esa Entiy en particular como su padre.
Esto es especialmente útil cuando necesitas encontrar entities que pueden estar anidadas bajo varios niveles bajo una entity padre. En lugar de recorrer manualmente la jerarquía nivel por nivel, getEntitiesWithParent() devuelve una lista plana de todos los descendientes que es fácil de iterar.
Si lo deseas, también puedes usar la función getComponentEntityTree(), que también filtra solo las entities que tienen un component dado o una lista de components.
La función getComponentEntityTree function toma tres parámetros:
engine: La instancia del engine que ejecuta las entitiesentity: La entity raíz desde la que empezarcomponent: El component por el que filtrar (típicamenteTransformpara jerarquías espaciales)
La función devuelve un generador que produce cada entity descendiente en la estructura del árbol. Solo se incluirán en los resultados las entities que tengan el component especificado.
Puedes combinar esto con otras comprobaciones de components para encontrar entities específicas en tu jerarquía:
Entities reservadas
Ciertos ids de entity están reservados para entities especiales que existen en cada escena. Se pueden acceder mediante los siguientes alias:
engine.RootEntityengine.PlayerEntityengine.CameraEntity
📔 Nota: Evita referirte a estas entities antes de que estén inicializadas. Para evitar este problema, refiérete a estas entities en la main() function, o en un system.
La entity raíz
Todas las entities de la escena son hijas de la engine.RootEntity, directa o indirectamente.
Esta entity carece de un Transform component, pero se usa para manejar varios components que representan configuraciones más globales, como skybox control, cursor position, o screen dimensions.
La entity del jugador
La función engine.PlayerEntity entity representa el avatar del jugador.
Obtén el Transform component del jugador para obtener la posición y rotación actuales del jugador, ver user data. El Transform del jugador es de solo lectura; para modificarlo usa la función movePlayerTo() , más información.
También puedes adjuntar objetos al jugador estableciéndolos como hijos de esta entity, aunque la opción Attach to Player suele ser la mejor opción para eso.
La entity de la cámara
La función engine.CameraEntity entity representa la cámara del jugador.
Obtén el Transform component de la cámara para obtener la posición y rotación de la cámara. El Transform de esta entity también es de solo lectura. Para modificar el ángulo o la posición de la cámara, usa una Virtual camera.
También puedes obtener el CameraMode component de la cámara para saber si el jugador está usando el modo de cámara en 1.ª o 3.ª persona, ver camera mode.
Última actualización