> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/creator/content-creator-es/scenes-sdk7/arquitectura/custom-components.md).

# Components personalizados

Los datos sobre una entidad se almacenan en su [componentes](/creator/content-creator-es/scenes-sdk7/arquitectura/entities-components.md). El SDK de Decentraland proporciona una serie de Components base que gestionan distintos 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 Render 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 es gestionada por los Components predeterminados del SDK, entonces puedes crear un tipo personalizado de Component en tu escena. Luego puedes construir [systems](/creator/content-creator-es/scenes-sdk7/arquitectura/systems.md) que verifiquen cambios en estos Components y respondan en consecuencia.

## Acerca de definir Components

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

* Un **componentName**: Un identificador de cadena único que el SDK usa internamente para identificar este tipo de Component. Puede ser cualquier cadena, siempre que sea única.
* A **schema**: Una clase que define la estructura de datos contenida por el Component.
* **valores predeterminados** *(opcional)*: Un objeto que contiene valores predeterminados para usar al inicializar una copia del Component, cuando estos no se proporcionan.

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

{% hint style="warning" %}
**📔 Nota**: Los Custom Components siempre deben escribirse fuera del `main()` función, en un archivo separado. Necesitan ser interpretadas antes de que se ejecute `main()` se ejecuta. El lugar recomendado para esto es en una carpeta `/components` dentro de `/src`, cada uno en su propio archivo. De ese modo es más fácil reutilizarlos en proyectos futuros.
{% endhint %}

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

```ts
// Crear entities
const wheel = engine.addEntity()
const wheel2 = engine.addEntity()

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

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

Cada entidad que tiene el Component agregado instancia una nueva copia del Component, que contiene datos específicos para esa entidad.

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

```ts
// Obtener una instancia de solo lectura del Component desde una entidad
const readOnlyInstance MyCustomComponent.get(myEntity)

// Obtener una instancia mutable del Component desde una entidad
const readOnlyInstance MyCustomComponent.getMutable(myEntity)

// Eliminar la instancia del Component de una entidad
const readOnlyInstance MyCustomComponent.deleteFrom(myEntity)

```

## Acerca del componentName

Cada Component debe tener un nombre o identificador de Component ú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 Component, pero comenzando con una letra minúscula, pero lo único que realmente importa es que este identificador sea único dentro del proyecto.

Al crear Components que se compartirán como parte de una Library, ten en cuenta que los nombres de Component en tu Library no deben superponerse con ningún nombre de Component en el proyecto donde se usa, ni con otras Libraries que también use ese proyecto. Para evitar el riesgo de cualquier superposición, la mejor práctica recomendada es incluir el nombre de la Library como parte de la cadena `componentName` string. Puedes seguir esta fórmula: `${packageName}::${componentName}`. Por ejemplo, si construyes una Library`MyUtilities` que incluye un Component `MoveEntity` Component, establece el `componentName` de ese Component en `MyUtilities::moveEntity`.

## Components como flags

Puede que quieras agregar un Component 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 al usar [querying components](/creator/content-creator-es/scenes-sdk7/arquitectura/querying-components.md). Un Component flag simple puede usarse para distinguir unas entidades de otras, y evitar que el System itere sobre más entidades de las necesarias.

```ts
export const IsEnemyFlag = engine.defineComponent('isEnemyFlag', {})
```

Luego puedes crear un System que itere sobre todas las entidades con este Component.

```ts
export function handleEnemies() {
	for (const [entity] of engine.getEntitiesWith(IsEnemyFlag)) {
		// hacer algo en cada entidad
	}
}

engine.addSystem(handleEnemies)
```

## Schemas de Component

Un schema describe la estructura de los datos dentro de un Component. Un Component 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.

```ts
export const WheelSpinComponent = engine.defineComponent('WheelSpinComponent', {
	spinning: Schemas.Boolean,
	speed: Schemas.Float,
})
```

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

Puedes elegir crear el schema en línea mientras defines el Component, o para mayor legibilidad puedes crearlo y luego referenciarlo.

```ts
// Opción 1: definición en línea
export const WheelSpinComponent = engine.defineComponent('WheelSpinComponent', {
	spinning: Schemas.Boolean,
	speed: Schemas.Float,
})

// Opción 2: definir schema y Component por separado

//// schema
const mySchema = {
	spinning: Schemas.Boolean,
	speed: Schemas.Float,
}

//// component
export const WheelSpinComponent = engine.defineComponent(
	'WheelSpinComponent',
	mySchema
)
```

{% hint style="info" %}
**💡 Consejo**: Al crear una instancia de un Component, las opciones de autocompletado de VS Studio sugerirán qué campos puedes agregar al Component presionando *Ctrl + Space*.
{% endhint %}

### Tipos de schema predeterminados

Los siguientes tipos básicos están disponibles para usarse 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`

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

* `Schemas.Vector3`
* `Schemas.Quaternion`
* `Schemas.Color3`
* `Schemas.Color4`

{% hint style="info" %}
**💡 Consejo**: Consulta [Tipos de geometría](/creator/content-creator-es/scenes-sdk7/conceptos-basicos-de-contenido-3d/special-types.md) y [Tipos de color](/creator/content-creator-es/scenes-sdk7/conceptos-basicos-de-contenido-3d/color-types.md) para más detalles sobre cómo estos tipos de datos son útiles.
{% endhint %}

Por ejemplo, puedes usar estos tipos de schema en un Component como este para rastrear el movimiento gradual de una entidad. Este Component almacena una posición inicial y una final como valores Vector3, así como una velocidad y fracción del recorrido completado como números float. Consulta [Mover entities](/creator/content-creator-es/scenes-sdk7/conceptos-basicos-de-contenido-3d/move-entities.md#move-between-two-points) para la implementación completa de este ejemplo.

```ts
const MoveTransportData = {
	start: Schemas.Vector3,
	end: Schemas.Vector3,
	fraction: Schemas.Float,
	speed: Schemas.Float,
}

export const LerpTransformComponent = engine.defineComponent(
	'LerpTransformComponent',
	MoveTransportData
)
```

### Tipos Array

Para establecer el tipo de un campo como un array, usa `Schemas.Array()`. Pasa el tipo de los elementos del array como una propiedad.

```ts
const MySchema = {
	numberList: Schemas.Array(Schemas.Int),
}
```

### Tipos de schema anidados

Para establecer el tipo de un campo para que sea 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 principal.

```ts
const MySchema = {
	simpleField: Schemas.Boolean,
	myComplexField: Schemas.Map({
		nestedField1: Schemas.Boolean,
		nestedField2: Schemas.Boolean
	})}
}
```

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 principal.

```ts
const MyNestedSchema = Schemas.Map({
	nestedField1: Schemas.Boolean,
	nestedField2: Schemas.Boolean,
})

const MySchema = {
	simpleField: Schemas.Boolean,
	myComplexField: MyNestedSchema,
}
```

### 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 uno.

Para establecer el tipo de un campo como un enum, primero debes definir el enum. Luego puedes referenciarlo usando `Schemas.EnumNumber` o `Schemas.EnumString`, según el 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 predeterminado para usar en este campo.

```ts
//// String enum

// Definir enum
enum Color {
	Red = 'red',
	Green = 'green',
	Pink = 'pink',
}

// Definir un Component que use este enum en un campo
const ColorComponent = engine.defineComponent('Color', {
	color: Schemas.EnumString<Color>(Color, Color.Red),
})

// Usar el Component en una entidad
ColorComponent.create(engine.addEntity(), { color: Color.Green })

//// Number enum

// Definir enum
enum CurveType {
	LINEAR,
	EASEIN,
	EASEOUT,
}

// Definir un Component que use este enum en un campo
const CurveComponent = engine.defineComponent('curveComponent', {
	curve: Schemas.EnumString<CurveType>(CurveType, CurveType.LINEAR),
})

// Usar el Component en una entidad
CurveComponent.create(engine.addEntity(), { curve: CurveType.EASEIN })
```

### Tipos intercambiables

Puedes establecer el tipo de un campo en un schema para que siga un patrón `oneOf` , donde se pueden aceptar distintos tipos.

```ts
const MySchema = {
	myField: Schemas.OneOf({ type1: Schemas.Vector3, type2: Schemas.Quaternion }),
}

export const MyComponent = engine.defineComponent('MyComponent', MySchema)
```

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

```ts
MyComponent.create(myEntity, {
	myField: {
		$case: type1
		value: Vector3.create(1, 1, 1)
	}
})
```

## Valores predeterminados

A menudo es bueno tener valores predeterminados en tus Components, 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 de forma predeterminada. Este objeto puede incluir algunos o todos los valores del schema. Los valores que no se proporcionen en los predeterminados deberán proporcionarse siempre al inicializar una copia del Component.

```ts
// Definición

//// schema
const mySchema = {
	spinning: Schemas.Boolean,
	speed: Schemas.Float,
}

//// valores predeterminados
const myDefaultValues = {
	spinning: true,
	speed: 1,
}

//// component
export const WheelSpinComponent = engine.defineComponent(
	'WheelSpinComponent',
	mySchema,
	myDefaultValues
)

// Uso
export function main() {
	//// Crear entidades
	const wheel = engine.addEntity()
	const wheel2 = engine.addEntity()

	//// inicializar Component usando valores predeterminados
	WheelSpinComponent.create(wheel)

	//// inicializar Component con un valor personalizado, usando el valor predeterminado para cualquier otro
	WheelSpinComponent.create(wheel2, { speed: 5 })
}
```

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

## Suscribirse a cambios

Un caso de uso común es ejecutar una function solo si los datos de un cierto component cambian. Usa la [OnChange](/creator/content-creator-es/scenes-sdk7/arquitectura/subscribe-to-changes.md) function para evitar tener que definir un system y tener que comparar explícitamente valores antiguos con nuevos valores.

```ts
export function main() {
	//Crear entidad, etc

	WheelSpinComponent.onChange(myEntity, (componentData) => {
		if (!componentData) return
		console.log(componentData.speed)
		console.log(componentData.spinning)
	})
}
```

## Construir Systems para usar un Component

Con tu Component definido y agregado a entidades en tu escena, puedes crear [Systems](https://github.com/decentraland/docs/blob/main/creator/deprecated/scenes/architecture/systems.md) para realizar lógica, haciendo uso de estos datos almacenados en el Component.

```ts
// definir Component
export const WheelSpinComponent = engine.defineComponent('WheelSpinComponent', {
	spinning: Schemas.Boolean,
	speed: Schemas.Float,
})

// Uso
export function main() {
	// Crear entities
	const wheel1 = engine.addEntity()
	const wheel2 = engine.addEntity()

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

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

// Definir un System para iterar sobre estas entidades
export function spinSystem(dt: number) {
	// iterar sobre todas las entidades con un WheelSpinComponent
	for (const [entity, wheelSpin] of engine.getEntitiesWith(
		WheelSpinComponent
	)) {
		// solo hacer algo si spinning == true
		if (wheelSpin.spinning) {
			// obtener un componente Transform mutable
			const transform = Transform.getMutable(entity)

			// actualizar el valor de rotación en consecuencia
			transform.rotation = Quaternion.multiply(
				transform.rotation,
				Quaternion.fromAngleAxis(dt * wheelSpin.speed, Vector3.Up())
			)
		}
	}
}

// Añadir el system al engine
engine.addSystem(spinSystem)
```

El ejemplo anterior define un System que itera sobre todas las entidades que incluyen el `wheelSpinComponent`, y las rota ligeramente en cada tick del game loop. La cantidad de esta rotación es proporcional al valor `speed` almacenado en la instancia del Component de cada entidad. El ejemplo hace uso de [component queries](/creator/content-creator-es/scenes-sdk7/arquitectura/querying-components.md) para obtener solo las entidades relevantes.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.decentraland.org/creator/content-creator-es/scenes-sdk7/arquitectura/custom-components.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
