# UI no Ecrã

Você pode construir uma UI para sua cena, para ser exibida no espaço 2D fixo da tela, em vez do espaço 3D do mundo.

Os elementos de UI só são visíveis quando o jogador está dentro dos parcelas de LAND da cena, pois cenas vizinhas podem ter sua própria UI para exibir. Partes da UI também podem ser acionadas para abrir quando certos eventos ocorrem no espaço do mundo, por exemplo se o jogador clicar em um lugar específico.

Construa uma UI definindo uma estrutura de `UIEntity` objetos em JSX. A sintaxe usada para UIs é muito semelhante à do [React](https://reactjs.org/) (uma biblioteca baseada em javascript muito popular para construir UIs web).

{% hint style="warning" %}
**📔 Nota**: Você só pode definir sintaxe de UI em arquivos que tenham um `.tsx` extensão. `.tsx` arquivos suportam tudo o que `.ts` arquivos suportam, além da sintaxe de UI. Recomendamos criar um `ui.tsx` arquivo e definir sua UI lá. Lembre-se de chamar seu método de renderização de UI a partir de `index.ts` com `ReactEcsRenderer.setUiRenderer(yourUiMethodName)`, veja o exemplo abaixo.
{% endhint %}

Uma UI simples com elementos estáticos pode parecer muito com HTML, mas quando você adiciona elementos dinâmicos que respondem a uma mudança de estado, você pode fazer coisas muito mais poderosas.

A UI padrão do Explorer do Decentraland inclui um widget de chat, um mapa e outros elementos. Esses elementos de UI são sempre exibidos na camada superior, acima de qualquer UI específica da cena. Portanto, se sua cena tiver elementos de UI que ocupem o mesmo espaço da tela que esses, eles serão ocultados.

Veja [UX guidelines](https://docs.decentraland.org/creator/content-creator-pt/scenes-sdk7/desenhar-a-experiencia/ux-ui-guide) para dicas sobre como projetar o visual e a sensação da sua UI.

Quando o jogador clica no *botão fechar UI* no canto inferior direito da tela, todos os elementos de UI são ocultados.

## Renderizar uma UI

Para exibir uma UI na sua cena, use a `ReactEcsRenderer.setUiRenderer()` função, passando-a uma estrutura válida de entidades, descrita em um `.tsx` arquivo.

Cada entidade é definida como um nó parecido com HTML, com propriedades para cada um de seus componentes.

***arquivo 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() }}
  />
)
```

***arquivo index.ts:***

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

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

Você também pode definir uma estrutura de entidade e renderizá-la, tudo em um mesmo comando em um `.tsx` arquivo.

***arquivo 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 })
}
```

***arquivo index.ts:***

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

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

{% hint style="warning" %}
**📔 Nota**: Todos os seus elementos de UI precisam estar aninhados na mesma estrutura e ter um único pai na raiz da estrutura. Você só pode chamar `ReactEcsRenderer.setUiRenderer()` uma vez na cena.
{% endhint %}

## Entidades de UI

Cada elemento na UI deve ser definido como um `UiEntity`, seja uma imagem, texto, fundo, uma caixa de alinhamento invisível, etc. Assim como no espaço 3D da cena, cada `UiEntity` tem seus próprios componentes para lhe dar posição, cor, etc.

A sintaxe parecida com React permite especificar cada componente como uma propriedade dentro do `UiEntity`, isso torna o código mais curto e mais legível.

Os componentes usados em um `UiEntity` são diferentes daqueles usados em entidades regulares. Você não pode aplicar um componente de UI a uma entidade regular, nem um componente regular a uma entidade de UI.

Os seguintes componentes estão disponíveis para uso em um `UiEntity`:

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

Como com tags HTML, você pode definir componentes como self-closing ou aninhar um dentro do outro.

***arquivo ui.tsx:***

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

export const uiMenu = () => (
  // entidade pai
  <UiEntity
    uiTransform={{
      width: 200,
      height: 200,
      margin: { top: '250px', left: '500px' },
    }}
    uiBackground={{ color: Color4.Blue() }}
  >
    // entidade filha self-closing
    <UiEntity
      uiTransform={{
        width: 400,
        height: 400,
        margin: { top: '35px', left: '500px' },
      }}
      uiText={{ value: `Hello world!`, fontSize: 40 }}
    />
    // declaração de fechamento para a entidade pai
  </UiEntity>
)
```

***arquivo index.ts:***

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

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

Uma definição de um módulo de UI só pode ter uma entidade no nível pai. Você pode definir quantas outras entidades quiser, mas todas devem caber dentro de uma estrutura com um único pai no topo.

## Escala Virtual da Tela

Defina uma largura e altura virtual para a UI. Isso é recomendado para garantir que sua UI pareça a mesma em diferentes tamanhos de tela, independentemente do tamanho real da tela em pixels.

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

Se você definir uma largura virtual de 1920 e uma altura virtual de 1080, a UI será escalada para caber no tamanho da tela. Se a tela for 1920x1080, a UI será exibida no mesmo tamanho que o tamanho virtual. Se a tela for maior ou menor, quaisquer valores em pixels serão escalados para caber no tamanho virtual. Por exemplo, se a tela for 3840x2160, um item definido como 100 pixels de largura será exibido em 200 pixels reais.

O cálculo real para o Fator de Escala da UI que é multiplicado nos valores em pixels é [`Math.min(realWidth / virtualWidth, realHeight / virtualHeight)`](https://github.com/decentraland/js-sdk-toolchain/blob/fbf4826ef686982ca1e60d368186e8e10c02a6e6/packages/%40dcl/react-ecs/src/system.ts#L124)

## Múltiplos módulos de UI

Se sua cena contém múltiplos sistemas ou módulos que definem cada um sua própria UI, você pode renderizar cada módulo de UI com `ReactEcsRenderer.addUiRenderer()`. Isso é especialmente útil ao trabalhar em uma cena complexa com múltiplos componentes de UI, ou ao definir UIs para um [smart item](https://github.com/decentraland/docs/blob/main/creator/sdk7/smart-items/smart-items.md), que deve ser utilizável independentemente do que está no código do restante da cena.

A função `ReactEcsRenderer.addUiRenderer()` exige que você forneça uma entidade como proprietária da UI. Isso pode ser qualquer entidade, até mesmo uma entidade dummy criada apenas para ser usada como proprietária da UI.

```ts
export function setupUi() {

    // Crie uma entidade dummy para ser a proprietária da UI
    const dummyEntity = engine.addEntity()

    // Defina o módulo de UI como uma função que retorna um array de módulos de UI
    const uiComponent = () => [
      // Função retornando um módulo de UI,
      // Função retornando um módulo de UI
      // ...
    ]

    // Renderize o módulo de UI com a entidade dummy como proprietária
    ReactEcsRenderer.addUiRenderer(dummyEntity, uiComponent)
}
```

Esse trecho pode existir independentemente de qualquer outro código de UI na cena. O resto da cena pode incluir um `ReactEcsRenderer.setUiRenderer()`, ou nenhum, e a UI ainda será renderizada.

Uma `addUiRenderer()` chamada também pode incluir uma largura e altura virtuais, assim como `setUiRenderer()`. Entretanto, se a cena tem uma `setUiRenderer()` chamada que também define uma largura e altura virtuais, a largura e altura virtuais da `addUiRenderer()` chamada serão ignoradas.

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

Essa UI pode ser removida com `ReactEcsRenderer.removeUiRenderer(dummyEntity)` , também se a entidade que possui a UI for destruída, a UI será removida também. Se `ReactEcsRenderer.addUiRenderer()` for chamada novamente para a mesma entidade mas com um UiRenderer diferente, o anterior é limpo e o novo o substitui.

### Compartilhando uma única declaração setUiRenderer

Em vez de chamar `ReactEcsRenderer.addUiRenderer()` para cada módulo de UI, você pode chamar `ReactEcsRenderer.setUiRenderer()` uma vez com um array de módulos de UI, que podem estar em arquivos diferentes.

```ts
const uiComponent = () => [
  // Função retornando um módulo de UI,
  // Função retornando um módulo de UI
  // ...
]

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

Abaixo está um exemplo mais completo:

***arquivo 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>
  )
}
```

***arquivo index.ts:***

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

export function main() {
    ReactEcsRenderer.setUiRenderer([
      UIModule1(),
      UIModule2(),
      // ...
      // A linha abaixo é para usar a biblioteca DCL UI Toolkit
      // https://github.com/decentraland-scenes/dcl-ui-toolkit
      ui.render(),
    ], { virtualWidth: 1920, virtualHeight: 1080 })
}
```
