# Mover Entities

Para mover, rodar ou redimensionar uma entidade na sua cena ao longo de um período de tempo, use o `Tween` componente. O engine realiza a transformação desejada de forma suave, atualizando a cada frame até que a duração especificada termine. Também os `Transform` valores do componente da entidade afetada são atualizados em tempo real caso seja necessário fazer verificações de proximidade no código da cena.

{% hint style="info" %}
**💡 Dica**: No [Scene Editor in Creator Hub](https://github.com/decentraland/docs/blob/main/creator/sdk7/scene-editor/get-started/about-editor.md), você pode mover entidades de forma no-code via **Actions**, veja [Make any item smart](https://github.com/decentraland/docs/blob/main/creator/sdk7/scene-editor/interactivity/make-any-item-smart.md).
{% endhint %}

O componente Tween possui as seguintes funções:

* `setMove`: Mover entre dois pontos
* `setRotate`: Rodar entre duas direções
* `setScale`: Escalar entre dois tamanhos
* `setMoveRotateScale`: Transicionar simultaneamente em todos os três parâmetros
* `setMoveContinuous`: Mover constantemente na mesma direção
* `setRotateContinuous`: Rodar constantemente na mesma direção
* `setTextureMove`: Deslocar a textura de um material entre duas posições
* `setTextureMoveContinuous`: Deslocar a textura de um material constantemente na mesma direção

## Mover entre dois pontos

Para mover uma entidade entre dois pontos, crie um `Tween` componente com a `setMove` função.

```ts
const myEntity = engine.addEntity()
Transform.create(myEntity, {
	position: Vector3.create(4, 1, 4),
})
MeshRenderer.setBox(myEntity)

Tween.setMove(myEntity, 
	Vector3.create(1, 1, 1), 
	Vector3.create(8, 1, 8), 
	2000
)
```

A tween de movimento recebe as seguintes informações:

* `entity`: A entidade a mover
* `start`: Um Vector3 para a posição inicial
* `end`: Um Vector3 para a posição final
* `duration`: Quantos milissegundos leva para mover entre as duas posições

Estes outros parâmetros opcionais também estão disponíveis:

* `faceDirection`: Se true, a entidade é rotacionada para olhar na direção do movimento.
* `easingFunction`: Qual função de easing usar. Veja [Non-linear tweens](#non-linear-tweens)

## Rodar entre duas direções

Para rodar uma entidade entre duas posições, crie um `Tween` componente com a `setRotate` função.

```ts
const myEntity = engine.addEntity()
Transform.create(myEntity, {
	position: Vector3.create(4, 1, 4),
})
MeshRenderer.setBox(myEntity)

Tween.setRotate(myEntity, 
	Quaternion.fromEulerDegrees(0, 0, 0), 
	Quaternion.fromEulerDegrees(0, 170, 0), 
	700
)
```

A tween de rotação recebe as seguintes informações:

* `start`: Um Quaternion para a rotação inicial
* `end`: Um Quaternion para a rotação final
* `duration`: Quantos milissegundos leva para mover entre as duas posições

Este outro parâmetro opcional também está disponível:

* `easingFunction`: Qual função de easing usar. Veja [Non-linear tweens](#non-linear-tweens)

### Rodar com um ponto de pivô

Ao rodar uma entidade, a rotação é sempre em referência à coordenada central da entidade. Para rodar uma entidade usando outro conjunto de coordenadas como ponto de pivô, crie uma segunda entidade (invisível) com o ponto de pivô como sua posição e torne-a pai da entidade que você deseja rotacionar.

Ao rodar a entidade pai, seus filhos serão todos rotacionados usando a posição do pai como ponto de pivô. Note que a `position` da entidade filha está em referência à da entidade pai.

```ts
const pivotEntity = engine.addEntity()
Transform.create(pivotEntity, {
	position: Vector3.create(4, 1, 4),
})

const childEntity = engine.addEntity()
Transform.create(childEntity, {
	position: Vector3.create(1, 0, 0),
	parent: pivotEntity,
})
MeshRenderer.setBox(myEntity)

Tween.setRotate(pivotEntity, 
	Quaternion.fromEulerDegrees(0, 0, 0), 
	Quaternion.fromEulerDegrees(0, 170, 0), 
	700
)
```

Note que neste exemplo, o sistema está rotacionando a `pivotEntity` entidade, que é pai da `childEntity` entidade.

## Escalar entre dois tamanhos

Para alterar a escala de uma entidade entre dois tamanhos, crie um `Tween` componente com seu modo definido como `Tween.Mode.Scale`.

```ts
const myEntity = engine.addEntity()
Transform.create(myEntity, {
	position: Vector3.create(4, 1, 4),
})
MeshRenderer.setBox(myEntity)

Tween.setScale(myEntity, 
	Vector3.create(1, 1, 1), 
	Vector3.create(4, 4, 4), 
	2000
)	

```

A tween de escala recebe as seguintes informações:

* `start`: Um Vector3 para o tamanho inicial
* `end`: Um Vector3 para o tamanho final
* `duration`: Quantos milissegundos leva para mover entre as duas posições

Este outro parâmetro opcional também está disponível:

* `easingFunction`: Qual função de easing usar. Veja [Non-linear tweens](#non-linear-tweens)

## Non-linear tweens

Tweens podem seguir diferentes **Easing Functions** que afetam a taxa de mudança ao longo do tempo. Uma **linear** função significa que a velocidade da mudança é constante do início ao fim. Existem muitas opções para escolher, que desenham curvas de formas diferentes dependendo se o começo e/ou o fim iniciam devagar, e o quanto. Uma **easeinexpo** curva começa devagar e termina rápida, aumentando a velocidade exponencialmente, ao contrário de uma **easeoutexpo** curva que começa rápida e termina devagar.

{% hint style="info" %}
**💡 Dica**: Experimente diferentes curvas de movimento. As diferenças são muitas vezes sutis, mas nós subconscientemente interpretamos informações de como as coisas se movem, como peso, atrito ou até personalidade.
{% endhint %}

```ts
Tween.setScale(myEntity, 
	Vector3.create(1, 1, 1), 
	Vector3.create(4, 4, 4), 
	2000,
	EasingFunction.EF_EASEOUTBOUNCE
)

```

O parâmetro opcional `easingFunction` toma seu valor do `EasingFunction` enum, que oferece as seguintes opções:

* `EF_EASEBACK`
* `EF_EASEBOUNCE`
* `EF_EASECIRC`
* `EF_EASECUBIC`
* `EF_EASEELASTIC`
* `EF_EASEEXPO`
* `EF_EASEINBACK`
* `EF_EASEINBOUNCE`
* `EF_EASEINCIRC`
* `EF_EASEINCUBIC`
* `EF_EASEINELASTIC`
* `EF_EASEINEXPO`
* `EF_EASEINQUAD`
* `EF_EASEINQUART`
* `EF_EASEINQUINT`
* `EF_EASEINSINE`
* `EF_EASEOUTBACK`
* `EF_EASEOUTBOUNCE`
* `EF_EASEOUTCIRC`
* `EF_EASEOUTCUBIC`
* `EF_EASEOUTELASTIC`
* `EF_EASEOUTEXPO`
* `EF_EASEOUTQUAD`
* `EF_EASEOUTQUART`
* `EF_EASEOUTQUINT`
* `EF_EASEOUTSINE`
* `EF_EASEQUAD`
* `EF_EASEQUART`
* `EF_EASEQUINT`
* `EF_EASESINE`
* `EF_LINEAR`

## Rotação constante

Para fazer uma entidade girar constantemente, use o `Tween` componente com a `setRotateContinuous` função.

```ts
Tween.setRotateContinuous(myEntity, 
	Quaternion.fromEulerDegrees(0, -1, 0), 
	700
)
```

A tween de rotação contínua recebe as seguintes informações:

* `entity`: A entidade a rotacionar
* `direction`: Um Quaternion para a rotação
* `speed`: Quantos graus por segundo a entidade irá girar

Este outro parâmetro opcional também está disponível:

* `duration`: Quantos milissegundos sustentar a rotação. Após esse tempo, a rotação irá parar.

## Movimento constante

Para fazer uma entidade mover-se constantemente na mesma direção, use o `Tween` componente com a `setMoveContinuous` função.

```ts
Tween.setMoveContinuous(myEntity, 
	Vector3.create(0, 0, 1), 
	0.7
)
```

A tween de movimento contínuo recebe as seguintes informações:

* `entity`: A entidade a mover
* `direction`: Um Vector3 para o movimento
* `speed`: Quantos metros por segundo a entidade irá mover-se

Este outro parâmetro opcional também está disponível:

* `duration`: Quantos milissegundos sustentar o movimento. Após esse tempo, o movimento irá parar.

A tween de movimento contínuo recebe as seguintes informações:

## Sequências de Tween

Para fazer uma entidade executar uma série de tweens em sequência, use o `TweenSequence` componente. Este componente requer dois campos:

* `sequence`: Um array com múltiplas definições de tween, que serão realizadas sequencialmente. O array pode estar vazio, caso em que apenas toca o tween atual.
* `loop` *(opcional)*: Se não fornecido, a sequência é tocada apenas uma vez. Se o campo estiver presente, o valor deve ser um valor do `TweenLoop` enum. Valores aceitos são:
  * `TL_RESTART`: Quando a sequência terminar, ela reinicia. Se o estado final não corresponder ao estado inicial, a entidade salta instantaneamente de um para o outro.
  * `TL_YOYO`: Quando a sequência terminar, ela volta para trás, executando todos os tweens em reverso até alcançar o início novamente. Então começa mais uma vez.

### Mover para frente e para trás

Para fazer uma plataforma mover-se constantemente para frente e para trás entre duas posições, deixe o `sequence` array vazio, e defina `loop` para `TweenLoop.TL_YOYO`

```ts
const myEntity = engine.addEntity()
Transform.create(myEntity, {
	position: Vector3.create(4, 1, 4),
})
MeshRenderer.setBox(myEntity)

Tween.setMove(myEntity, 
	Vector3.create(1, 1, 1), 
	Vector3.create(8, 1, 8), 
	2000
)

TweenSequence.create(myEntity, { sequence: [], loop: TweenLoop.TL_YOYO })
```

A entidade moverá-se para frente e para trás entre o ponto inicial e o ponto final, com a mesma duração e a mesma função de easing em ambas as direções.

### Seguir um caminho

Para fazer uma entidade seguir um caminho mais complexo com múltiplos pontos, forneça uma lista de definições de tween no `sequence` de um `TweenSequence` componente.

```ts
const myEntity = engine.addEntity()
Transform.create(myEntity, {
	position: Vector3.create(4, 1, 4),
})
MeshRenderer.setBox(myEntity)

Tween.setMove(myEntity, 
	Vector3.create(6.5, 7, 4), 
	Vector3.create(6.5, 7, 12), 
	4000
)

TweenSequence.create(myEntity, {
	sequence: [
		{
			duration: 2000,
			easingFunction: EasingFunction.EF_LINEAR,
			mode: Tween.Mode.Move({
				start: Vector3.create(6.5, 7, 12),
				end: Vector3.create(6.5, 10.5, 12),
			}),
		},
		{
			duration: 3000,
			easingFunction: EasingFunction.EF_LINEAR,
			mode: Tween.Mode.Move({
				start: Vector3.create(6.5, 10.5, 12),
				end: Vector3.create(6.5, 10.5, 4),
			}),
		},
		{
			duration: 3000,
			easingFunction: EasingFunction.EF_LINEAR,
			mode: Tween.Mode.Move({
				start: Vector3.create(6.5, 10.5, 4),
				end: Vector3.create(6.5, 7, 4),
			}),
		},
	],
	loop: TweenLoop.TL_RESTART,
})
```

Note que ao definir um tween dentro de um TweenSequence, você precisa usar o formato mais verboso de `Tween.Mode.Move`, ou `Tween.Mode.Rotate`, ou `Tween.Mode.Scale` para definir o tween. Neste formato mais verboso, você precisa especificar:

* `duration`: Quantos milissegundos leva para mover entre as duas posições
* `easingFunction`: Qual função de easing usar. Veja [Non-linear tweens](#non-linear-tweens). Neste formato o valor é obrigatório.
* `mode`: O modo do tween, que pode ser `Tween.Mode.Move`, `Tween.Mode.Rotate`, ou `Tween.Mode.Scale`.

E dentro do campo `mode` você precisa especificar:

* `start`: O valor inicial do tween
* `end`: O valor final do tween

## Ao término do tween

Use `tweenSystem.tweenCompleted` para detectar quando um tween terminou. Isso pode ser útil para executar ações quando um tween terminar, por exemplo para abrir a porta de um elevador.

```ts
engine.addSystem(() => {
	const tweenCompleted = tweenSystem.tweenCompleted(myEntity)
	if (tweenCompleted) {
		//play sound
	}
})
```

## Tweens simultâneos

Para mover, rodar e escalar uma entidade entre um estado inicial e um estado final, crie uma `Tween` componente com a `setMoveRotateScale` função. Esta função também pode ser usada em qualquer combinação desses três parâmetros.

```ts
const myEntity = engine.addEntity()
Transform.create(myEntity, {
	position: Vector3.create(14, 1, 2),
})
MeshRenderer.setBox(myEntity)

Tween.setMoveRotateScale(mrsEntity, {
	position: { start: Vector3.create(14, 1, 2), end: Vector3.create(14, 3, 2) },
	rotation: { start: Quaternion.fromEulerDegrees(0, 0, 0), end: Quaternion.fromEulerDegrees(0, 180, 90) },
	scale: { start: Vector3.One(), end: Vector3.create(2, 0.5, 2) },
	duration: 2000
})
```

A tween de movimento recebe as seguintes informações:

* `entity`: A entidade a mover
* `params`: Um objeto com vários parâmetros opcionais
  * `position`: Um objeto com um `start` e `end` value, ambos como `Vector3`.
  * `rotation`: Um objeto com um `start` e `end` value, ambos como `Quaternion`.
  * `scale`: Um objeto com um `start` e `end` value, ambos como `Vector3`.
  * `duration`: Quantos milissegundos leva para transicionar entre os dois conjuntos de valores
  * `easingFunction`: Qual função de easing usar. Veja [Non-linear tweens](#non-linear-tweens)

Uma entidade só pode ter um `Tween` componente, e cada componente tween só pode executar uma transformação de cada vez. Através do `setMoveRotateScale` tween type, você pode fazer uma entidade mover-se lateralmente e também rotacionar ao mesmo tempo, mas ambos os movimentos seguirão a mesma linha do tempo. Se você precisar que as transições sejam independentes entre si, como solução alternativa, você pode usar entidades com hierarquia de pai/filho. Por exemplo, você pode ter uma entidade pai invisível que se move lateralmente, com um filho visível que rotaciona.

No snippet a seguir, uma entidade pai rotaciona enquanto uma filha aumenta de escala.

```ts
const parentEntity = engine.addEntity()
Transform.create(parentEntity, {
	position: Vector3.create(4, 1, 4),
})
MeshRenderer.setBox(parentEntity)
Tween.setRotate(parentEntity, 
	Quaternion.fromEulerDegrees(0, 0, 0), 
	Quaternion.fromEulerDegrees(0, 170, 0), 
	5000
)

const childEntity = engine.addEntity()
Transform.create(childEntity, {
	position: Vector3.create(0, 0, 0),
	parent: parentEntity,
})
MeshRenderer.setBox(childEntity)
Tween.setScale(childEntity, 
	Vector3.create(1, 1, 1), 
	Vector3.create(4, 4, 4), 
	2000
)
```

## Pausar um tween

Para pausar um tween, altere a propriedade `playing` para false. Para retomá-lo, altere de volta para true.

```ts
pointerEventsSystem.onPointerDown(
	{
		entity: button,
		opts: { button: InputAction.IA_POINTER, hoverText: 'pause' },
	},
	() => {
		let tweenData = Tween.getMutable(myEntity)
		tweenData.playing = !tweenData.playing
	}
)
```

Para encerrar um tween que não precisa continuar, apague o `Tween` componente da entidade. Se a entidade também estava usando um `TweenSequence` componente, apague esse também.

```ts
pointerEventsSystem.onPointerDown(
	{
		entity: button,
		opts: { button: InputAction.IA_POINTER, hoverText: 'pause' },
	},
	() => {
		if( Tween.has(myEntity)){
			Tween.deleteFrom(myEntity)
		}
		if( TweenSequence.has(myEntity)){
			TweenSequence.deleteFrom(myEntity)
		}
	}
)
```

## Tweens baseados em um sistema

Ao invés de usar o componente Tween e deixar o engine tratar da transformação, você pode preferir fazer essa transição incrementalmente, frame a frame, via um [system](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/architecture/systems.md) na sua cena. Movendo a entidade uma pequena quantidade cada vez que a função roda.

Por um lado, isso lhe dá mais controle para recalcular movimentos em cada frame. Por outro lado, o código fica mais complicado, e jogadores com máquinas menos performáticas podem experimentar o tween como travado, notando cada incremento.

### Mover via system

A maneira mais fácil de mover uma entidade é modificar gradualmente o valor *position* armazenado no `Transform` componente.

```ts
função SimpleMove() {
	let transform = Transform.getMutable(myEntity)
	transform.position = Vector3.add(
		transform.position,
		Vector3.scale(Vector3.Forward(), 0.05)
	)
}

engine.addSystem(SimpleMove)

const myEntity = engine.addEntity()
Transform.create(myEntity, {
	position: Vector3.create(4, 1, 4),
})
MeshRenderer.setBox(myEntity)
```

Neste exemplo estamos movendo uma entidade 0.1 metros por tick do loop do jogo.

`Vector3.Forward()` retorna um vetor que aponta para frente e mede 1 metro de comprimento. Neste exemplo estamos então escalando este vetor para 1/10 de seu comprimento com `Vector3.scale()`. Se nossa cena tiver 30 frames por segundo, a entidade está movendo-se a 3 metros por segundo de velocidade.

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

### Rodar via system

A maneira mais fácil de rodar uma entidade é alterar gradualmente os valores no componente Transform incrementalmente, e executar isso como parte da função de um system.

```ts
function SimpleRotate() {
	let transform = Transform.getMutable(myEntity)
	transform.rotation = Quaternion.multiply(
		transform.rotation,
		Quaternion.fromAngleAxis(1, Vector3.Up())
	)
}

engine.addSystem(SimpleRotate)

const myEntity = engine.addEntity()
Transform.create(myEntity, {
	position: Vector3.create(4, 1, 4),
})
MeshRenderer.setBox(myEntity)
```

Note que para combinar a rotação atual com cada incremento, estamos usando `Quaternion.multiply`. Em matemática de quaternions, você combina duas rotações multiplicando-as, NÃO somando-as. A rotação resultante de multiplicar um quaternion por outro será a rotação final equivalente após primeiro executar uma rotação e depois a outra.

Neste exemplo, estamos rotacionando a entidade em 1 grau na direção para cima em cada tick do loop do jogo.

{% hint style="info" %}
**💡 Dica**: Para fazer uma entidade sempre rotacionar para olhar o jogador, você pode adicionar um [`Billboard` componente](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/3d-essentials/entity-positioning.md#face-the-user).
{% endhint %}

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

### Rodar via system sobre um ponto de pivô

Ao rodar uma entidade, a rotação é sempre em referência à coordenada central da entidade. Para rodar uma entidade usando outro conjunto de coordenadas como ponto de pivô, crie uma segunda entidade (invisível) com o ponto de pivô como sua posição e torne-a pai da entidade que você deseja rotacionar.

Ao rodar a entidade pai, seus filhos serão todos rotacionados usando a posição do pai como ponto de pivô. Note que a `position` da entidade filha está em referência à da entidade pai.

```ts
function SimpleRotate() {
	let transform = Transform.getMutable(pivotEntity)
	transform.rotation = Quaternion.multiply(
		transform.rotation,
		Quaternion.fromAngleAxis(1, Vector3.Up())
	)
}

engine.addSystem(SimpleRotate)

const pivotEntity = engine.addEntity()
Transform.create(pivotEntity, {
	position: Vector3.create(4, 1, 4),
})

const childEntity = engine.addEntity()
Transform.create(childEntity, {
	position: Vector3.create(1, 0, 0),
	parent: pivotEntity,
})
MeshRenderer.setBox(myEntity)
```

Note que neste exemplo, o sistema está rotacionando a `pivotEntity` entidade, que é pai da `childEntity` entidade.

![](https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-9d8e4fb2bbc8007ef87c4918cc2b44f3c0cd4a54%2Fpivot-rotate.gif?alt=media)

### Ajustar movimento para tempo de atraso

Suponha que o jogador visitando sua cena esteja lutando para acompanhar o ritmo da taxa de frames. Isso poderia resultar no movimento parecendo irregular, pois nem todos os frames são cronometrados igualmente mas cada um move a entidade pela mesma quantidade.

Você pode compensar esse tempo desigual usando o `dt` parâmetro para ajustar a escala do movimento.

```ts
function SimpleMove(dt: number) {
	let transform = Transform.getMutable(myEntity)
	transform.position = Vector3.add(
		transform.position,
		Vector3.scale(Vector3.Forward(), dt)
	)
}

engine.addSystem(SimpleMove)

const myEntity = engine.addEntity()
Transform.create(myEntity, {
	position: Vector3.create(4, 1, 4),
})
MeshRenderer.setBox(myEntity)
```

O exemplo acima mantém o movimento aproximadamente na mesma velocidade que o exemplo de movimento acima, mesmo se a taxa de frames cair. Ao rodar a 30 frames por segundo, o valor de `dt` é 1/30.

Você também pode suavizar rotações da mesma forma multiplicando a quantidade de rotação por `dt`.

### Mover entre dois pontos via system

Se você quiser que uma entidade se mova suavemente entre dois pontos, use o *lerp* (interpolação linear) algoritmo. Este algoritmo é muito conhecido no desenvolvimento de jogos, pois é realmente útil.

A `lerp()` função recebe três parâmetros:

* O vetor para a posição de origem
* O vetor para a posição alvo
* A quantidade, um valor de 0 a 1 que representa que fração da translação fazer.

```ts
const originVector = Vector3.Zero()
const targetVector = Vector3.Forward()

let newPos = Vector3.lerp(originVector, targetVector, 0.6)
```

O algoritmo de interpolação linear encontra um ponto intermediário no caminho entre ambos os vetores que corresponde à quantidade fornecida.

Por exemplo, se o vetor de origem for *(0, 0, 0)* e o vetor alvo for *(10, 0, 10)*:

* Usando uma quantidade de 0 retornaria *(0, 0, 0)*
* Usando uma quantidade de 0.3 retornaria *(3, 0, 3)*
* Usando uma quantidade de 1 retornaria *(10, 0, 10)*

Para implementar isto `lerp()` na sua cena, recomendamos criar um [componente customizado](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/architecture/custom-components.md) para armazenar a informação necessária. Você também precisa definir um system que implemente o movimento gradual em cada frame.

```ts
// define custom component
const MoveTransportData = {
	start: Schemas.Vector3,
	end: Schemas.Vector3,
	fraction: Schemas.Float,
	speed: Schemas.Float,
}

export const LerpTransformComponent = engine.defineComponent(
	'LerpTransformComponent',
	MoveTransportData
)

// define system
function LerpMove(dt: number) {
	let transform = Transform.getMutable(myEntity)
	let lerp = LerpTransformComponent.getMutable(myEntity)
	if (lerp.fraction < 1) {
		lerp.fraction += dt * lerp.speed
		transform.position = Vector3.lerp(lerp.start, lerp.end, lerp.fraction)
	}
}

engine.addSystem(LerpMove)

// create entity
const myEntity = engine.addEntity()

Transform.create(myEntity, {
	position: Vector3.create(4, 1, 4),
})

MeshRenderer.setBox(myEntity)

LerpTransformComponent.create(myEntity, {
	start: Vector3.create(4, 1, 4),
	end: Vector3.create(8, 1, 8),
	fraction: 0,
	speed: 1,
})
```

![](https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-821f48b2d4a6afbb451813d7df73aa4196228c0f%2Flerp-move.gif?alt=media)

### Rodar entre dois ângulos via system

Para rodar suavemente entre dois ângulos, use o *slerp* (*esférica* interpolação linear) algoritmo. Este algoritmo é muito similar a um *lerp*, mas ele lida com rotações em quaternion.

A `slerp()` função recebe três parâmetros:

* A [quaternion](https://en.wikipedia.org/wiki/Quaternion) ângulo para a rotação de origem
* A [quaternion](https://en.wikipedia.org/wiki/Quaternion) ângulo para a rotação alvo
* A quantidade, um valor de 0 a 1 que representa que fração da translação fazer.

{% hint style="info" %}
**💡 Dica**: Você pode passar valores de rotação em [euler](https://en.wikipedia.org/wiki/Euler_angles) graus (de 0 a 360) usando `Quaternion.fromEulerDegrees()`.
{% endhint %}

```ts
const originRotation = Quaternion.fromEulerDegrees(0, 90, 0)
const targetRotation = Quaternion.fromEulerDegrees(0, 0, 0)

let newRotation = Quaternion.slerp(originRotation, targetRotation, 0.6)
```

Para implementar isto na sua cena, recomendamos armazenar os dados que entram na função `Slerp()` em um [componente customizado](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/architecture/custom-components.md)componente. Você também precisa definir um system que implemente a rotação gradual em cada frame.

```ts
// define custom component
const RotateSlerpData = {
	start: Schemas.Quaternion,
	end: Schemas.Quaternion,
	fraction: Schemas.Float,
	speed: Schemas.Float,
}

export const SlerpData = engine.defineComponent('SlerpData', RotateSlerpData)

// define system
function SlerpRotate(dt: number) {
	let transform = Transform.getMutable(myEntity)
	let slerpData = SlerpData.getMutable(myEntity)
	if (slerpData.fraction < 1) {
		slerpData.fraction += dt * slerpData.speed
		transform.rotation = Quaternion.slerp(
			slerpData.start,
			slerpData.end,
			slerpData.fraction
		)
	}
}

engine.addSystem(SlerpRotate)

// create entity
const myEntity = engine.addEntity()

Transform.create(myEntity, {
	position: Vector3.create(4, 1, 4),
})

MeshRenderer.setBox(myEntity)

SlerpData.create(myEntity, {
	start: Quaternion.fromEulerDegrees(0, 0, 0),
	end: Quaternion.fromEulerDegrees(0, 180, 0),
	fraction: 0,
	speed: 0.3,
})
```

{% hint style="warning" %}
**📔 Nota**: Você poderia representar a rotação com ângulos euler em vez disso como `Vector3` valores e usar uma `Lerp()` função, mas isso implicaria uma conversão de `Vector3` para `Quaternion` a cada frame. Valores de rotação são armazenados internamente como quaternions no `Transform` componente, então é mais eficiente para a cena trabalhar com quaternions.
{% endhint %}

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

Uma abordagem mais simples mas menos eficiente para isto aproveita a `Quaternion.rotateTowards` função, e evita usar quaisquer componentes personalizados.

```ts
function SimpleRotate(dt: number) {
	let transform = Transform.getMutable(myEntity)
	transform.rotation = Quaternion.rotateTowards(
		transform.rotation,
		Quaternion.fromEulerDegrees(90, 0, 0),
		dt * 10
	)
	if (transform.rotation === Quaternion.fromEulerDegrees(90, 0, 0)) {
		console.log('done')
		engine.removeSystem(this)
	}
}

const simpleRotateSystem = engine.addSystem(SimpleRotate)

const myEntity = engine.addEntity()
Transform.create(myEntity, {
	position: Vector3.create(4, 1, 4),
	rotation: Quaternion.fromEulerDegrees(0, 0, 90),
})

MeshRenderer.setBox(myEntity)
```

No exemplo acima `Quaternion.rotateTowards` leva três argumentos: a rotação inicial, a rotação final desejada e o incremento máximo por quadro. Neste caso, como o incremento máximo é de `dt * 10` graus, a rotação será realizada ao longo de um período de alguns 9 segundos.

Note que o sistema também verifica se a rotação está completa e, se estiver, remove o sistema do engine. Caso contrário, o sistema continuaria fazendo cálculos em cada quadro, mesmo depois que a rotação estivesse completa.

### Alterar escala entre dois tamanhos via system

Se você quer que uma entidade mude de tamanho suavemente e sem alterar suas proporções, use o *lerp* (interpolação linear) algoritmo do `Scalar` objeto.

Caso contrário, se você quiser alterar os eixos em proporções diferentes, use `Vector3` para representar a escala de origem e a escala alvo, e então use a *lerp* função do `Vector3`.

A `lerp()` função do `Scalar` objeto recebe três parâmetros:

* Um número para a escala de origem
* Um número para a escala alvo
* A quantidade, um valor de 0 a 1 que representa qual fração da escala aplicar.

```ts
const originScale = 1
const targetScale = 10

let newScale = Scalar.Lerp(originScale, targetScale, 0.6)
```

Para implementar este lerp na sua cena, recomendamos criar um componente personalizado para armazenar a informação necessária. Você também precisa definir um system que implemente o escalonamento gradual em cada quadro.

```ts
// define custom component
const ScaleTransportData = {
	start: Schemas.Number,
	end: Schemas.Number,
	fraction: Schemas.Float,
	speed: Schemas.Float,
}

export const ScaleTransformComponent = engine.defineComponent(
	'ScaleTransformComponent',
	ScaleTransportData
)

// define system
function LerpMove(dt: number) {
	let transform = Transform.getMutable(myEntity)
	let lerp = ScaleTransformComponent.getMutable(myEntity)
	if (lerp.fraction < 1) {
		lerp.fraction += dt * lerp.speed
		const newScale = Scalar.lerp(lerp.start, lerp.end, lerp.fraction)
		transform.scale = Vector3.create(newScale, newScale, newScale)
	}
}

engine.addSystem(LerpMove)

// create entity
const myEntity = engine.addEntity()

Transform.create(myEntity, {
	position: { x: 4, y: 1, z: 4 },
})

MeshRenderer.setBox(myEntity)

ScaleTransformComponent.create(myEntity, {
	start: 1,
	end: 2,
	fraction: 0,
	speed: 1,
})

Vector3.create(1, 1, 1)
```

![](https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-860402e1bd825057ab2b47d06e95ba6061dc5327%2Flerp-scale.gif?alt=media)

### Mover em velocidades irregulares entre dois pontos via system

Ao usar o método lerp, você pode tornar a velocidade de movimento não linear. No exemplo anterior incrementamos a quantidade do lerp por um valor dado a cada quadro, mas também poderíamos usar uma função matemática para aumentar o número exponencialmente ou em outras medidas que deem um ritmo de movimento diferente.

Você também poderia usar uma função que dê resultados recorrentes, como uma função seno, para descrever um movimento que vai e volta.

Frequentemente essas transições não lineares podem trazer muita vida a uma cena. Um movimento que acelera seguindo uma curva ou que desacelera gradualmente pode dizer muito sobre a natureza de um objeto ou personagem. Você pode até aproveitar funções matemáticas que adicionam efeitos de quique.

```ts
// define custom component
const MoveTransportData = {
	start: Schemas.Vector3,
	end: Schemas.Vector3,
	fraction: Schemas.Float,
	speed: Schemas.Float,
}

export const LerpTransformComponent = engine.defineComponent(
	'LerpTransformComponent',
	MoveTransportData
)

// define system
function LerpMove(dt: number) {
	let transform = Transform.getMutable(myEntity)
	let lerp = LerpTransformComponent.getMutable(myEntity)
	if (lerp.fraction < 1) {
		lerp.fraction += dt * lerp.speed
		const interpolatedValue = interpolate(lerp.fraction)
		transform.position = Vector3.lerp(lerp.start, lerp.end, interpolatedValue)
	}
}

// mapear a fração do lerp para uma curva exponencial
function interpolate(t: number) {
	return t * t
}

engine.addSystem(LerpMove)

// create entity
const myEntity = engine.addEntity()

Transform.create(myEntity, {
	position: { x: 4, y: 1, z: 4 },
})

MeshRenderer.setBox(myEntity)

LerpTransformComponent.create(myEntity, {
	start: Vector3.create(4, 1, 4),
	end: Vector3.create(8, 1, 8),
	fraction: 0,
	speed: 1,
})
```

O exemplo acima é igual ao exemplo linear de lerp que mostramos antes, mas o `fraction` campo mapeado para um valor não linear a cada tick. Esse valor não linear é usado para calcular a `lerp` função, resultando em um movimento que segue uma curva exponencial.

Você também pode mapear uma transição em rotação ou em escala da mesma forma que mostrado acima, mapeando uma transição linear para uma curva.

![](https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-91fe24f5e5561834eea21e052ee0036a2987d747%2Flerp-speed-up.gif?alt=media)

### Seguir um caminho via system

Você pode fazer uma entidade percorrer um array de vetores, realizando um movimento lerp entre cada um para seguir um caminho mais complexo.

```ts
// define custom component
const PathTransportData = {
	path: Schemas.Array(Schemas.Vector3),
	start: Schemas.Vector3,
	end: Schemas.Vector3,
	fraction: Schemas.Float,
	speed: Schemas.Float,
	pathTargetIndex: Schemas.Int,
}

export const LerpTransformComponent = engine.defineComponent(
	'LerpTransformComponent',
	PathTransportData
)

// define system
function PathMove(dt: number) {
	let transform = Transform.getMutable(myEntity)
	let lerp = LerpTransformComponent.getMutable(myEntity)
	if (lerp.fraction < 1) {
		lerp.fraction += dt * lerp.speed
		transform.position = Vector3.lerp(lerp.start, lerp.end, lerp.fraction)
	} else {
		lerp.pathTargetIndex += 1
		if (lerp.pathTargetIndex >= lerp.path.length) {
			lerp.pathTargetIndex = 0
		}
		lerp.start = lerp.end
		lerp.end = lerp.path[lerp.pathTargetIndex]
		lerp.fraction = 0
	}
}

engine.addSystem(PathMove)

// create entity
const myEntity = engine.addEntity()

Transform.create(myEntity, {
	position: Vector3.create(1, 1, 1),
})

MeshRenderer.setBox(myEntity)

const point1 = Vector3.create(1, 1, 1)
const point2 = Vector3.create(8, 1, 3)
const point3 = Vector3.create(8, 4, 7)
const point4 = Vector3.create(1, 1, 7)

const myPath = [point1, point2, point3, point4]

LerpTransformComponent.create(myEntity, {
	path: myPath,
	start: Vector3.create(4, 1, 4),
	end: Vector3.create(8, 1, 8),
	fraction: 0,
	speed: 1,
	pathTargetIndex: 1,
})
```

O exemplo acima define um caminho 3D composto por quatro vetores 3D. O `PathTransportData` componente personalizado contém os mesmos dados usados pelo componente personalizado no *lerp* exemplo acima, mas adiciona um `path` array, com todos os pontos do nosso caminho, e um `pathTargetIndex` campo para acompanhar qual segmento do caminho está sendo usado atualmente.

O sistema é muito similar ao sistema no *lerp* exemplo, mas quando uma ação de lerp é concluída, ele define os `target` e `origin` campos para novos valores. Se alcançarmos o final do caminho, retornamos ao primeiro valor do caminho.

![](https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-5ca6c0f3c12c8838c80b848e718d463c4e5fa441%2Flerp-path.gif?alt=media)

## Texture tweens

Para fazer uma textura deslizar suavemente, use o `Tween` componente com a `setTextureMove` função.

```ts
Tween.setTextureMove(myEntity, 
	Vector2.create(0, 0), 
	Vector2.create(1, 0), 
	2000
)
```

O texture tween recebe as seguintes informações:

* `entity`: A entidade cuja textura será movida
* `start`: Um Vector2 para a posição inicial
* `end`: Um Vector2 para a posição final
* `duration`: Quantos milissegundos leva para mover entre as duas posições

Este outro parâmetro opcional também está disponível:

* `movementType`: (opcional), define se o movimento será no campo offset ou no campo tiling. Por padrão usa offset.
* `easingFunction`: Qual função de easing usar. Veja [Non-linear tweens](#non-linear-tweens). Nota: Este parâmetro é usado apenas se uma duração for fornecida.

## Movimento constante de textura

Para fazer uma textura deslizar constantemente, use o `Tween` componente com a `setTextureMoveContinuous` função.

```ts
Tween.setTextureMoveContinuous(myEntity, 
	Vector2.create(0, 1), 
	0.7
)
```

O texture continuous tween recebe as seguintes informações:

* `entity`: A entidade cuja textura será movida
* `direction`: Um Vector2 para o movimento
* `speed`: Quantas unidades por segundo a entidade irá mover

Este outro parâmetro opcional também está disponível:

* `movementType`: define se o movimento será no campo offset ou no campo tiling. Por padrão usa offset.
* `duration`: Quantos milissegundos sustentar o movimento. Após esse tempo, o movimento irá parar.

Leia mais sobre texture tweens em [Texture Tweens](https://github.com/decentraland/docs/blob/main/creator/sdk7/sdk7/3d-essentials/materials.md#texture-tweens) seção.
