> 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/contributor/contributor-pt/runtime-da-scene/components/component-creation.md).

# Criação de Components

Para criar novos components, precisamos de fazer algumas coisas antes de começar a codificá-los no renderer. Existe um guia passo a passo que precisa de seguir. Vou listar todos os passos e, depois, pode seguir cada passo com uma explicação mais detalhada.

1. Crie a definição do proto em [@dcl/protocol](https://github.com/decentraland/protocol)
2. Gere o novo código TypeScript do proto no [js-sdk-toolchain](https://github.com/decentraland/js-sdk-toolchain)
3. Crie um teste para `js-sdk-toolchain`
4. Gere o novo código C# do proto no projeto
5. Codifique o novo component
6. Garanta que o component segue a convention

## Crie a definição do proto em [@dcl/protocol](https://github.com/decentraland/protocol)

Para criar uma definição, tem de ir a este repo: <https://github.com/decentraland/protocol>

1. Crie a definição do proto nesta folder: <https://github.com/decentraland/protocol/tree/main/ecs/components>
2. Crie um PR com as novas alterações

> ***NOTA:*** Depois de criar o PR, um GitHub Bot irá comentar com o link do package para testar o PR. Pode usar esse link para testar nos passos seguintes

Coisas a ter em conta

* Estamos a usar proto 3, por isso toda a definição do proto tem de compilar com a sua [syntax](https://developers.google.com/protocol-buffers/docs/proto3/)
* Temos alguns tipos comuns que não podem ser recriados
* O proto deve ter a definição básica
* Tem de adicionar o seguinte código para enumerar o component

```
import "common/id.proto";
option (ecs_component_id) = 1100;
```

Exemplo de .proto

```
syntax = "proto3";

import "common/id.proto";
option (ecs_component_id) = 1020;

message PBAudioSource {
  optional bool playing = 1;
  optional float volume = 2; // default=1.0f
  optional bool loop = 3;
  optional float pitch = 4; // default=1.0f
  string audio_clip_url = 5;
}
```

## Gere o novo código TypeScript do proto no [js-sdk-toolchain](https://github.com/decentraland/js-sdk-toolchain)

Descarregue o seguinte repo: <https://github.com/decentraland/js-sdk-toolchain>

Dentro dele, navegue até `packages/dcl/ecs` (`https://github.com/decentraland/js-sdk-toolchain/tree/main/packages/%40dcl/ecs`)

E, aí, pode executar

```
npm install @dcl/protocol@next
```

Ou o comando gerado pelo GitHub Bot no seu PR de @dcl/protocol (isto tem de ser temporário para testar os PRs).

Depois, execute os seguintes comandos na raiz do `js-sdk-toolchain` projeto:

```
make install
make build
```

E faça push do código gerado.

## Gere o novo código C# do proto no projeto

Para gerar o código C#, precisamos de ir para o `protocol-gen` path na raiz do `@decentraland/unity-renderer` repositório. E execute os seguintes comandos:

```
npm install
npm run build-components
```

Para atualizar para a versão mais recente do `@dcl/protocol` (main branch), devemos atualizar usando

```
npm install @dcl/protocol@next
npm run build-components
```

Para testar um PR, podemos usar uma URL gerada pelo GitHub Bot no `@dcl/protocol` PR: Exemplo:

```
npm install "https://sdk-team-cdn.decentraland.org/@dcl/protocol/branch//dcl-protocol-1.0.0-3143233696.commit-45f1290.tgz"
npm run build-components
```

Depois de fazer merge do PR de @dcl/protocol, devemos usar o `@dcl/protocol@next` e gerar o código.

## Codifique o novo component

Agora é tempo de implementar a funcionalidade do novo component.

Os components têm cinco partes essenciais.

* ComponentID O ID que o component vai usar. Tem de ser único e gerado a partir da definição do proto
* Model Este é o model do component. São os dados que vamos usar para lidar com o component. Foi gerado automaticamente com a geração do proto e tem o mesmo nome do ficheiro do proto com um PB na frente. Por exemplo, se tiver um `BoxShape.proto` definition, a classe gerada do model será `PBBoxShape`
* Component Handler O Component Handler vai gerir toda a funcionalidade do component. Nesta class deve implementar o `IECSComponentHandler<ModelClass>` (ModelClass é o model. É uma class gerada a partir do proto, o nome será PB + nome do ficheiro .proto). Esta interface tem 3 methods que são importantes de implementar para criar um component

```
        void OnComponentCreated(IParcelScene scene, IDCLEntity entity);
        void OnComponentRemoved(IParcelScene scene, IDCLEntity entity);
        void OnComponentModelUpdated(IParcelScene scene, IDCLEntity entity, ModelType model);
```

* Serializer Cada component é responsável por implementar a sua serialização e desserialização do component. Este serializer deve ser capaz de serializar/desserializar para byte array
* Register Este irá registar o component no system, ligando-o ao system. Este register irá registar o component na factory e no component writer

O design dos components é evitar herança, por isso incentivamos o uso de pure functions tanto quanto possível

Para os criar, temos de seguir os próximos passos

1. Crie a folder do component e o assembly. Temos todos os components na seguinte folder `DCLPlugins/ECS7/ECSComponents`. Precisa de criar uma folder e um novo assembly que irá conter o component
2. No novo assembly, deve referenciar o seguinte `DCL.ECSComponents.Data`. Isso irá referenciar o novo model do component que acabou de atualizar
3. Tem de criar o component handler com toda a lógica (Veja `ECSBoxShapeComponentHandler.cs` como exemplo)
4. Tem de criar a class serializer (provavelmente pode copiá-la de outra class e adaptá-la à sua)
5. Tem de criar a class register

```sh
   public static class AudioSourceSerializer
    {
        public static byte[] Serialize(PBAudioSource model)
        {
            int size = model.CalculateSize();
            byte[] buffer = new byte[size];
            CodedOutputStream output = new CodedOutputStream(buffer);
            model.WriteTo(output);
            return buffer;
        }

        public static PBAudioSource Deserialize(object data)
        {
            return PBAudioSource.Parser.ParseFrom((byte[])data);
        }
    }
```

6. Adicione o novo register ao `ECS7ComponentsComposer` class com o seu ID correspondente

```sh
   public class ECS7ComponentsComposer : IDisposable
    {
        private readonly TransformRegister transformRegister;
        private readonly SphereShapeRegister sphereShapeRegister;
        private readonly BoxShapeRegister boxShapeRegister;
        private readonly PlaneShapeRegister planeShapeRegister;
        private readonly CylinderShapeRegister cylinderShapeRegister;
        private readonly AudioStreamRegister audioStreamRegister;
        private readonly AudioSourceRegister audioSourceRegister;

        public ECS7ComponentsComposer(ECSComponentsFactory componentsFactory, IECSComponentWriter componentsWriter)
        {
            transformRegister = new TransformRegister(ComponentID.TRANSFORM, componentsFactory, componentsWriter);
            sphereShapeRegister = new SphereShapeRegister(ComponentID.SPHERE_SHAPE, componentsFactory, componentsWriter);
            boxShapeRegister = new BoxShapeRegister(ComponentID.BOX_SHAPE, componentsFactory, componentsWriter);
            planeShapeRegister = new PlaneShapeRegister(ComponentID.PLANE_SHAPE, componentsFactory, componentsWriter);
            cylinderShapeRegister = new CylinderShapeRegister(ComponentID.CYLINDER_SHAPE, componentsFactory, componentsWriter);
            audioStreamRegister = new AudioStreamRegister(ComponentID.AUDIO_STREAM, componentsFactory, componentsWriter);
            audioSourceRegister = new AudioSourceRegister(ComponentID.AUDIO_SOURCE, componentsFactory, componentsWriter);
        }

        public void Dispose()
        {
            transformRegister.Dispose();
            sphereShapeRegister.Dispose();
            boxShapeRegister.Dispose();
            planeShapeRegister.Dispose();
            cylinderShapeRegister.Dispose();
            audioStreamRegister.Dispose();
            audioSourceRegister.Dispose();
        }
    }
```

E agora já tem o seu component adicionado e a funcionar!

## Garanta que o component segue a convention

Há uma checklist que temos de ter em conta ao desenvolver novos components; esta parte tenta resumi-la.

* Unit test Todos os components devem incluir unit tests que cubram a sua funcionalidade, dispose e a serialização/desserialização, pelo menos, para garantir que o component vai funcionar
* Tenha em conta o que acontece quando o component não está dentro da scene (Consulte `SceneBoundariesChecker` class para mais informações)
* Se o component renderiza algo no world, tem de adicionar a informação renderizável ao data store; desta forma, adicionamos a informação do renderer à scene para que conte para o limite
* Se o component renderiza algo no world, tem de adicionar o `MeshesInfo` à entity
* Tem de ser o mais performante possível. Este code será executado muitas vezes, por isso precisamos de garantir que tudo funcione da forma mais fluida possível
* Tem de funcionar com `Hot reload` no modo de preview. Se tiver codificado o `OnComponentRemoved` corretamente, isto funcionará de imediato, mas o hot reload é uma forma de testar se tudo funciona bem com o dispose do component
* Se o component usa um resource, tem de implementar a gestão desse resource com um `AssetPromiseKeeper`. O component deve notificar o `AssetPromiseKeeper` quando o resource é usado e quando já não é usado


---

# 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/contributor/contributor-pt/runtime-da-scene/components/component-creation.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.
