# Colliders

Entidades que têm colliders ocupam espaço e bloqueiam o caminho de um jogador; entidades sem colliders podem ser atravessadas pelo avatar de um jogador.

Colliders também são necessários para tornar uma entidade clicável. Os eventos de botão são baseados na forma do collider de uma entidade, e não na sua forma visível.

Existem layers de colisão separadas para interagir com a física do jogador ou com pointer events; os colliders podem ser configurados para interagir apenas com um ou com outro. Eles também podem ser configurados para interagir com custom layers, que podem ser usadas com [raycasts](https://docs.decentraland.org/creator/content-creator-pt/scenes-sdk7/essenciais-de-conteudo-3d/colliders) para lidar com o que fizer mais sentido para a scene.

{% hint style="warning" %}
**📔 Nota**: Colliders não afetam como outras entidades interagem entre si; as entidades podem sempre se sobrepor. As configurações de colisão afetam apenas como a entidade interage com o avatar do jogador e com eventos de botão. Decentraland não tem um motor de física nativo, então, se você quiser que entidades caiam, batam ou ricocheteiem, você deve programar esse comportamento na scene ou importar uma library para lidar com isso.
{% endhint %}

## Use o Scene Editor

A maneira mais fácil de gerenciar os colliders de uma entidade é usar o [Scene Editor](https://github.com/decentraland/docs/blob/main/creator/sdk7/scene-editor/get-started/about-editor.md).

Você pode adicionar um **Mesh Collider** component à sua entidade para atribuir uma primitive shape (cubo, plano, esfera, cilindro ou cone) à sua entidade. Em seguida, você pode escolher [Collision layers](#collision-layers) em um dropdown.

Você também pode configurar as collision layers em um **GLTF** component para alterar o [Collision layers](#collision-layers) padrão usado na geometria do collider ou na geometria visível do model. Veja [Add Components](https://github.com/decentraland/docs/blob/main/creator/sdk7/scene-editor/build/components.md#add-components).

![](https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-a1566c33728b1ef858f518365390d2ce5c97fe82%2Fgltf-component.png?alt=media)

## Colliders em primitive shapes

O `MeshCollider` component dá a uma entidade um collider simples com base em uma primitive shape (caixas, esferas, planos, cilindros ou cones).

Entidades que têm um `MeshRenderer` component para lhes dar uma [primitive shape](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/3d-essentials/shape-components.md#primitive-shapes) não têm colliders por padrão. Você também deve dar à entidade um `MeshCollider` component.

As seguintes formas de collider estão disponíveis em `MeshCollider`. Várias formas incluem campos adicionais opcionais, específicos dessa forma.

* **box**:

  Use `MeshCollider.setBox()`, passando a entidade.
* **plane**:

  Use `MeshCollider.setPlane()`, passando a entidade.
* **sphere**:

  Use `MeshCollider.setSphere()`, passando a entidade.
* **cylinder**:

  Use `MeshCollider.setCylinder()`, passando a entidade. Passe `radiusTop` e `radiusBottom` como campos adicionais opcionais, para modificar o cilindro.

{% hint style="info" %}
**💡 Dica**: Defina `radiusTop` ou `radiusBottom` como 0 para fazer um cone.
{% endhint %}

Este exemplo define uma entidade em forma de caixa que não pode ser atravessada.

```ts
// criar entidade
const myCollider = engine.addEntity()

// forma visível
MeshRenderer.setBox(myCollider)

// collider
MeshCollider.setBox(myCollider)
```

A forma usada pelo `MeshCollider` não precisa necessariamente corresponder à usada pelo `MeshRenderer`. Você também pode adicionar um `MeshCollider` a uma entidade que tem um model 3D de um `GLTFContainer` component, ou a uma entidade que não tem nenhuma forma visível.

{% hint style="warning" %}
**📔 Nota**: O `MeshCollider` component e `ColliderLayer` devem ser importados via

`import { MeshCollider, ColliderLayer } from "@dcl/sdk/ecs"`

Veja [Imports](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/getting-started/coding-scenes.md#imports) para saber como lidar facilmente com isso.
{% endhint %}

## Colliders em models 3D

Models 3D podem receber colliders em dois níveis diferentes de geometria:

* `visibleMeshesCollisionMask`: Refere-se à geometria visível do model. Por padrão, essa geometria não tem colliders.
* `invisibleMeshesCollisionMask`: refere-se às collider meshes, cujo nome termina em `_collider`. Por padrão, essa geometria é tratada como um collider tanto para física quanto para pointer events.

Qualquer mesh incorporada como parte de um model 3D cujo nome termina em `_collider` é tratada como parte da `invisibleMeshesCollisionMask` layer e interpretada como um collider por padrão.

Definir a geometria do collider como uma layer invisível separada permite muito mais controle e é muito menos exigente para o sistema do que usar a geometria visível, já que o objeto de colisão costuma ser muito mais simples (com menos vertices) do que o model original.

Se um model não tiver nenhuma geometria de collider e você quiser que ele afete a physics ou os pointer events systems, você pode:

* Atribuir collision layers diretamente à geometria visível, via o `visibleMeshesCollisionMask`.

{% hint style="warning" %}
**📔 Nota**: Se a geometria visível do objeto tiver muitos vertices, observe que isso pode ter um custo de performance maior.
{% endhint %}

* Dar à entidade um `MeshCollider` component, para lhe dar um collider com primitive shape.
* Sobrepor uma entidade invisível que tenha um `MeshCollider` component.
* Editar o model em uma tool externa como o Blender para incluir um *collider mesh*. O collider deve ser nomeado *x\_collider*, onde *x* é o nome do model. Então, para um model chamado *house*, o collider deve ser nomeado *house\_collider*.

Talvez você também queira atribuir a collision layer de pointer events ao `visibleMeshesCollisionMask` caso queira que as dicas de hover e os pointer events respondam de forma mais precisa ao contorno da entidade. Note que isso é mais exigente em termos de performance.

{% hint style="warning" %}
**📔 Nota**: Certifique-se de não ter a mesma layer (physics, pointer events ou custom layers) atribuída a ambos `visibleMeshesCollisionMask` e `invisibleMeshesCollisionMask`, pois isso seria um uso muito ineficiente de recursos. Você pode ter layers diferentes em cada um, como physics na layer invisível e pointer events na layer visível.
{% endhint %}

```ts
// criar entidade
const myEntity = engine.addEntity()

// atribuir forma GLTF
GltfContainer.create(myEntity, {
	src: '/models/myModel.gltf',
	invisibleMeshesCollisionMask: ColliderLayer.CL_PHYSICS,
	visibleMeshesCollisionMask: ColliderLayer.CL_POINTER,
})
```

Veja [models 3D](https://github.com/decentraland/docs-creator/blob/main/creator/3d-modeling/3d-models/README.md) para mais detalhes sobre como adicionar geometria invisível de collider a um model 3D.

{% hint style="warning" %}
**📔 Nota**: O `GltfContainer` component e `ColliderLayer` devem ser importados via

> `import { GltfContainer, ColliderLayer } from "@dcl/sdk/ecs"`

Veja [Imports](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/getting-started/coding-scenes.md#imports) para saber como lidar facilmente com isso.
{% endhint %}

### Models animados

Ao configurar colliders para usar a geometria visível em um model que inclui [armature-based animations](https://github.com/decentraland/docs/blob/main/creator/sdk7/3d-modeling/animations.md), as animações não são acompanhadas pelos colliders. As collider meshes mantêm sua forma original. Se uma animação envolve deformar a geometria de uma mesh, as collider meshes retêm a forma não animada enquanto a animação é reproduzida.

Ao reproduzir animações que envolvem mover meshes inteiras sem alterar sua forma, essas mudanças são refletidas com precisão pelos colliders. Por exemplo, se uma plataforma se move como parte de uma animação, o collider da plataforma também se move com a animação.

## Collision layers

A scene pode lidar com layers de colisão separadas, que têm comportamentos diferentes.

Você pode configurar um `MeshCollider` component ou o `GltfContainer` component para responder apenas a um tipo de interação, ou a vários, ou a nenhum. Para fazer isso, no `MeshCollider` defina a propriedade `collisionMask` e, em `GltfContainer` defina a propriedade `visibleMeshesCollisionMask` ou `invisibleMeshesCollisionMask` propriedades para um ou vários dos seguintes valores:

* `ColliderLayer.CL_PHYSICS`: Bloqueia apenas o movimento do jogador (e não afeta pointer events)
* `ColliderLayer.CL_POINTER`: Responde apenas a pointer events (e não bloqueia o movimento do jogador)
* `ColliderLayer.CL_CUSTOM1` até `CL_CUSTOM8`: Pode ser usado junto com raycasts, de modo que um ray detecte colisões apenas com uma layer específica.
* `ColliderLayer.CL_NONE`: Não responde a colisões de nenhum tipo.

{% hint style="warning" %}
**📔 Nota**: Para desabilitar colisões de um `MeshCollider` component, exclua o component. Não defina a collision layer como `ColliderLayer.CL_NONE`. Há um problema conhecido com o `MeshCollider` component. Em vez de desabilitar todas as colisões, isso torna esse valor equivalente ao padrão (`ColliderLayer.CL_PHYSICS | ColliderLayer.CL_POINTER`).
{% endhint %}

```ts
// criar entidade
const myEntity = engine.addEntity()
// forma visível
MeshRenderer.setBox(myEntity)

// criar um component MeshCollider que responde apenas à física do jogador
MeshCollider.setBox(myEntity, ColliderLayer.CL_PHYSICS)
```

Uma única collision mask pode responder a várias collision layers. Use o caractere `|` como um *ou*para incluir quantas layers você precisar. O valor padrão em um MeshCollider é `ColliderLayer.CL_PHYSICS | ColliderLayer.CL_POINTER`.

```ts
MeshCollider.setBox(
	myEntity,
	ColliderLayer.CL_CUSTOM1 |
		ColliderLayer.CL_CUSTOM3 |
		ColliderLayer.CL_PHYSICS |
		ColliderLayer.CL_POINTER
)
```

Você pode usar as 8 different custom layers para o que melhor se adequar à sua scene; por exemplo, uma pode ser usada para cálculos de linha de visão de NPCs, enquanto outra para estimar trajetórias de objetos em queda. Usar layers diferentes para sistemas diferentes permite usar menos recursos, já que, em cada caso, você verificará apenas colisões com as entidades relevantes.

Veja [Raycasting](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/interactivity/raycasting.md) para saber mais sobre como usar custom collision layers.

### Cameras e colliders

Quando a câmera de um jogador se move em modo de terceira pessoa, ela pode ser bloqueada por colliders ou não, dependendo das collision layers atribuídas às entidades. Tenha isso em mente ao projetar sua scene; talvez você queira impedir que a câmera atravesse walls ou outras entidades.

Para evitar que a câmera atravesse walls, você deve atribuir tanto as `ColliderLayer.CL_PHYSICS` quanto as `ColliderLayer.CL_POINTER` layers às entidades que você quer que bloqueiem a câmera. É importante que ambas as layers sejam atribuídas à mesma geometria na entidade. Então, se você atribuir a layer `ColliderLayer.CL_PHYSICS` à layer visível da entidade, você também deve atribuir a layer `ColliderLayer.CL_POINTER` à mesma geometria.

Por exemplo, no Creator Hub, a seguinte combinação de configurações impedirá que a câmera atravesse walls:

![](https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-426b56fda6516e2c2e63296a534d02aa089a566e%2Fcolliders-camera.png?alt=media)

As layers `ColliderLayer.CL_PHYSICS` quanto as `ColliderLayer.CL_POINTER` são atribuídas à mesma layer invisível da geometria da entidade. Se ambas fossem atribuídas à layer visível, o resultado seria o mesmo. Esse é o comportamento padrão, tanto ao adicionar uma entidade via Creator Hub quanto via código.

![](https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-6c77ca6fce6934f939ce70f2146d972718a25556%2Fcolliders-no-camera.png?alt=media)

Neste segundo exemplo, a câmera pode atravessar a wall, porque a layer `ColliderLayer.CL_PHYSICS` está atribuída à layer invisível da entidade, e a layer `ColliderLayer.CL_POINTER` está atribuída à layer visível da entidade, mesmo que ambas as geometrias tenham a mesma forma geral.

```ts
// A CÂMERA NÃO ATRAVESSA A WALL
// padrão (tanto pointer quanto physics usam a geometria invisível)
GLTFContainer.create(myEntity, {
	src: '/models/myModel.gltf',
})

// A CÂMERA NÃO ATRAVESSA A WALL
// Ambas usam a mesma geometria invisível
GltfContainer.create(myEntity2, {
	src: '/models/myModel.gltf',
	invisibleMeshesCollisionMask:
		ColliderLayer.CL_PHYSICS | ColliderLayer.CL_POINTER,
})

// A CÂMERA NÃO ATRAVESSA A WALL
// Ambas usam a mesma geometria visível
GltfContainer.create(myEntity2, {
	src: '/models/myModel.gltf',
	visibleMeshesCollisionMask:
		ColliderLayer.CL_PHYSICS | ColliderLayer.CL_POINTER,
})

// SIM, A CÂMERA ATRAVESSA A WALL
// physics e pointer estão em layers diferentes
GltfContainer.create(myEntity2, {
	src: '/models/myModel.gltf',
	invisibleMeshesCollisionMask: ColliderLayer.CL_PHYSICS,
	visibleMeshesCollisionMask: ColliderLayer.CL_POINTER,
})

// SIM, A CÂMERA ATRAVESSA A WALL
// physics e pointer estão em layers diferentes
GltfContainer.create(myEntity2, {
	src: '/models/myModel.gltf',
	invisibleMeshesCollisionMask: ColliderLayer.CL_POINTER,
	visibleMeshesCollisionMask: ColliderLayer.CL_PHYSICS,
})
```

### Bloqueio de pointer

Somente formas que têm colliders podem ser ativadas com [pointer events](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/interactivity/button-events/click-events.md). Uma entidade também precisa ter um collider para bloquear que pointer events a atravessem e evitar atingir entidades atrás dela. Então, por exemplo, um jogador não pode pegar algo que está trancado dentro de um baú se o baú tiver colliders ao redor. Os pointer events do jogador são afetados apenas por meshes que estão ativas na `ColliderLayer.CL_POINTER` layer.

Por padrão, um MeshCollider afeta tanto as layers Physics quanto Pointer, mas você pode alterar esse valor para afetar apenas uma, ou nenhuma, e afetar custom layers no lugar.

{% hint style="warning" %}
**📔 Nota**: Além de colliders, uma entidade também precisa ter um `PointerEvents` component para responder a pointer events. Os helpers do `pointerEventsSystem` também cuidam desse requisito.
{% endhint %}

```ts
// responde apenas à física do jogador
// por exemplo, para uma parede invisível que você não pode atravessar, mas pela qual você pode clicar
MeshCollider.setBox(myEntity, ColliderLayer.CL_PHYSICS)

// responde apenas ao pointer do jogador
// por exemplo, para um item que você pode clicar para pegar, mas atravessar sem parar
MeshCollider.setBox(myEntity2, ColliderLayer.CL_POINTER)
```

Por padrão, a geometria visível de um `GLTFContainer` não é mapeada para nenhuma collision layer, mas a geometria invisível afeta tanto as layers Physics quanto Pointer. Você pode alterar esse valor para afetar apenas uma, ou nenhuma, e afetar custom layers no lugar. Você também pode configurar a layer da geometria visível da mesma forma.

```ts
// padrão (tanto pointer quanto physics usam a geometria invisível)
GLTFContainer.create(myEntity, {
	src: '/models/myModel.gltf',
})

// a física do jogador usa a geometria invisível mais simples
// pointer events usam o contorno detalhado completo da geometria visível
GltfContainer.create(myEntity2, {
	src: '/models/myModel.gltf',
	invisibleMeshesCollisionMask: ColliderLayer.CL_PHYSICS,
	visibleMeshesCollisionMask: ColliderLayer.CL_POINTER,
})

// tanto a física do jogador quanto os pointer events usam o contorno detalhado completo da geometria visível
// a geometria invisível mais simples é mapeada para ColliderLayer.CL_NONE para evitar calcular ambos
GltfContainer.create(myEntity, {
	src: '/models/myModel.gltf',
	invisibleMeshesCollisionMask: ColliderLayer.CL_NONE,
	visibleMeshesCollisionMask:
		ColliderLayer.CL_POINTER | ColliderLayer.CL_PHYSICS,
})

// não responde a colisões de nenhum tipo, nem com a geometria visível nem com a invisível:
GltfContainer.create(myEntity, {
	src: '/models/myModel.gltf',
	invisibleMeshesCollisionMask: ColliderLayer.CL_NONE,
})
```

## Sintaxe avançada de MeshCollider

A sintaxe completa para criar um `MeshCollider` component, sem nenhum helper para simplificá-la, é assim:

```ts
MeshCollider.create(myBox, {
	mesh: {
		$case: 'box',
		box: {},
	},
})

MeshCollider.create(myPlane, {
	mesh: {
		$case: 'plane',
		plane: {},
	},
})

MeshCollider.create(myShpere, {
	mesh: {
		$case: 'sphere',
		sphere: {},
	},
})

MeshCollider.create(myCylinder, {
	mesh: {
		$case: 'cylinder',
		cylinder: {},
	},
})
```

É assim que o protocolo base interpreta os components MeshCollider. As funções helper abstraem isso e expõem uma sintaxe mais amigável, mas, por trás dos panos, elas geram essa sintaxe.

O `$case` field permite especificar um dos tipos permitidos. Cada tipo oferece um conjunto diferente de parâmetros.

Os valores suportados para `$case` são os seguintes:

* `box`
* `plane`
* `sphere`
* `cylinder`

Dependendo do valor de `$case`, é válido definir o objeto para a forma correspondente, passando quaisquer propriedades relevantes.
