> 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/escenas-sdk7/arquitectura/custom-components.md).

# Custom Components

Los datos sobre una entity se almacenan en su [components](/creator/content-creator-es/escenas-sdk7/arquitectura/entities-components.md). El Decentraland SDK proporciona una serie de base components que gestionan diferentes aspectos de una entity, como su posición, shape, material, etc. El engine sabe cómo interpretar la información en estos y cambiará cómo se renderiza la entity en consecuencia en cuanto cambien sus valores.

Si la lógica de tu scene requiere almacenar información sobre una entity que no es manejada por los components predeterminados del SDK, entonces puedes crear un tipo personalizado de component en tu scene. Luego puedes construir [systems](/creator/content-creator-es/escenas-sdk7/arquitectura/systems.md) que comprueben los 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 string único que el SDK usa internamente para identificar este tipo de component. Puede ser cualquier string, siempre que sea único.
* Un **schema**: Una class que define la estructura de datos que contiene el component.
* **default values** *(opcional)*: Un objeto que contiene los default values que se usarán para inicializar una copia del component, cuando estos no se proporcionen.

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

{% hint style="warning" %}
**📔 Nota**: Los Custom Components siempre deben escribirse fuera de la `main()` function, en un archivo separado. Necesitan ser interpretados antes de que `main()` se ejecute. El lugar recomendado para esto es en una `/components` folder dentro de `/src`, cada uno en su propio archivo. Así es más fácil reutilizarlos en futuros projects.
{% endhint %}

Una vez que hayas definido un custom component, puedes crear instances de este component, que referencian entities en la scene. Cuando creas una instance de un component, proporcionas values para cada uno de los fields en el schema del component. Los values deben cumplir con los tipos declarados de cada field.

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

// Create instances of the component
WheelSpinComponent.create(wheel1, {
	spinning: true,
	speed: 10,
})

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

Cada entity a la que se le agrega el component instancia una nueva copia del component, que contiene datos específicos para esa entity.

Tu custom component también puede realizar las otras funciones comunes que están disponibles en otros components:

```ts
// Fetch a read only instance of the component from an entity
const readOnlyInstance = MyCustomComponent.get(myEntity)

// Fetch a mutable instance of the component from an entity
const mutableInstance = MyCustomComponent.getMutable(myEntity)

// Delete an entity's instance of the component
MyCustomComponent.deleteFrom(myEntity)
```

## Acerca de componentName

Cada component debe tener un nombre o identificador único de component, que lo diferencie internamente. No necesitarás usar este identificador interno en ninguna otra parte de tu código. Una buena práctica es usar el mismo nombre que asignas al component, pero empezando con una letra minúscula; sin embargo, lo 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 los components en tu library no deben coincidir con ningún nombre de component en el proyecto donde se usa, ni con otras libraries que también utilice ese proyecto. Para evitar el riesgo de cualquier coincidencia, la mejor práctica recomendada es incluir el nombre de la library como parte de la `componentName` string. Puedes seguir esta fórmula: `${packageName}::${componentName}`. Por ejemplo, si construyes una`MyUtilities` library que incluye un `MoveEntity` component, establece el `componentName` de ese component en `MyUtilities::moveEntity`.

## Components como flags

Es posible que quieras añadir un component que simplemente marque un entity para diferenciarlo de otros, sin usarlo para almacenar ningún dato. Para hacerlo, deja el schema como un objeto vacío.

Esto es especialmente útil al usar [querying components](/creator/content-creator-es/escenas-sdk7/arquitectura/querying-components.md). Un simple flag component puede usarse para distinguir entities de otras y evitar que el system itere sobre más entities de las necesarias.

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

Luego puedes crear un system que itere sobre todas las entities con este component.

```ts
export function handleEnemies() {
	for (const [entity] of engine.getEntitiesWith(IsEnemyFlag)) {
		// do something on each entity
	}
}

engine.addSystem(handleEnemies)
```

## Component Schemas

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

Cada field en el schema debe incluir una declaración de type. Solo puedes usar los special schema types proporcionados por el SDK. Por ejemplo, usa el type `Schemas.Boolean` en lugar de type `boolean`. Write `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 whose schema holds dos values, un `spinning` boolean y un `speed` floating point number.

Puedes elegir crear el schema inline mientras defines el component, o para mayor legibilidad puedes crearlo y luego referenciarlo.

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

// Option 2: define schema and component separately

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

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

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

### Default Schema types

Los siguientes basic types están disponibles para usarse dentro de los fields 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 complex types. Cada uno incluye una serie de nested properties con numerical values.

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

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

Por ejemplo, puedes usar estos schema types en un component como este para seguir el gradual movement de un entity. Este component almacena una initial y una final position como Vector3 values, así como una speed y una fraction del path completado como float numbers. Consulta [Mover entidades](/creator/content-creator-es/escenas-sdk7/conceptos-basicos-de-contenido-3d/move-entities.md#move-between-two-points) para ver la implementación completa de este example.

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

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

### Array types

Para set el type de un field como un array, usa `Schemas.Array()`. Pasa el type de los elements en el array como una property.

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

### Nested schema types

Para set el type de un field to be an object, usa `Schemas.Map()`. Pasa el contents de este object como una property. Este nested object es esencialmente un schema en sí mismo, nested dentro del parent schema.

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

Alternativamente, para que todo sea más legible y reusable, podrías lograr lo mismo definiendo el nested schema por separado y luego referenciándolo al definir el parent schema.

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

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

### Enums types

Puedes set que el type de un field en un schema sea un enum. Los enums facilitan elegir entre un número finito de opciones, proporcionando values legibles para cada una.

Para set que el type de un field sea un enum, primero debes definir el enum. Luego puedes referenciarlo usando `Schemas.EnumNumber` o `Schemas.EnumString`, según el type de enum. Estas functions toman dos parameters: el enum al que se hace referencia y un default value que se usará para este field.

```ts
//// String enum

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

// Define un component que usa este enum en un field
const ColorComponent = engine.defineComponent('Color', {
	color: Schemas.EnumString<Color>(Color, Color.Red),
})

// Usar el component en un entity
ColorComponent.create(engine.addEntity(), { color: Color.Green })

//// Number enum

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

// Define un component que usa este enum en un field
const CurveComponent = engine.defineComponent('curveComponent', {
	curve: Schemas.EnumNumber<CurveType>(CurveType, CurveType.LINEAR),
})

// Usar el component en un entity
CurveComponent.create(engine.addEntity(), { curve: CurveType.EASEIN })
```

### Interchangeable types

Puedes set que el type de un field en un schema siga un `oneOf` pattern, where different types can be accepted.

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

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

Al crear una instance del component, necesitas especificar el selected type con un `$case`, for example:

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

## Default values

A menudo es bueno tener default values en tus components, para que no sea necesario set explícitamente cada value cada vez que creas una nueva copy.

El `engine.defineComponent()` function toma un third argument, que te permite pasar un object con values que se usarán by default. Este object puede incluir todos o algunos de los values en el schema. Los fields que no estén cubiertos por los defaults ni por los values que proporciones al inicializar una copy del component se inicializan con un zero-like value, like `0`, `false`, o una cadena vacía, depending on the type.

```ts
// Definition

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

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

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

// Usage
export function main() {
	//// Create entities
	const wheel = engine.addEntity()
	const wheel2 = engine.addEntity()

	//// initialize component using default values
	WheelSpinComponent.create(wheel)

	//// initialize component with one custom value, using default for any others
	WheelSpinComponent.create(wheel2, { speed: 5 })
}
```

El example anterior crea un `WheelSpinComponent` component que incluye tanto un schema como un set de default values para usar. Si luego inicializas una copy de este component sin especificar ningún value, utilizará los establecidos en el default.

## Suscribirse a los cambios

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

```ts
export function main() {
	//Create entity, etc

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

## Construcción de systems para usar un component

Con tu component definido y agregado a entities en tu scene, puedes crear [Systems](/creator/content-creator-es/escenas-sdk7/arquitectura/systems.md) para realizar lógica, haciendo uso de estos data almacenados en el component.

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

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

	// Create instances of the component
	WheelSpinComponent.create(wheel1, {
		spinning: true,
		speed: 10,
	})

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

// Define un system para iterar sobre estas entities
export function spinSystem(dt: number) {
	// iterar sobre todas las entities 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 value de la rotation en consecuencia
			transform.rotation = Quaternion.multiply(
				transform.rotation,
				Quaternion.fromAngleAxis(dt * wheelSpin.speed, Vector3.Up())
			)
		}
	}
}

// Agrega el sistema al engine
engine.addSystem(spinSystem)
```

El ejemplo anterior define un system que itera sobre todas las entities que incluyen el custom `wheelSpinComponent`, y las rota ligeramente en cada tick del game loop. La cantidad de esta rotación es proporcional al `speed` valor almacenado en la instance del component de cada entity. El example hace uso de [component queries](/creator/content-creator-es/escenas-sdk7/arquitectura/querying-components.md) para obtener solo las entities 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, and the optional `goal` query parameter:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
