> 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

As informações de uma entity são armazenadas em seus [components](/creator/content-creator-pt/scenes-sdk7/arquitetura/entities-components.md). O Decentraland SDK fornece uma série de base components que gerenciam diferentes aspectos de uma entity, como sua posição, forma, material etc. O engine sabe como interpretar as informações neles e mudará como a entity é renderizada de acordo, assim que eles mudarem seus valores.

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. Você então pode criar [systems](/creator/content-creator-pt/scenes-sdk7/arquitetura/systems.md) que verifiquem mudanças nesses components e reajam 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 único que o SDK usa internamente para identificar este tipo de component. Isso pode ser qualquer string, desde que seja única.
* A **schema**: Uma classe que define a estrutura de dados mantida pelo component.
* **valores padrão** *(opcional)*: Um objeto contendo valores padrão para usar ao 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" %}
**📔 Nota**: Os Custom Components devem sempre ser escritos fora da `main()` função, em um arquivo separado. Eles precisam ser interpretados antes de `main()` ser 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 fazem referência a 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 executar as outras funções comuns disponíveis em outros components:

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

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

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

## Sobre o componentName

Cada component deve ter um nome ou identificador único, que o diferencie internamente. Você não precisará usar esse identificador interno em nenhum outro 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 dos components na sua library não devem se sobrepor a nenhum nome de component no projeto em que ela está sendo usada, nem em outras libraries que também sejam usadas por esse projeto. Para evitar o risco de sobreposição, a prática recomendada é incluir o nome da library como parte da `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 sinalizadores

Você pode querer adicionar um component que simplesmente sinaliza uma entity para diferenciá-la das outras, sem usá-lo para armazenar nenhum dado. Para fazer isso, deixe o schema como um objeto vazio.

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

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

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

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

engine.addSystem(handleEnemies)
```

## Schemas de Component

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 o 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` boolean e um `speed` número de ponto flutuante.

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

```ts
// Opção 1: definição inline
export const WheelSpinComponent = engine.defineComponent('WheelSpinComponent', {
	spinning: Schemas.Boolean,
	speed: Schemas.Float,
})

// Opção 2: definir schema e component separadamente

//// 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 autocomplete 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 nos 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 acompanhar o movimento gradual de uma entity. Este component armazena uma posição inicial e uma posição final como valores Vector3, bem como uma speed e a fração do percurso concluído como números float. Veja [Mover entidades](/creator/content-creator-pt/scenes-sdk7/fundamentos-de-conteudo-3d/move-entities.md#move-between-two-points) para 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 no array como uma propriedade.

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

Quando você lê de volta um campo de array com `MyComponent.get()`, o array é somente leitura. Métodos que o alteram no próprio lugar, 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),
}
```

Apenas `undefined` conta como "não definido". Valores falsy como `false`, `0`, e `''` são armazenados e lidos de volta exatamente como foram 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,
	}),
}
```

Alternativamente, para manter tudo mais legível e reutilizável, 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 enums

Você pode definir o tipo de um campo em um schema como um enum. Enums tornam fácil selecionar 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, você deve primeiro 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 referenciar e um valor padrão a ser usado para este campo.

```ts
//// enum de string

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

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

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

//// enum numérico

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

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

// Usar 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 oneOf, 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 sem definir também é válido. Um `OneOf` campo OneOf não tem `$case` e é lido de volta 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`, não um component ausente.

## Valores padrão

É frequentemente bom ter valores padrão em seus components, para que não seja necessário definir explicitamente cada valor toda vez que você criar uma nova cópia.

Os `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 no schema. Os campos que não forem cobertos pelos padrões ou pelos valores que você fornecer 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,
}

//// padrões
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 component usando valores padrão
	WheelSpinComponent.create(wheel)

	//// inicializar component com um valor personalizado, usando o padrão para quaisquer outros
	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 deste component sem especificar quaisquer valores, ela usará aqueles definidos no padrão.

## Subscrever alterações

Um caso de uso comum é executar uma função apenas caso os dados em um determinado component mudem. Use a [OnChange](/creator/content-creator-pt/scenes-sdk7/arquitetura/subscribe-to-changes.md) função para evitar ter de definir um system e ter 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 o 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,
	})
}

// Definir 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
	)) {
		// fazer algo apenas se spinning == true
		if (wheelSpin.spinning) {
			// obter um Transform component mutável
			const transform = Transform.getMutable(entity)

			// atualizar 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 os 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 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-pt/scenes-sdk7/arquitetura/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.
