Componentes personalizados

Crea un componente personalizado para manejar datos específicos relacionados con una entidad

Los datos sobre una entidad se almacenan en su componentsarrow-up-right. 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á la forma en que la entidad se renderiza en consecuencia tan pronto como cambien sus valores.

Si la lógica de tu escena requiere almacenar información sobre una entidad que no es manejada por los componentes por defecto del SDK, entonces puedes crear un tipo personalizado de componente en tu escena. Luego puedes construir systemsarrow-up-right que verifiquen 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 estos no son proporcionados.

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

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

// Create entities
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, conteniendo 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 empezando con una letra minúscula, aunque lo que realmente importa es que este identificador sea único dentro del proyecto.

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

Componentes como banderas

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 componentesarrow-up-right. Un componente bandera 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 system 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 estar incluido 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 especiales de schema 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

💡 Consejo: 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

💡 Consejo: Ver Tipos de geometríaarrow-up-right y Tipos de colorarrow-up-right 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 seguir el movimiento gradual de una entidad. Este componente almacena una posición inicial y una final como valores Vector3, así como una velocidad y una fracción del recorrido completado como números float. Ver Mover entidadesarrow-up-right para la implementación completa de este ejemplo.

Tipos de arreglo

Para establecer el tipo de un campo como un arreglo, usa Schemas.Array(). Pasa el tipo de los elementos en el arreglo como una 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í mismo, 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 para que sea un enum. Los enums facilitan seleccionar entre un número finito de opciones, proporcionando valores legibles para humanos 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 u Schemas.EnumString, dependiendo del tipo de enum. Debes pasar el enum a la referencia 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 OnChangearrow-up-right función para evitar tener que definir un system y tener que comparar explícitamente valores antiguos con nuevos.

Construir systems para usar un componente

Con tu componente definido y añadido a entidades en tu escena, puedes crear Systemsarrow-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 custom 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 componentesarrow-up-right para obtener solo las entidades relevantes.

Última actualización