> 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-es/tiempo-de-ejecucion-de-la-escena/componentes/component-creation.md).

# Creación de componentes

Para crear nuevos componentes, necesitamos hacer un par de cosas antes de empezar a programarlos en el renderer. Hay una guía paso a paso que debes seguir. Voy a enumerar todos los pasos y luego podrás seguir cada paso con una explicación más detallada.

1. Crea la definición del proto en [@dcl/protocol](https://github.com/decentraland/protocol)
2. Genera el nuevo código TypeScript del proto en el [js-sdk-toolchain](https://github.com/decentraland/js-sdk-toolchain)
3. Crea una prueba para `js-sdk-toolchain`
4. Genera el nuevo código C# del proto en el proyecto
5. Programa el nuevo componente
6. Asegúrate de que el componente siga la convención

## Crea la definición del proto en [@dcl/protocol](https://github.com/decentraland/protocol)

Para crear una definición, debes ir a este repo: <https://github.com/decentraland/protocol>

1. Crea la definición del proto en esta carpeta: <https://github.com/decentraland/protocol/tree/main/ecs/components>
2. Crea un PR con los nuevos cambios

> ***NOTA:*** Después de crear el PR, un GitHub Bot comentará con el enlace del paquete para probar el PR. Puedes usar ese enlace para probarlo en los siguientes pasos

Cosas a tener en cuenta

* Estamos usando proto 3, así que toda la definición del proto debe compilar con su [sintaxis](https://developers.google.com/protocol-buffers/docs/proto3/)
* Tenemos algunos tipos comunes que no pueden recrearse
* El proto debe tener la definición básica
* Debes añadir el siguiente código para enumerar el componente

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

Ejemplo 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;
}
```

## Genera el nuevo código TypeScript del proto en el [js-sdk-toolchain](https://github.com/decentraland/js-sdk-toolchain)

Descarga el siguiente repo: <https://github.com/decentraland/js-sdk-toolchain>

Dentro de él, navega a `packages/dcl/ecs` (`https://github.com/decentraland/js-sdk-toolchain/tree/main/packages/%40dcl/ecs`)

Y allí, puedes ejecutar

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

O el comando generado por el GitHub Bot en tu PR de @dcl/protocol (esto debe ser temporal para probar el PR).

Luego ejecuta los siguientes comandos en la raíz del `js-sdk-toolchain` proyecto:

```
make install
make build
```

Y haz push del código generado.

## Genera el nuevo código C# del proto en el proyecto

Para generar el código C#, necesitamos ir a la `protocol-gen` ruta en la raíz del `@decentraland/unity-renderer` repositorio. Y ejecuta los siguientes comandos:

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

Para actualizar a la última versión de `@dcl/protocol` (rama main), deberíamos actualizar usando

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

Para probar un PR, podemos usar una URL generada por el GitHub Bot en el `@dcl/protocol` PR: Ejemplo:

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

Después de hacer merge del PR de @dcl/protocol, debemos usar `@dcl/protocol@next` y generar el código.

## Programa el nuevo componente

Ahora es el momento de implementar la funcionalidad del nuevo componente.

Los componentes tienen cinco partes esenciales.

* ComponentID El ID que usará el componente. Debe ser único y generado a partir de la definición del proto
* Model Este es el modelo del componente. Son los datos que usaremos para manejar el componente. Se ha generado automáticamente con la generación del proto y tiene el mismo nombre que el archivo .proto con un PB delante. Por ejemplo, si tienes una `BoxShape.proto` definición, la clase generada del modelo será `PBBoxShape`
* Component Handler El manejador del componente gestionará toda la funcionalidad del componente. En esta clase debes implementar `IECSComponentHandler<ModelClass>` (ModelClass es el modelo. Es una clase generada a partir del proto; el nombre será PB + el nombre del archivo .proto). Esta interfaz tiene 3 métodos importantes que debes implementar para crear un componente

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

* Serializer Cada componente es responsable de implementar su serialización y deserialización. Este serializer debe poder serializar/deserializar a un array de bytes
* Register Esto registrará el componente en el sistema, conectándolo al sistema. Este register registrará el componente en la factory y el component writer

El diseño de los componentes busca evitar la herencia, así que recomendamos usar funciones puras tanto como sea posible

Para crearlos, debemos seguir los siguientes pasos

1. Crea la carpeta del componente y el assembly. Tenemos todos los componentes dentro de la siguiente carpeta `DCLPlugins/ECS7/ECSComponents`. Debes crear una carpeta y un nuevo assembly que contendrá el componente
2. En el nuevo assembly, debes referenciar el siguiente `DCL.ECSComponents.Data`. Esto hará referencia al nuevo modelo del componente que acabas de actualizar
3. Debes crear el manejador del componente con toda la lógica (echa un vistazo a `ECSBoxShapeComponentHandler.cs` como ejemplo)
4. Debes crear la clase serializer (probablemente puedas copiarla de otra clase y adaptarla a la tuya)
5. Debes crear la clase 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. Añade el nuevo register a la `ECS7ComponentsComposer` clase con su ID correspondiente

```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();
        }
    }
```

¡Y ahora tienes tu componente añadido y funcionando!

## Asegúrate de que el componente siga la convención

Hay una lista de verificación que debemos tener en cuenta al desarrollar nuevos componentes; esta parte intenta resumirla.

* Prueba unitaria Todos los componentes deben incluir pruebas unitarias que cubran su funcionalidad, dispose y la serialización/deserialización, al menos para asegurar que el componente funcionará
* Ten en cuenta qué ocurre cuando el componente no está dentro de la Scene (consulta `SceneBoundariesChecker` la clase para más información)
* Si el componente renderiza algo en el mundo, debe añadir la información renderizable al data store; de este modo añadimos la información del renderer a la Scene para que cuente para el límite
* Si el componente renderiza algo en el mundo, debe añadir `MeshesInfo` a la entidad
* Debe ser lo más eficiente posible. Este código se ejecutará muchas veces, así que necesitamos asegurarnos de que todo funcione de la forma más fluida posible
* Debe funcionar con `Hot reload` en el modo Preview. Si has codificado `OnComponentRemoved` correctamente, esto funcionará de inmediato, pero el hot reload es una forma de probar que todo funciona bien con el dispose del componente
* AssetPromiseKeeper `. El componente debe notificar al`cuando el recurso se usa y cuando ya no se usa `. El componente debe notificar al` cuando el recurso se usa y cuando ya no se usa


---

# 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-es/tiempo-de-ejecucion-de-la-escena/componentes/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.
