# UI en pantalla

Puedes construir una UI para tu escena, para mostrarse en el espacio 2D fijo de la pantalla, en lugar de en el espacio 3D del mundo.

Los elementos de la UI solo son visibles cuando el jugador está dentro de los LAND del lote de la escena, ya que las escenas vecinas podrían tener su propia UI para mostrar. Partes de la UI también pueden activarse para abrirse cuando ocurren ciertos eventos en el espacio del mundo, por ejemplo si el jugador hace clic en un lugar específico.

Construye una UI definiendo una estructura de `UIEntity` objetos anidados en JSX. La sintaxis usada para las UIs es muy similar a la de [React](https://reactjs.org/) (una biblioteca basada en javascript muy popular para construir UIs web).

{% hint style="warning" %}
**📔 Nota**: Solo puedes definir sintaxis de UI en archivos que tengan una `.tsx` extensión. `.tsx` los archivos soportan todo lo que `.ts` los archivos soportan, además de la sintaxis de UI. Recomendamos crear un `ui.tsx` archivo y definir tu UI allí. Recuerda llamar al método de render de tu UI desde `index.ts` con `ReactEcsRenderer.setUiRenderer(yourUiMethodName)`, ver ejemplo abajo.
{% endhint %}

Una UI simple con elementos estáticos puede parecerse mucho al HTML, pero cuando agregas elementos dinámicos que responden a un cambio de estado, puedes hacer cosas mucho más poderosas.

&#x20;La UI por defecto del Explorer de Decentraland incluye un widget de chat, un mapa y otros elementos. Estos elementos de UI siempre se muestran en la capa superior, por encima de cualquier UI específica de la escena. Entonces, si tu escena tiene elementos de UI que ocupan el mismo espacio de pantalla que éstos, serán ocultados.

Ver [UX guidelines](https://docs.decentraland.org/creator/content-creator-es/scenes-sdk7/disenando-la-experiencia/ux-ui-guide) para consejos sobre cómo diseñar el aspecto y la sensación de tu UI.

Cuando el jugador hace clic el *botón cerrar UI* en la esquina inferior derecha de la pantalla, todos los elementos de la UI se ocultan.

## Renderizar una UI

Para mostrar una UI en tu escena, utiliza `ReactEcsRenderer.setUiRenderer()` la función, pasándole una estructura válida de entidades, descrita en un `.tsx` archivo.

Cada entidad se define como un nodo similar a HTML, con propiedades para cada uno de sus componentes.

***archivo ui.tsx:***

```ts
import { UiEntity, ReactEcs } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export const uiMenu = () => (
  <UiEntity
    uiTransform={{
      width: 700,
      height: 400,
      margin: { top: '35px', left: '500px' },
    }}
    uiBackground={{ color: Color4.Red() }}
  />
)
```

***archivo index.ts:***

```ts
import { ReactEcsRenderer } from '@dcl/sdk/react-ecs'
import { uiMenu } from './ui'

export function main() {
    ReactEcsRenderer.setUiRenderer(uiMenu, { virtualWidth: 1920, virtualHeight: 1080 })
}
```

También puedes definir una estructura de entidad y renderizarla, todo en un mismo comando en un `.tsx` archivo.

***archivo ui.tsx:***

```tsx
import ReactEcs, { ReactEcsRenderer, UiEntity } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export function setupUI() {
  ReactEcsRenderer.setUiRenderer(() => (
    <UiEntity
      uiTransform={{
        width: 700,
        height: 400,
        margin: { top: '35px', left: '500px' },
      }}
      uiBackground={{ color: Color4.Red() }}
    />
  ), { virtualWidth: 1920, virtualHeight: 1080 })
}
```

***archivo index.ts:***

```ts
import { setupUI } from './ui'

export function main() {
    setupUI()
}
```

{% hint style="warning" %}
**📔 Nota**: Todos tus elementos de UI deben estar anidados en la misma estructura, y tener un único padre en la raíz de la estructura. Solo puedes llamar a `ReactEcsRenderer.setUiRenderer()` una vez en la escena.
{% endhint %}

## Entidades UI

Cada elemento en la UI debe definirse como una `UiEntity`, ya sea que sea una imagen, texto, fondo, una caja de alineación invisible, etc. Al igual que en el espacio 3D de la escena, cada `UiEntity` tiene sus propios componentes para darle posición, color, etc.

La sintaxis tipo React te permite especificar cada componente como una propiedad dentro del `UiEntity`, esto hace el código más corto y legible.

Los componentes usados en una `UiEntity` son diferentes de los usados en entidades regulares. No puedes aplicar un componente de UI a una entidad regular, ni un componente regular a una entidad de UI.

Los siguientes componentes están disponibles para usar en una `UiEntity`:

* `uiTransform`
* `uiBackground`
* `uiText`
* `onClick`

Al igual que con las etiquetas HTML, puedes definir componentes como autocontenidos o anidar uno dentro de otro.

***archivo ui.tsx:***

```tsx
import ReactEcs, { ReactEcsRenderer, UiEntity } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export const uiMenu = () => (
  // entidad padre
  <UiEntity
    uiTransform={{
      width: 200,
      height: 200,
      margin: { top: '250px', left: '500px' },
    }}
    uiBackground={{ color: Color4.Blue() }}
  >
    // entidad hija autocontenida
    <UiEntity
      uiTransform={{
        width: 400,
        height: 400,
        margin: { top: '35px', left: '500px' },
      }}
      uiText={{ value: `Hello world!`, fontSize: 40 }}
    />
    // cierre de la entidad padre
  </UiEntity>
)
```

***archivo index.ts:***

```ts
import { ReactEcsRenderer } from '@dcl/sdk/react-ecs'
import { uiMenu } from './ui'

export function main() {
    ReactEcsRenderer.setUiRenderer(uiMenu, { virtualWidth: 1920, virtualHeight: 1080 })
}
```

Una definición de un módulo UI solo puede tener una entidad a nivel padre. Puedes definir tantas otras entidades como desees, pero todas deben encajar dentro de una estructura con un único padre en la parte superior.

## Escala virtual de pantalla

Define un ancho y alto virtual para la UI. Esto se recomienda para asegurarte de que tu UI se vea igual en diferentes tamaños de pantalla, independientemente del tamaño real de la pantalla en píxeles.

```ts
export function setupUi() {
    ReactEcsRenderer.setUiRenderer(uiComponent, { virtualWidth: 1920, virtualHeight: 1080 })
}
```

Si estableces un ancho virtual de 1920 y un alto virtual de 1080, la UI se escalará para ajustarse al tamaño de la pantalla. Si la pantalla es 1920x1080, la UI se mostrará al mismo tamaño que el tamaño virtual. Si la pantalla es más grande o más pequeña, cualquier valor en píxeles se escalará para ajustarse al tamaño virtual. Por ejemplo, si la pantalla es 3840x2160, un elemento definido como 100 píxeles de ancho se mostrará sobre 200 píxeles reales.

El cálculo real para el Factor de Escala de la UI que se multiplica sobre los valores en píxeles es [`Math.min(realWidth / virtualWidth, realHeight / virtualHeight)`](https://github.com/decentraland/js-sdk-toolchain/blob/fbf4826ef686982ca1e60d368186e8e10c02a6e6/packages/%40dcl/react-ecs/src/system.ts#L124)

## Múltiples módulos UI

Si tu escena contiene múltiples sistemas o módulos que cada uno define su propia UI, puedes renderizar cada módulo UI con `ReactEcsRenderer.addUiRenderer()`. Esto es especialmente útil cuando trabajas en una escena compleja con múltiples componentes de UI, o cuando defines UIs para un [smart item](https://github.com/decentraland/docs/blob/main/creator/sdk7/smart-items/smart-items.md), que debería ser usable independientemente de lo que haya en el código del resto de la escena.

La `ReactEcsRenderer.addUiRenderer()` función requiere que proporciones una entidad como dueña de la UI. Esto puede ser cualquier entidad, incluso una entidad ficticia creada solo para usarse como dueña de la UI.

```ts
export function setupUi() {

    // Crea una entidad ficticia para ser la propietaria de la UI
    const dummyEntity = engine.addEntity()

    // Define el módulo UI como una función que devuelve un arreglo de módulos UI
    const uiComponent = () => [
      // Función que devuelve un módulo UI,
      // Función que devuelve un módulo UI
      // ...
    ]

    // Renderiza el módulo UI con la entidad ficticia como propietaria
    ReactEcsRenderer.addUiRenderer(dummyEntity, uiComponent)
}
```

Este fragmento puede existir independientemente de cualquier otro código UI en la escena. El resto de la escena podría incluir un `ReactEcsRenderer.setUiRenderer()`, o ninguno en absoluto, y la UI aún se renderizará.

Una `addUiRenderer()` llamada también puede incluir un ancho y alto virtual, igual que `setUiRenderer()`. Sin embargo, si la escena tiene una `setUiRenderer()` llamada que también define un ancho y alto virtual, el ancho y alto virtual de la `addUiRenderer()` llamada serán ignorados.

```tsx
ReactEcsRenderer.addUiRenderer(dummyEntity, uiComponent, { virtualWidth: 1920, virtualHeight: 1080 })
```

Esa UI puede ser removida con `ReactEcsRenderer.removeUiRenderer(dummyEntity)` , también si la entidad que posee la UI es destruida, la UI será eliminada también. Si `ReactEcsRenderer.addUiRenderer()` se llama de nuevo para la misma entidad pero con un UiRenderer diferente, el anterior se limpia y el nuevo lo reemplaza.

### Compartir una única instrucción setUiRenderer

En lugar de llamar a `ReactEcsRenderer.addUiRenderer()` para cada módulo UI, puedes llamar a `ReactEcsRenderer.setUiRenderer()` una vez con un arreglo de módulos UI, que pueden vivir en diferentes archivos.

```ts
const uiComponent = () => [
  // Función que devuelve un módulo UI,
  // Función que devuelve un módulo UI
  // ...
]

ReactEcsRenderer.setUiRenderer(uiComponent, { virtualWidth: 1920, virtualHeight: 1080 })
```

A continuación hay un ejemplo más completo:

***archivo ui.tsx:***

```ts
export function UIModule1() {
  return (
    <UiEntity
      uiTransform={{
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'space-between',
        positionType: 'absolute',
        position: { right: '3%', bottom: '3%' },
      }}
    >
      <Label value="Hello World!" fontSize={18} textAlign="middle-center" />
    </UiEntity>
  )
}

export function UIModule2() {
  return (
    <UiEntity
      uiTransform={{
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'space-between',
        positionType: 'absolute',
        position: { right: '3%', top: '3%' },
      }}
    >
      <Label
        value="Here's some more UI!"
        fontSize={18}
        textAlign="middle-center"
      />
    </UiEntity>
  )
}
```

***archivo index.ts:***

```ts
import { ReactEcsRenderer } from '@dcl/sdk/react-ecs'
import { UIModule1, UIModule2 } from './ui'

export function main() {
    ReactEcsRenderer.setUiRenderer([
      UIModule1(),
      UIModule2(),
      // ...
      // La línea de abajo es para usar la librería DCL UI Toolkit
      // https://github.com/decentraland-scenes/dcl-ui-toolkit
      ui.render(),
    ], { virtualWidth: 1920, virtualHeight: 1080 })
}
```
