# Mover Entities

Para mover, girar ou redimensionar uma Entity na tua scene ao longo de um período de tempo, usa o `Tween` component. O engine executa a transformação desejada suavemente, mostrando atualizações em cada frame até que a duração especificada termine. Além disso, os valores do `Transform` component da entity afetada são atualizados em tempo real, caso seja necessário fazer verificações de proximidade no código da scene.

{% hint style="info" %}
**💡 Dica**: No [Scene Editor no Creator Hub](/creator/content-creator-pt/scene-editor/comecar/about-editor.md), podes mover entities de uma forma no-code através de **Actions**, vê [Torne qualquer item smart](/creator/content-creator-pt/scene-editor/interatividade/make-any-item-smart.md).
{% endhint %}

O componente Tween tem as seguintes funções:

* `setMove`: Mover entre dois pontos
* `setRotate`: Girar entre duas direções
* `setScale`: Escalar entre dois tamanhos
* `setMoveRotateScale`: Transição simultânea nos três parâmetros
* `setMoveContinuous`: Mover constantemente na mesma direção
* `setRotateContinuous`: Girar constantemente na mesma direção
* `setTextureMove`: Deslocar a texture de um material entre duas posições
* `setTextureMoveContinuous`: Deslocar a texture de um material constantemente na mesma direção

## Mover entre dois pontos

Para mover uma entity entre dois pontos, cria um `Tween` component com o `setMove` function.

```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
)
```

O tween de movimento recebe a seguinte informação:

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

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

* `faceDirection`: Se for true, a entity é rodada para ficar virada na direção do movimento.
* `easingFunction`: Que função de easing usar. Vê [Tweens não lineares](#non-linear-tweens)

## Girar entre duas direções

Para girar uma entity entre dois pontos, cria um `Tween` component com o `setRotate` function.

```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
)
```

O tween de rotação recebe a seguinte informação:

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

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

* `easingFunction`: Que função de easing usar. Vê [Tweens não lineares](#non-linear-tweens)

### Girar com um ponto pivot

Ao girar uma entity, a rotação é sempre feita em referência à coordenada central da entity. Para girar uma entity usando outro conjunto de coordenadas como ponto pivot, cria uma segunda entity (invisível) com o ponto pivot como posição e torna-a parent da entity que queres girar.

Ao girar a entity parent, as suas children serão todas giradas usando a posição do parent como ponto pivot. Nota que o `posição` da entity child está em referência à da entity parent.

```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
)
```

Nota que, neste exemplo, o sistema está a girar a `pivotEntity` entity, que é parent da `childEntity` entity.

## Escalar entre dois tamanhos

Para alterar a escala de uma entity entre dois tamanhos, cria um `Tween` component com o 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
)	

```

O tween de escala recebe a seguinte informação:

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

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

* `easingFunction`: Que função de easing usar. Vê [Tweens não lineares](#non-linear-tweens)

## Tweens não lineares

Tweens podem seguir diferentes **Easing Functions** que afetam a taxa de alteração ao longo do tempo. Uma curva **linear** function, significa que a velocidade da alteração é constante do início ao fim. Há muitas opções por escolher, que desenham curvas com formas diferentes consoante o início e/ou o fim começam devagar, e quão devagar. Um **easeinexpo** começa devagar e termina rápido, aumentando a velocidade exponencialmente; pelo contrário, uma curva **easeoutexpo** começa rápido e termina devagar.

{% hint style="info" %}
**💡 Dica**: Experimente diferentes curvas de movimento. As diferenças são muitas vezes subtis, mas interpretamos subconscientemente informação a partir de como as coisas se movem, como peso, fricção 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` recebe o 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 entity girar constantemente, usa o `Tween` component com o `setRotateContinuous` function.

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

O tween de rotação contínua recebe a seguinte informação:

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

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

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

## Movimento constante

Para fazer uma entity mover-se constantemente na mesma direção, usa o `Tween` component com o `setMoveContinuous` function.

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

O tween de movimento contínuo recebe a seguinte informação:

* `entity`: A entity a mover
* `direction`: Um Vector3 para o movimento
* `speed`: Quantos metros por segundo a entity se vai mover

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

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

O tween de movimento contínuo recebe a seguinte informação:

## Sequências de Tween

Para fazer uma entity reproduzir uma série de tweens em sequência, usa o `TweenSequence` component. Este component requer dois campos:

* `sequence`: Uma array com várias definições de tween, que serão executadas sequencialmente. A array pode estar vazia; nesse caso, apenas reproduz o tween atual.
* `loop` *(opcional)*: Se não for fornecido, a sequência é reproduzida apenas uma vez. Se o campo estiver presente, o valor tem de ser um valor do `TweenLoop` enum. Os valores aceites são:
  * `TL_RESTART`: Quando a sequência termina, recomeça. Se o último estado não corresponder ao primeiro estado, a entity salta instantaneamente de um para o outro.
  * `TL_YOYO`: Quando a sequência termina, ela recua, fazendo todos os tweens no sentido inverso até voltar a atingir o início. Depois, começa novamente.

### Mover para trás e para a frente

Para fazer uma plataforma mover-se constantemente para trás e para a frente entre duas posições, deixa a `sequence` array vazia, e define `loop` a `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 entity vai mover-se para trás e para a frente 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 entity seguir um caminho mais complexo com vários pontos, fornece 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,
})
```

Nota que, ao definir um tween dentro de um TweenSequence, tens de usar o formato mais detalhado de `Tween.Mode.Move`, ou `Tween.Mode.Rotate`, ou `Tween.Mode.Scale` para definir o tween. Neste formato mais detalhado, tens de especificar:

* `duration`: Quantos milissegundos demora a mover entre as duas posições
* `easingFunction`: Que função de easing usar. Vê [Tweens não lineares](#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` precisas de especificar:

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

## Quando o tween termina

Use `tweenSystem.tweenCompleted` para detetar quando um tween terminou. Isto pode ser útil para realizar ações quando um tween acaba, por exemplo, abrir a porta de um elevador.

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

## Tweens simultâneos

Para mover, girar e escalar uma entity entre um estado inicial e um estado final, cria uma `Tween` component com o `setMoveRotateScale` function. Esta function também pode ser usada em qualquer combinação destes 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
})
```

O tween de movimento recebe a seguinte informação:

* `entity`: A entity a mover
* `params`: Um objeto com vários parâmetros opcionais
  * `posição`: Um objeto com um `start` e `end` value, ambos como `Vector3`.
  * `rotação`: Um objeto com um `start` e `end` value, ambos como `Quaternion`.
  * `escala`: Um objeto com um `start` e `end` value, ambos como `Vector3`.
  * `duration`: Quantos milissegundos demora a transição entre os dois conjuntos de valores
  * `easingFunction`: Que função de easing usar. Vê [Tweens não lineares](#non-linear-tweens)

Uma entity só pode ter um `Tween` component, e cada tween component só pode realizar uma transformação de cada vez. Através do `setMoveRotateScale` tipo tween, podes fazer uma entity mover-se lateralmente e também girar ao mesmo tempo, mas ambos estes movimentos seguirão a mesma linha temporal. Se precisares de transições independentes entre si, como alternativa podes usar entities com parent. Por exemplo, podes ter uma entity parent invisível que se move lateralmente, com um child visível que gira.

No seguinte excerto, uma entity parent gira enquanto uma child 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, altera a `playing` propriedade para false. Para o retomar, volta a alterá-la para true.

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

Para terminar um tween que não precise de continuar, apaga o `Tween` component da entity. Se a entity também estiver a usar um `TweenSequence` component, apaga-o 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 num system

Em vez de usares o componente Tween e deixares o engine tratar da transformação, podes preferir fazer esta transição de forma incremental, frame a frame, através de um [system](/creator/content-creator-pt/scenes-sdk7/arquitetura/systems.md) na tua scene. Ao mover a entity um pequeno valor de cada vez que a function é executada.

Por um lado, isto dá-te mais controlo para recalcular movimentos em cada frame. Por outro lado, o código é mais complicado, e jogadores com máquinas menos performantes podem sentir o tween com atrasos, notando cada incremento.

### Mover via system

A forma mais fácil de mover uma entity é modificar gradualmente o *posição* valor guardado no `Transform` componente.

```ts
function 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 a mover uma entity 0.1 metros por tick do game loop.

`Vector3.Forward()` devolve um vector que aponta para a frente e mede 1 metro de comprimento. Neste exemplo, estamos então a reduzir este vector para 1/10 do seu comprimento com `Vector3.scale()`. Se a nossa scene tiver 30 frames por segundo, a entity está a mover-se a uma velocidade de 3 metros por segundo.

![](/files/6feeac5e3b1766f4115689babbf39820ea12496b)

### Girar via system

A forma mais fácil de girar uma entity é alterar gradualmente os valores no component Transform de forma incremental e executar isto como parte da function 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)
```

Nota que, para combinar a rotação atual com cada incremento, estamos a usar `Quaternion.multiply`. Na matemática de quaternions, combinas duas rotações multiplicando-as, e NÃO adicionando-as. A rotação resultante de multiplicar um quaternion por outro será a rotação final equivalente após realizar primeiro uma rotação e depois a outra.

Neste exemplo, estamos a girar a entity em 1 grau na direção ascendente em cada tick do game loop.

{% hint style="info" %}
**💡 Dica**: Para fazer uma entity girar sempre de forma a ficar virada para o jogador, podes adicionar um [`Billboard` component](/creator/content-creator-pt/scenes-sdk7/essenciais-de-conteudo-3d/entity-positioning.md#face-the-user).
{% endhint %}

![](/files/da5ce4208abf08f50cfc853c4008f962bb1441f9)

### Girar via system sobre um ponto pivot

Ao girar uma entity, a rotação é sempre feita em referência à coordenada central da entity. Para girar uma entity usando outro conjunto de coordenadas como ponto pivot, cria uma segunda entity (invisível) com o ponto pivot como posição e torna-a parent da entity que queres girar.

Ao girar a entity parent, as suas children serão todas giradas usando a posição do parent como ponto pivot. Nota que o `posição` da entity child está em referência à da entity parent.

```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)
```

Nota que, neste exemplo, o sistema está a girar a `pivotEntity` entity, que é parent da `childEntity` entity.

![](/files/b44b19be919cccd3a26c05dcff17e430b82dd8ff)

### Ajustar movimento ao tempo de atraso

Imagina que o jogador que visita a tua scene está com dificuldade em acompanhar o ritmo da frame rate. Isso pode fazer com que o movimento pareça aos solavancos, já que nem todos os frames têm a mesma duração, mas cada um move a entity a mesma quantidade.

Podes compensar este tempo irregular usando o parâmetro `dt` 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 à mesma velocidade do exemplo de movimento acima, mesmo que a frame rate diminua. Quando está a correr a 30 frames por segundo, o valor de `dt` é 1/30.

Também podes suavizar rotações da mesma forma multiplicando a quantidade de rotação por `dt`.

### Mover entre dois pontos via system

Se quiseres que uma entity se mova suavemente entre dois pontos, usa o *lerp* (linear interpolation) algorithm. Este algoritmo é muito conhecido no desenvolvimento de jogos, pois é realmente útil.

O `lerp()` function recebe três parâmetros:

* O vector para a posição de origem
* O vector para a posição de destino
* 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 intermédio no caminho entre ambos os vectors que corresponde à quantidade fornecida.

Por exemplo, se o vector de origem for *(0, 0, 0)* e o vector de destino for *(10, 0, 10)*:

* Usar uma quantidade de 0 iria devolver *(0, 0, 0)*
* Usar uma quantidade de 0.3 iria devolver *(3, 0, 3)*
* Usar uma quantidade de 1 iria devolver *(10, 0, 10)*

Para implementar este `lerp()` na tua scene, recomendamos criar um [componente personalizado](/creator/content-creator-pt/scenes-sdk7/arquitetura/custom-components.md) para armazenar a informação necessária. Também precisas de 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)

// criar entidade
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,
})
```

![](/files/2f9ea15474555829fe0646b88ae28d6c02c76242)

### Girar entre dois ângulos via system

Para girar suavemente entre dois ângulos, usa o *slerp* (*spherical* linear interpolation) algorithm. Este algoritmo é muito semelhante a um *lerp*, mas lida com rotações de quaternion.

O `slerp()` function recebe três parâmetros:

* O [quaternion](https://en.wikipedia.org/wiki/Quaternion) angle for the origin rotation
* O [quaternion](https://en.wikipedia.org/wiki/Quaternion) angle for the target rotation
* A quantidade, um valor de 0 a 1 que representa que fração da translação fazer.

{% hint style="info" %}
**💡 Dica**: Podes passar valores de rotação em [euler](https://en.wikipedia.org/wiki/Euler_angles) degrees (from 0 to 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 tua scene, recomendamos armazenar os dados que entram na `Slerp()` function num [componente personalizado](/creator/content-creator-pt/scenes-sdk7/arquitetura/custom-components.md). Também precisas de 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)

// criar entidade
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**: Podes, em alternativa, representar a rotação com ângulos euler como `Vector3` values e usar uma `Lerp()` function, mas isso implicaria uma conversão de `Vector3` a `Quaternion` em cada frame. Os valores de rotação são armazenados internamente como quaternions no `Transform` component, por isso é mais eficiente para a scene trabalhar com quaternions.
{% endhint %}

![](/files/b965737c91cfb2ea1f914c1720b3a0c08a51e692)

Uma abordagem mais simples, mas menos eficiente, tira partido da `Quaternion.rotateTowards` function, e evita usar quaisquer componentes customizados.

```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` recebe três argumentos: a rotação inicial, a rotação final desejada e o incremento máximo por frame. Neste caso, como o incremento máximo é de `dt * 10` graus, a rotação será executada ao longo de um período de cerca de 9 segundos.

Nota que o system também verifica se a rotação está concluída e, em caso afirmativo, remove o system do engine. Caso contrário, o system continuaria a fazer cálculos em cada frame, mesmo depois de a rotação estar concluída.

### Alterar a escala entre dois tamanhos via system

Se quiseres que uma entity mude de tamanho de forma suave e sem alterar as suas proporções, usa o *lerp* (linear interpolation) algorithm of the `Scalar` objeto.

Caso contrário, se quiseres alterar os eixos em proporções diferentes, usa `Vector3` para representar a escala de origem e a escala de destino, e depois usa a *lerp* function do `Vector3`.

O `lerp()` function do `Scalar` objeto recebe três parâmetros:

* Um número para a escala de origem
* Um número para a escala de destino
* A quantidade, um valor de 0 a 1 que representa que fração do scaling fazer.

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

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

Para implementar este lerp na tua scene, recomendamos criar um component customizado para armazenar a informação necessária. Também precisas de definir um system que implemente a escalagem gradual em cada frame.

```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)

// criar entidade
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)
```

![](/files/a0e10ee5c174918cc39485def5113a7aeecd8d7c)

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

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

Também poderias usar uma função que produz resultados recorrentes, como uma função seno, para descrever um movimento que vai e vem.

Muitas vezes, estas transições não lineares podem dar muita vida a uma Scene. Um movimento que acelera ao longo de uma curva ou abranda gradualmente pode dizer muito sobre a natureza de um objeto ou personagem. Poderias até aproveitar funções matemáticas que adicionam efeitos de ressalto.

```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 de lerp para uma curva exponencial
function interpolate(t: number) {
	return t * t
}

engine.addSystem(LerpMove)

// criar entidade
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 de lerp linear que mostrámos antes, mas o `fraction` campo mapeado para um valor não linear em cada tick. Este valor não linear é usado para calcular a `lerp` function, resultando num movimento que segue uma curva exponencial.

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

![](/files/1a75a723305ae26a2c4cc315386846d65c0fa3c2)

### Seguir um caminho via system

Podes fazer com que uma entity percorra em loop um array de vectors, 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)

// criar entidade
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 vectors 3D. O `PathTransportData` componente personalizado guarda 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 que segmento do caminho está atualmente em uso.

O system é muito semelhante ao system no *lerp* exemplo, mas quando uma ação lerp é concluída, define os campos `target` e `origin` para novos valores. Se chegarmos ao fim do caminho, voltamos ao primeiro valor no caminho.

![](/files/2c853c0bca1313551c1ee722dce77dc701bf22e6)

## Tweens de textura

Para fazer uma textura deslizar suavemente, usa o `Tween` component com o `setTextureMove` function.

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

O tween de textura recebe as seguintes informações:

* `entity`: A entity para mover a textura de
* `start`: Um Vector2 para a posição inicial
* `end`: Um Vector2 para a posição final
* `duration`: Quantos milissegundos demora a 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 tiling. Por defeito, usa offset.
* `easingFunction`: Que função de easing usar. Vê [Tweens não lineares](#non-linear-tweens). Nota: Este parâmetro só é usado se for fornecida uma duração.

## Movimento constante de textura

Para fazer uma textura deslizar constantemente, usa o `Tween` component com o `setTextureMoveContinuous` function.

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

O tween contínuo de textura recebe as seguintes informações:

* `entity`: A entity para mover a textura de
* `direction`: Um Vector2 para o movimento
* `speed`: Quantas unidades por segundo a entity se moverá

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

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

Lê mais sobre tweens de textura no [Texture Tweens](/creator/content-creator-pt/scenes-sdk7/essenciais-de-conteudo-3d/materials.md#texture-tweens) .


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.decentraland.org/creator/content-creator-pt/scenes-sdk7/essenciais-de-conteudo-3d/move-entities.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
