> 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-pt/scenes-sdk7/arquitetura/custom-components.md).

# Components Personalizados

Crie um component personalizado para lidar com dados específicos relacionados a uma entity

Os dados sobre uma Entity são armazenados no seu [components](/creator/content-creator-pt/scenes-sdk7/arquitetura/entities-components.md). O Decentraland SDK fornece uma série de components base que gerenciam diferentes aspectos sobre uma Entity, como sua posição, forma, material etc. O engine sabe como interpretar as informações nesses componentes e alterará como a Entity é renderizada de acordo assim que seus valores mudarem.

Se a lógica da sua Scene exigir armazenar informações sobre uma Entity que não seja tratada pelos components padrão do SDK, então você pode criar um tipo personalizado de component na sua Scene. Depois, você pode criar [systems](/creator/content-creator-pt/scenes-sdk7/arquitetura/systems.md) que verificam alterações nesses components e respondem de acordo.

## Sobre a definição de components

Para definir um novo component, use `engine.defineComponent`. Cada component precisa do seguinte:

* Um **componentName**: Um identificador de string exclusivo que o SDK usa internamente para identificar este tipo de component. Pode ser qualquer string, desde que seja única.
* Um **schema**: Uma classe que define a estrutura de dados mantida pelo component.
* **valores padrão** *(opcional)*: Um objeto contendo valores padrão a serem usados para inicializar uma cópia do component, quando estes não forem fornecidos.

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

{% hint style="warning" %}
**📔 Observação**: Custom Components devem sempre ser escritas fora do `main()` função, em um arquivo separado. Elas precisam ser interpretadas antes de `main()` é executado. O local recomendado para isso é em uma `/components` pasta dentro de `/src`, cada um em seu próprio arquivo. Dessa forma, fica mais fácil reutilizá-los em projetos futuros.
{% endhint %}

Depois de definir um custom component, você pode criar instâncias desse component, que referenciam Entities na Scene. Quando você cria uma instância de um component, você fornece valores para cada um dos campos no schema do component. Os valores devem estar de acordo com os tipos declarados de cada campo.

```ts
// Criar entidades
const wheel1 = engine.addEntity()
const wheel2 = engine.addEntity()

// Criar instâncias do component
WheelSpinComponent.create(wheel1, {
	spinning: true,
	speed: 10,
})

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

Cada Entity que tem o component adicionado instancia uma nova cópia do component, contendo dados específicos para essa Entity.

Seu custom component também pode realizar as outras funções comuns disponíveis em outros components:

```ts
// Obtenha uma instância somente leitura do component a partir de uma Entity
const readOnlyInstance = MyCustomComponent.get(myEntity)

// Obtenha uma instância mutável do component a partir de uma Entity
const mutableInstance = MyCustomComponent.getMutable(myEntity)

// Exclua a instância do component de uma Entity
MyCustomComponent.deleteFrom(myEntity)
```

## Sobre o componentName

Cada component deve ter um nome ou identificador exclusivo, que o diferencie internamente. Você não precisará usar esse identificador interno em mais nenhum lugar do seu código. Uma boa prática é usar o mesmo nome que você atribui ao component, mas começando com uma letra minúscula; porém, o que realmente importa é que esse identificador seja único dentro do projeto.

Ao criar components que serão compartilhados como parte de uma library, tenha em mente que os nomes de components na sua library não devem se sobrepor a nenhum nome de component no projeto em que ela está sendo usada, nem a outras libraries que também são usadas por esse projeto. Para evitar o risco de qualquer sobreposição, a melhor prática recomendada é incluir o nome da library como parte do `componentName` string. Você pode seguir esta fórmula: `${packageName}::${componentName}`. Por exemplo, se você criar uma`MyUtilities` library que inclui um `MoveEntity` component, defina o `componentName` desse component como `MyUtilities::moveEntity`.

## Components como flags

Talvez você queira adicionar um component que apenas sinalize uma Entity para diferenciá-la das outras, sem usá-lo para armazenar quaisquer dados. Para fazer isso, deixe o schema como um objeto vazio.

Isso é especialmente útil ao usar [consulta de components](/creator/content-creator-pt/scenes-sdk7/arquitetura/querying-components.md). Um component de flag simples pode ser usado para distinguir Entities de outras e evitar que o system percorra mais Entities do que o necessário.

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

Você pode então criar um system que itera sobre todas as Entities com este component.

```ts
export function handleEnemies() {
	for (const [entity] of engine.getEntitiesWith(IsEnemyFlag)) {
		// faça algo em cada Entity
	}
}

engine.addSystem(handleEnemies)
```

## Schemas de components

Um schema descreve a estrutura dos dados dentro de um component. Um component pode armazenar quantos campos você quiser; cada um deve ser incluído na estrutura do schema. O schema pode incluir quantos níveis de itens aninhados você precisar.

Cada campo no schema deve incluir uma declaração de tipo. Você só pode usar os tipos especiais de schema fornecidos pelo SDK. Por exemplo, use o tipo `Schemas.Boolean` em vez do tipo `boolean`. Escreva `Schemas.` e seu IDE exibirá todas as opções disponíveis.

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

O exemplo acima define um component cujo schema contém dois valores, um `spinning` booleano e um `speed` número de ponto flutuante.

Você pode optar por criar o schema inline ao definir o component, ou, para maior legibilidade, pode criá-lo e depois referenciá-lo.

```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" %}
**💡 Dica**: Ao criar uma instância de um component, as opções de autocompletar do VS Studio sugerirão quais campos você pode adicionar ao component pressionando *Ctrl + Space*.
{% endhint %}

### Tipos padrão de schema

Os seguintes tipos básicos estão disponíveis para uso dentro dos campos de um schema:

* `Schemas.Boolean`
* `Schemas.Byte`
* `Schemas.Double`
* `Schemas.Float`
* `Schemas.Int`
* `Schemas.Int64`
* `Schemas.Number`
* `Schemas.Short`
* `Schemas.String`
* `Schemas.Entity`

Os seguintes tipos complexos também existem. Cada um inclui uma série de propriedades aninhadas com valores numéricos.

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

{% hint style="info" %}
**💡 Dica**: Veja [Tipos de geometria](/creator/content-creator-pt/scenes-sdk7/fundamentos-de-conteudo-3d/special-types.md) e [Tipos de cor](/creator/content-creator-pt/scenes-sdk7/fundamentos-de-conteudo-3d/color-types.md) para mais detalhes sobre como esses tipos de dados são úteis.
{% endhint %}

Por exemplo, você pode usar esses tipos de schema em um component como este para rastrear o movimento gradual de uma Entity. Este component armazena uma posição inicial e uma final como valores Vector3, bem como uma velocidade e a fração do percurso concluído como números de ponto flutuante. Veja [Mover entities](/creator/content-creator-pt/scenes-sdk7/fundamentos-de-conteudo-3d/move-entities.md#move-between-two-points) a implementação completa deste exemplo.

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

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

### Tipos de array

Para definir o tipo de um campo como um array, use `Schemas.Array()`. Passe o tipo dos elementos do array como uma propriedade.

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

Quando você lê novamente um campo de array com `MyComponent.get()`, o array é somente leitura. Métodos que o modificam no local, como `.push()`, não estão disponíveis. Use `MyComponent.getMutable()` quando precisar alterar o conteúdo.

### Campos opcionais

Use `Schemas.Optional()` para permitir que um campo contenha um valor ou `undefined`.

```ts
const MySchema = {
	playerId: Schemas.Optional(Schemas.String),
	score: Schemas.Optional(Schemas.Int),
}
```

Somente `undefined` conta como "não definido". Valores falsy como `false`, `0`, e `''` são armazenados e lidos exatamente como escritos.

### Tipos de schema aninhados

Para definir o tipo de um campo como um objeto, use `Schemas.Map()`. Passe o conteúdo deste objeto como uma propriedade. Esse objeto aninhado é essencialmente um schema em si, aninhado dentro do schema pai.

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

Como alternativa, para deixar as coisas mais legíveis e reutilizáveis, você pode obter o mesmo resultado definindo o schema aninhado separadamente e depois referenciando-o ao definir o schema pai.

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

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

### Tipos de enum

Você pode definir o tipo de um campo em um schema como um enum. Enums facilitam a seleção entre um número finito de opções, fornecendo valores legíveis por humanos para cada uma.

Para definir o tipo de um campo como um enum, primeiro você deve definir o enum. Depois, você pode referenciá-lo usando `Schemas.EnumNumber` ou `Schemas.EnumString`, dependendo do tipo de enum. Essas funções recebem dois parâmetros: o enum a ser referenciado e um valor padrão a ser usado para este campo.

```ts
//// enum de String

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

// Defina um component que use este enum em um campo
const ColorComponent = engine.defineComponent('Color', {
	color: Schemas.EnumString<Color>(Color, Color.Red),
})

// Use o component em uma Entity
ColorComponent.create(engine.addEntity(), { color: Color.Green })

//// enum numérico

// Defina o enum
enum CurveType {
	LINEAR,
	EASEIN,
	EASEOUT,
}

// Defina um component que use este enum em um campo
const CurveComponent = engine.defineComponent('curveComponent', {
	curve: Schemas.EnumNumber<CurveType>(CurveType, CurveType.LINEAR),
})

// Use o component em uma Entity
CurveComponent.create(engine.addEntity(), { curve: CurveType.EASEIN })
```

### Tipos intercambiáveis

Você pode definir o tipo de um campo em um schema para seguir um `oneOf` padrão, no qual diferentes tipos podem ser aceitos.

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

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

Ao criar uma instância do component, você precisa especificar o tipo selecionado com um `$case`, por exemplo:

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

Deixar o campo não definido também é válido. Um `OneOf` campo não tem `$case` e é lido novamente como um objeto vazio, `{}`.

### Components de um único tipo

Um component não precisa conter um objeto com vários campos. Para definir um que contenha um único valor, use `engine.defineComponentFromSchema()` e passe o tipo diretamente:

```ts
// Um component que contém um número por Entity
export const Score = engine.defineComponentFromSchema('my-scene::Score', Schemas.Int)

// Um component que contém uma lista de números por Entity
export const History = engine.defineComponentFromSchema(
	'my-scene::History',
	Schemas.Array(Schemas.Int)
)
```

Eles se comportam como qualquer outro component, inclusive quando o valor armazenado é falsy. Um `Score` de `0` é um component que existe e contém `0`, e não um component ausente.

## Valores padrão

Muitas vezes é bom ter valores padrão nos seus components, para que não seja necessário definir explicitamente cada valor sempre que você criar uma nova cópia.

O `engine.defineComponent()` função recebe um terceiro argumento, que permite passar um objeto com valores a serem usados por padrão. Esse objeto pode incluir alguns ou todos os valores do schema. Campos que não são cobertos pelos valores padrão nem pelos valores que você fornece ao inicializar uma cópia do component são inicializados com um valor semelhante a zero, como `0`, `false`, ou uma string vazia, dependendo do tipo.

```ts
// Definição

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

//// valores padrão
const myDefaultValues = {
	spinning: true,
	speed: 1,
}

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

// Uso
export function main() {
	//// Criar Entities
	const wheel = engine.addEntity()
	const wheel2 = engine.addEntity()

	//// inicializar o component usando valores padrão
	WheelSpinComponent.create(wheel)

	//// inicializar o component com um valor personalizado, usando o padrão para os demais
	WheelSpinComponent.create(wheel2, { speed: 5 })
}
```

O exemplo acima cria um `WheelSpinComponent` component que inclui tanto um schema quanto um conjunto de valores padrão a serem usados. Se você então inicializar uma cópia desse component sem especificar nenhum valor, ela usará os definidos no padrão.

## Inscreva-se nas alterações

Um caso de uso comum é executar uma function apenas se os dados de um determinado component mudarem. Use a [OnChange](/creator/content-creator-pt/scenes-sdk7/arquitetura/subscribe-to-changes.md) function para evitar ter de definir um system e de comparar explicitamente valores antigos com valores novos.

```ts
export function main() {
	//Criar Entity, etc

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

## Construindo systems para usar um component

Com seu component definido e adicionado a Entities na sua Scene, você pode criar [Systems](/creator/content-creator-pt/scenes-sdk7/arquitetura/systems.md) para executar lógica, fazendo uso desses dados armazenados no component.

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

// Uso
export function main() {
	// Criar entidades
	const wheel1 = engine.addEntity()
	const wheel2 = engine.addEntity()

	// Criar instâncias do component
	WheelSpinComponent.create(wheel1, {
		spinning: true,
		speed: 10,
	})

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

// Defina um system para iterar sobre essas Entities
export function spinSystem(dt: number) {
	// iterar sobre todas as Entities com um WheelSpinComponent
	for (const [entity, wheelSpin] of engine.getEntitiesWith(
		WheelSpinComponent
	)) {
		// faça algo somente se spinning == true
		if (wheelSpin.spinning) {
			// obter um componente Transform mutável
			const transform = Transform.getMutable(entity)

			// atualize o valor da rotação de acordo
			transform.rotation = Quaternion.multiply(
				transform.rotation,
				Quaternion.fromAngleAxis(dt * wheelSpin.speed, Vector3.Up())
			)
		}
	}
}

// adicionar system ao engine
engine.addSystem(spinSystem)
```

O exemplo acima define um system que itera sobre todas as Entities que incluem o custom `wheelSpinComponent`, e as rotaciona ligeiramente a cada tick do game loop. A quantidade dessa rotação é proporcional ao `speed` valor armazenado na instância do component de cada Entity. O exemplo faz uso de [consultas de components](/creator/content-creator-pt/scenes-sdk7/arquitetura/querying-components.md) para obter apenas as 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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.decentraland.org/creator/content-creator-pt/scenes-sdk7/arquitetura/custom-components.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
