Components personalizados

Crea un component personalizado para manejar datos específicos relacionados con una entity

Los datos sobre una entidad se almacenan en su components. El SDK de Decentraland proporciona una serie de componentes base que gestionan diferentes aspectos de una entidad, como su posición, forma, material, etc. El engine sabe cómo interpretar la información en estos, y cambiará cómo se renderiza la entidad en consecuencia tan pronto como cambien sus valores.

Si la lógica de tu escena requiere almacenar información sobre una entidad que no está manejada por los componentes predeterminados del SDK, entonces puedes crear un tipo personalizado de componente en tu escena. Luego puedes construir systems que comprueben cambios en estos componentes y respondan en consecuencia.

Acerca de definir componentes

Para definir un nuevo componente, usa engine.defineComponent. Cada componente necesita lo siguiente:

  • Un componentName: Un identificador de cadena único que el SDK usa internamente para identificar este tipo de componente. Puede ser cualquier cadena, siempre que sea única.

  • A schema: Una clase que define la estructura de datos contenida por el componente.

  • default values (opcional): Un objeto que contiene valores por defecto para usar al inicializar una copia del componente, cuando no se proporcionen.

export const WheelSpinComponent = engine.defineComponent('wheelSpinComponent', {
	spinning: Schemas.Boolean,
	speed: Schemas.Float,
})
circle-exclamation

Una vez que hayas definido un componente personalizado, puedes crear instancias de este componente, que referencien entidades en la escena. Cuando creas una instancia de un componente, proporcionas valores para cada uno de los campos del schema del componente. Los valores deben cumplir con los tipos declarados de cada campo.

// Crear entidades
const wheel = engine.addEntity()
const wheel2 = engine.addEntity()

// Crear instancias del componente
WheelSpinComponent.create(wheel1, {
	spinning: true,
	speed: 10,
})

WheelSpinComponent.create(wheel2, {
	spinning: false,
	speed: 0,
})

Cada entidad que tiene el componente añadido instancia una nueva copia del componente, manteniendo datos específicos para esa entidad.

Tu componente personalizado también puede realizar las otras funciones comunes que están disponibles en otros componentes:

Acerca del componentName

Cada componente debe tener un nombre o identificador de componente único, que lo diferencie internamente. No necesitarás usar este identificador interno en ningún otro lugar de tu código. Una buena práctica es usar el mismo nombre que asignas al componente, pero comenzando con una letra minúscula, aunque lo que realmente importa es que este identificador sea único dentro del proyecto.

Al crear componentes que serán compartidos como parte de una biblioteca, ten en cuenta que los nombres de componentes en tu biblioteca no deben coincidir con los nombres de componentes en el proyecto donde se usan, ni con otras bibliotecas que también use ese proyecto. Para evitar el riesgo de solapamientos, la práctica recomendada es incluir el nombre de la biblioteca como parte de la componentName cadena. Puedes seguir esta fórmula: ${packageName}::${componentName}. Por ejemplo, si construyes unaMyUtilities biblioteca que incluye un MoveEntity componente, establece el componentName de ese componente en MyUtilities::moveEntity.

Componentes como marcas

Puede que quieras añadir un componente que simplemente marque una entidad para diferenciarla de otras, sin usarlo para almacenar datos. Para hacer esto, deja el schema como un objeto vacío.

Esto es especialmente útil cuando se usan consultas de componentes. Un componente de marca simple puede usarse para distinguir entidades de otras, y evitar que el sistema itere sobre más entidades de las necesarias.

Luego puedes crear un sistema que itere sobre todas las entidades con este componente.

Schemas de Componentes

Un schema describe la estructura de los datos dentro de un componente. Un componente puede almacenar tantos campos como quieras, cada uno debe incluirse en la estructura del schema. El schema puede incluir tantos niveles de elementos anidados como necesites.

Cada campo en el schema debe incluir una declaración de tipo. Solo puedes usar los tipos de schema especiales proporcionados por el SDK. Por ejemplo, usa el tipo Schemas.Boolean en lugar del tipo boolean. Escribe Schemas. y tu IDE mostrará todas las opciones disponibles.

El ejemplo anterior define un componente cuyo schema contiene dos valores, un spinning booleano y un speed número de punto flotante.

Puedes optar por crear el schema en línea al definir el componente, o para mayor legibilidad puedes crearlo y luego referenciarlo.

circle-info

💡 Tip: Al crear una instancia de un componente, las opciones de autocompletado de VS Studio sugerirán qué campos puedes agregar al componente presionando Ctrl + Space.

Tipos de Schema por defecto

Los siguientes tipos básicos están disponibles para usar dentro de los campos de un schema:

  • Schemas.Boolean

  • Schemas.Byte

  • Schemas.Double

  • Schemas.Float

  • Schemas.Int

  • Schemas.Int64

  • Schemas.Number

  • Schemas.Short

  • Schemas.String

  • Schemas.Entity

También existen los siguientes tipos complejos. Cada uno incluye una serie de propiedades anidadas con valores numéricos.

  • Schemas.Vector3

  • Schemas.Quaternion

  • Schemas.Color3

  • Schemas.Color4

circle-info

💡 Tip: Ver Tipos de geometría y Tipos de color para más detalles sobre cómo estos tipos de datos son útiles.

Por ejemplo, puedes usar estos tipos de schema en un componente como este para rastrear el movimiento gradual de una entidad. Este componente almacena una posición inicial y una final como valores Vector3, además de una velocidad y una fracción del recorrido completado como números float. Ver Mover entidades para la implementación completa de este ejemplo.

Tipos de arreglos

Para establecer el tipo de un campo como arreglo, usa Schemas.Array(). Pasa el tipo de los elementos en el arreglo como propiedad.

Tipos de schema anidados

Para establecer el tipo de un campo como un objeto, usa Schemas.Map(). Pasa el contenido de este objeto como una propiedad. Este objeto anidado es esencialmente un schema en sí, anidado dentro del schema padre.

Alternativamente, para mantener las cosas más legibles y reutilizables, podrías lograr lo mismo definiendo el schema anidado por separado, y luego referenciándolo al definir el schema padre.

Tipos enum

Puedes establecer el tipo de un campo en un schema como un enum. Los enums facilitan seleccionar entre un número finito de opciones, proporcionando valores legibles para cada una.

Para establecer el tipo de un campo como enum, primero debes definir el enum. Luego puedes referirte a él usando Schemas.EnumNumber o Schemas.EnumString, dependiendo del tipo de enum. Debes pasar el enum a referenciar entre <>, así como el tipo como parámetro (ya sea Schemas.Int para enums numéricos, o Schemas.String para enums de cadena). También debes pasar un valor por defecto para usar en este campo.

Tipos intercambiables

Puedes establecer el tipo de un campo en un schema para seguir un oneOf patrón, donde se pueden aceptar diferentes tipos.

Al crear una instancia del componente, necesitas especificar el tipo seleccionado con un $case, por ejemplo:

Valores por defecto

A menudo es bueno tener valores por defecto en tus componentes, para que no sea necesario establecer explícitamente cada valor cada vez que creas una nueva copia.

El engine.defineComponent() la función toma un tercer argumento, que te permite pasar un objeto con valores para usar por defecto. Este objeto puede incluir algunos o todos los valores del schema. Los valores que no se proporcionen en los defaults deberán siempre proporcionarse al inicializar una copia del componente.

El ejemplo anterior crea un WheelSpinComponent componente que incluye tanto un schema como un conjunto de valores por defecto para usar. Si luego inicializas una copia de este componente sin especificar ningún valor, usará los establecidos en el default.

Suscribirse a cambios

Un caso de uso común es ejecutar una función solo en caso de que los datos en cierto componente cambien. Usa la OnChange function para evitar tener que definir un system y tener que comparar explícitamente valores antiguos con nuevos.

Construyendo systems para usar un componente

Con tu componente definido y añadido a entidades en tu escena, puedes crear Sistemasarrow-up-right para realizar lógica, haciendo uso de estos datos almacenados en el componente.

El ejemplo anterior define un system que itera sobre todas las entidades que incluyen el componente personalizado wheelSpinComponent, y las rota ligeramente en cada tick del bucle del juego. La cantidad de esta rotación es proporcional al speed valor almacenado en la instancia del componente de cada entidad. El ejemplo hace uso de consultas de componente para obtener solo las entidades relevantes.

Última actualización