> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/creator/content-creator-ko/sdk7/3d/move-entities.md).

# 엔티티 이동

점진적 변경을 통해 엔티티를 시간에 따라 서서히 이동, 회전, 크기 조정하는 방법입니다.

씬에서 일정 시간 동안 엔티티를 이동, 회전 또는 크기 조정하려면 다음을 사용하세요. `트윈` 컴포넌트를 사용하세요. 엔진은 원하는 변환을 부드럽게 수행하며, 지정된 지속 시간이 끝날 때까지 매 프레임마다 업데이트를 표시합니다. 또한 `Transform` 영향을 받는 엔티티의 컴포넌트 값도 실시간으로 업데이트되어, 씬 코드에서 근접성 검사를 해야 할 때 필요합니다.

{% hint style="info" %}
**💡 팁**: In the [Creator Hub의 씬 편집기](/creator/content-creator-ko/scene-editor/get-started/about-editor.md), 코드 없이 다음을 통해 엔티티를 이동할 수 있습니다. **동작**, 자세한 내용은 [어떤 항목이든 스마트 아이템으로 만들기](/creator/content-creator-ko/scene-editor/interactivity/make-any-item-smart.md).
{% endhint %}

Tween 컴포넌트에는 다음 함수들이 있습니다:

* `setMove`: 두 지점 사이를 이동
* `setRotate`: 두 방향 사이를 회전
* `setScale`: 두 크기 사이를 크기 조정
* `setMoveRotateScale`: 세 매개변수 모두를 동시에 전환
* `setMoveContinuous`: 같은 방향으로 계속 이동
* `setRotateContinuous`: 같은 방향으로 계속 회전
* `setTextureMove`: 두 위치 사이에서 머티리얼의 텍스처를 오프셋
* `setTextureMoveContinuous`: 머티리얼의 텍스처를 같은 방향으로 계속 오프셋

## 두 지점 사이를 이동

엔티티를 두 지점 사이로 이동하려면 다음을 생성하세요. `트윈` 다음을 가진 컴포넌트를 `setMove` 함수를 실행 중이라고 가정합니다.

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

이동 트윈에는 다음 정보가 필요합니다:

* `entity`: 이동할 엔티티
* `start`: 시작 위치를 나타내는 Vector3
* `end`: 종료 위치를 나타내는 Vector3
* `지속 시간`: 두 위치 사이를 이동하는 데 걸리는 밀리초 수

다음 선택적 매개변수도 사용할 수 있습니다:

* `easingFunction`: 사용할 이징 함수를 지정합니다. 다음을 참조하세요. [비선형 트윈](#non-linear-tweens)

## 두 방향 사이를 회전

엔티티를 두 방향 사이로 회전하려면 다음을 생성하세요. `트윈` 다음을 가진 컴포넌트를 `setRotate` 함수를 실행 중이라고 가정합니다.

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

회전 트윈에는 다음 정보가 필요합니다:

* `start`: 시작 회전을 나타내는 Quaternion
* `end`: 종료 회전을 나타내는 Quaternion
* `지속 시간`: 두 위치 사이를 이동하는 데 걸리는 밀리초 수

다음 선택적 매개변수도 사용할 수 있습니다:

* `easingFunction`: 사용할 이징 함수를 지정합니다. 다음을 참조하세요. [비선형 트윈](#non-linear-tweens)

### 피벗 점을 기준으로 회전

엔티티를 회전할 때 회전은 항상 엔티티의 중심 좌표를 기준으로 합니다. 다른 좌표계를 피벗 점으로 사용해 엔티티를 회전하려면, 피벗 점을 위치로 하는 보이지 않는 두 번째 엔티티를 만들고 회전하려는 엔티티의 부모로 설정하세요.

부모 엔티티를 회전하면 자식들도 모두 부모의 위치를 피벗 점으로 사용하여 회전합니다. 다음을 유의하세요. `위치` 자식 엔티티의 위치는 부모 엔티티의 위치를 기준으로 합니다.

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

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

이 예시에서는 시스템이 다음을 회전하고 있음을 유의하세요. `pivotEntity` 엔티티입니다. 이것은 다음의 부모입니다. `childEntity` 엔티티를 사용해 플레이어 카메라 위치에서 앞으로 레이를 추적합니다.

## 두 크기 사이를 크기 조정

엔티티의 크기를 두 크기 사이에서 변경하려면 다음을 생성하세요. `트윈` 다음을 가진 컴포넌트를 `setScale` 함수를 실행 중이라고 가정합니다.

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

```

크기 조정 트윈에는 다음 정보가 필요합니다:

* `start`: 시작 크기를 나타내는 Vector3
* `end`: 종료 크기를 나타내는 Vector3
* `지속 시간`: 두 위치 사이를 이동하는 데 걸리는 밀리초 수

다음 선택적 매개변수도 사용할 수 있습니다:

* `easingFunction`: 사용할 이징 함수를 지정합니다. 다음을 참조하세요. [비선형 트윈](#non-linear-tweens)

## 현재 위치에서 이동

트윈이 활성화되어 있는 동안 엔진은 엔티티의 `Transform` 컴포넌트 값을 실시간으로 업데이트합니다. 즉, 엔티티의 현재 위치를 다음에서 읽을 수 있습니다. `Transform` 컴포넌트에서 언제든 읽고, 그 값을 다음의 값으로 사용할 수 있습니다. `start` 새로운 트윈의 시작값으로 사용할 수 있습니다. 그러면 엔티티는 현재 어디에 있든 목표 지점으로 이동하며, 그곳에 어떻게 도달했는지는 상관없습니다.

```ts
pointerEventsSystem.onPointerDown(
	{
		entity: myEntity,
		opts: { button: InputAction.IA_POINTER, hoverText: 'Move' },
	},
	() => {
		Tween.setMove(myEntity, 
			Transform.get(myEntity).position, 
			Vector3.create(8, 1, 8), 
			2000,
			EasingFunction.EF_EASEOUTQUAD
		)
	}
)
```

{% hint style="warning" %}
**📔 참고**: 항상 명시적인 `start` 값은 전달하세요. 만약 `start` 매개변수가 설정되지 않으면 다음으로 간주됩니다. *(0, 0, 0)*, 따라서 엔티티는 이동하기 전에 씬 원점으로 순간 이동합니다. 마찬가지로, 엔티티의 현재 위치와 일치하지 않는 하드코딩된 `start` 를 전달하면, 엔티티는 이동하기 전에 해당 위치로 눈에 띄게 순간 이동합니다. 이동을 엔티티가 현재 있는 곳에서 시작하고 싶을 때는 언제나 이 섹션의 예시처럼 엔티티의 `Transform` 컴포넌트에서 현재 위치를 읽어 오세요.
{% endhint %}

### 이동 중 재지정

호출하면 `setMove` (또는 다른 트윈 함수들 중 하나)를 이미 실행 중인 트윈이 있는 엔티티에 호출하면 실행 중인 트윈이 새 트윈으로 대체됩니다. 다음을 기준으로 하기 때문에 `start` 는 엔티티의 실시간 위치에서 읽히므로, 이동 중에도 엔티티의 경로가 부드럽게 재지정됩니다. 즉, 그 순간 어디에 있든 방향을 바꾸며 아무런 점프 없이 진행됩니다.

아래 예시는 플레이어가 클릭한 패드로 엔티티를 이동시킵니다. 엔티티가 아직 이동 중일 때 플레이어가 다른 패드를 클릭하면, 엔티티는 새 목표를 향해 부드럽게 방향을 바꿉니다.

```ts
function moveTo(target: Vector3) {
	Tween.setMove(shipEntity, 
		Transform.get(shipEntity).position, 
		target, 
		2000,
		EasingFunction.EF_EASEOUTQUAD
	)
}

pointerEventsSystem.onPointerDown(
	{
		entity: padA,
		opts: { button: InputAction.IA_POINTER, hoverText: 'Move here' },
	},
	() => moveTo(Vector3.create(2, 1, 2))
)

pointerEventsSystem.onPointerDown(
	{
		entity: padB,
		opts: { button: InputAction.IA_POINTER, hoverText: 'Move here' },
	},
	() => moveTo(Vector3.create(14, 1, 14))
)
```

엔티티가 목적지에 도달했을 때 로직을 실행하려면 다음을 참조하세요. [트윈 완료 시](#on-tween-finished).

{% hint style="warning" %}
**📔 참고**: 이 방법은 클릭이나 웨이포인트 도달 같은 간헐적인 이벤트에만 사용하세요. 예를 들어 플레이어를 따라가는 엔티티처럼 목표가 계속 바뀌는 경우에는 매 프레임마다 새 트윈을 만들지 마세요. 엔티티가 눈에 띄게 끊깁니다. 다음을 참조하세요. [움직이는 대상 따라가기](#follow-a-moving-target).
{% endhint %}

## 비선형 트윈

트윈은 서로 다른 **이징 함수** 을 따라 시간에 따른 변화 속도에 영향을 줄 수 있습니다. **선형적** 함수는 변화 속도가 시작부터 끝까지 일정하다는 뜻입니다. 선택할 수 있는 옵션은 아주 많으며, 시작 및/또는 끝이 얼마나 느리게 시작하는지에 따라 서로 다른 곡선 형태를 그립니다. 하나의 **easeinexpo** 곡선은 느리게 시작해 빠르게 끝나며, 지수적으로 속도가 증가합니다. 반대로 **easeoutexpo** 곡선은 빠르게 시작해 느리게 끝납니다.

{% hint style="info" %}
**💡 팁**: 다양한 이동 곡선을 실험해 보세요. 차이는 종종 미묘하지만, 우리는 움직임을 통해 무게, 마찰, 심지어 성격 같은 정보를 무의식적으로 해석합니다.
{% endhint %}

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

```

선택적 `easingFunction` 매개변수의 값은 다음에서 가져옵니다. `EasingFunction` 열거형으로, 다음 옵션을 제공합니다:

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

## 상수 회전

엔티티를 계속 회전시키려면 다음을 사용하세요. `트윈` 다음을 가진 컴포넌트를 `setRotateContinuous` 함수를 실행 중이라고 가정합니다.

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

계속 회전 트윈에는 다음 정보가 필요합니다:

* `entity`: 회전할 엔티티
* `방향`: 회전할 축을 결정하는 Quaternion입니다. 이 회전에서는 축만 중요하며 각도의 크기는 무시됩니다. 예를 들어, `Quaternion.fromEulerDegrees(0, -1, 0)` 및 `Quaternion.fromEulerDegrees(0, -90, 0)` 둘 다 동일하게 동작하며, 모두 다음을 중심으로 회전합니다. *y* 축을 음의 방향으로 회전합니다.
* `속도`: 엔티티가 초당 몇 도 회전할지입니다. 음수 값이면 반대 방향으로 회전합니다.

다음 선택적 매개변수도 사용할 수 있습니다:

* `지속 시간`: 회전을 유지할 밀리초 수입니다. 이 시간이 지나면 회전이 멈춥니다.

## 상수 이동

엔티티를 같은 방향으로 계속 이동시키려면 다음을 사용하세요. `트윈` 다음을 가진 컴포넌트를 `setMoveContinuous` 함수를 실행 중이라고 가정합니다.

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

계속 이동 트윈에는 다음 정보가 필요합니다:

* `entity`: 이동할 엔티티
* `방향`: 이동 방향을 나타내는 Vector3
* `속도`: 엔티티가 초당 몇 미터 이동할지

다음 선택적 매개변수도 사용할 수 있습니다:

* `지속 시간`: 이동을 유지할 밀리초 수입니다. 이 시간이 지나면 이동이 멈춥니다.

계속 이동 트윈에는 다음 정보가 필요합니다:

### 움직이는 대상 따라가기

`setMoveContinuous` 목적지가 계속 바뀔 때도 올바른 도구입니다. 예를 들어 플레이어를 따라가는 엔티티가 그렇습니다. 대신 새 `setMove` 를 매 프레임마다 플레이어의 최신 위치를 향해 만드는 유혹이 있지만, 그러면 눈에 띄는 끊김이 생깁니다.

그 이유는 다음에서 읽어 온 위치가 `Transform` 트윈이 적용된 엔티티의 컴포넌트는 엔진이 마지막으로 씬에 보고한 위치이므로, 실제 엔티티 위치보다 몇 프레임 뒤처지기 때문입니다. 그 값을 다음으로 전달하면 `start` 새로운 트윈의 시작값으로 사용되면, 엔진이 즉시 적용하고 엔티티는 몇 프레임 전의 위치로 되돌아갑니다. 그러한 보정이 한 번은 눈에 띄지 않지만, 초당 여러 번 반복되면 그렇지 않습니다.

`setMoveContinuous` 시작점 대신 방향과 속도를 주기 때문에 이 문제를 완전히 피할 수 있습니다. 엔진은 계속 엔티티의 실제 위치를 사용하므로, 방향을 바꾸기 위해 트윈을 교체해도 엔티티가 점프하지 않습니다.

```ts
const CHASE_SPEED = 3
const STOP_DISTANCE = 1

engine.addSystem(() => {
	const playerPosition = Transform.get(engine.PlayerEntity).position
	const myPosition = Transform.get(myEntity).position

	if (Vector3.distance(myPosition, playerPosition) <= STOP_DISTANCE) {
		// 충분히 가까움: 계속 트윈에는 목적지가 없으므로 직접 중지해야 합니다
		if (Tween.has(myEntity)) {
			Tween.deleteFrom(myEntity)
		}
		return
	}

	const direction = Vector3.subtract(playerPosition, myPosition)
	direction.y = 0 // 플레이어가 점프해도 지면에 머물기

	Tween.setMoveContinuous(myEntity, Vector3.normalize(direction), CHASE_SPEED)
})
```

{% hint style="warning" %}
**📔 참고**: 계속 트윈은 스스로 끝나지 않으므로, 엔티티는 다음을 제거할 때까지 계속 이동합니다. `트윈` 사용해 컴포넌트를 `Tween.deleteFrom()`, 또는 선택적 `지속 시간` 이 소진될 때까지입니다. 또한 매 프레임마다 다시 조준하면 매 프레임 엔진에 업데이트가 전송됩니다. 이를 피하려면 다음만 호출하세요. `setMoveContinuous` 방향이 의미 있을 정도로 충분히 바뀌었을 때만 다시 호출하세요.
{% endhint %}

## Tween 시퀀스

엔티티가 여러 트윈을 순서대로 재생하게 하려면 다음을 사용하세요. `TweenSequence` 컴포넌트입니다. 이 컴포넌트에는 두 필드가 필요합니다:

* `sequence`: 여러 트윈 정의가 들어 있는 배열로, 순차적으로 실행됩니다. 배열은 비어 있을 수 있으며, 이 경우 현재 트윈만 재생합니다.
* `loop` *(선택 사항)*: 제공하지 않으면 시퀀스는 한 번만 재생됩니다. 필드가 있으면 값은 다음의 값이어야 합니다. `TweenLoop` 열거형입니다. 허용되는 값은 다음과 같습니다:
  * `TL_RESTART`: 시퀀스가 끝나면 다시 시작합니다. 마지막 상태가 첫 상태와 같지 않으면 엔티티가 한 상태에서 다른 상태로 즉시 점프합니다.
  * `TL_YOYO`: 시퀀스가 끝나면 뒤로 진행하며, 다시 시작점에 도달할 때까지 모든 트윈을 역순으로 실행합니다. 그런 다음 다시 시작합니다.

### 앞뒤로 이동

플랫폼이 두 위치 사이를 계속 앞뒤로 움직이게 하려면 다음을 비워 두세요. `sequence` 배열을 비워 두고 다음으로 설정하세요. `loop` 를 `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 })
```

엔티티는 시작점과 종료점 사이를 앞뒤로 이동하며, 양방향 모두 같은 지속 시간과 같은 이징 함수를 사용합니다.

### 경로 따라가기

엔티티가 여러 지점을 가진 더 복잡한 경로를 따라가게 하려면 다음에 트윈 정의 목록을 제공하세요. `sequence` 의 `TweenSequence` 컴포넌트를 부여해야 합니다.

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

TweenSequence 안에서 트윈을 정의할 때는 다음의 더 상세한 형식을 사용해야 함을 유의하세요. `Tween.Mode.Move`, 또는 `Tween.Mode.Rotate`, 또는 `Tween.Mode.Scale` 을 사용해 트윈을 정의하세요. 이 더 상세한 형식에서는 다음을 지정해야 합니다:

* `지속 시간`: 두 위치 사이를 이동하는 데 걸리는 밀리초 수
* `easingFunction`: 사용할 이징 함수를 지정합니다. 다음을 참조하세요. [비선형 트윈](#non-linear-tweens). 이 형식에서는 값이 필수입니다.
* `mode`: 트윈의 모드이며, 다음 중 하나일 수 있습니다. `Tween.Mode.Move`, `Tween.Mode.Rotate`, 또는 `Tween.Mode.Scale`.

그리고 다음 안에서 `mode` 필드에는 다음을 지정해야 합니다:

* `start`: 트윈의 시작값
* `end`: 트윈의 종료값

## 트윈 완료 시

사용: `tweenSystem.tweenCompleted` 를 사용해 트윈이 끝났는지 감지하세요. 이는 트윈이 끝날 때 동작을 수행하는 데 유용하며, 예를 들어 엘리베이터 문을 여는 데 사용할 수 있습니다.

```ts
engine.addSystem(() => {
	const tweenCompleted = tweenSystem.tweenCompleted(myEntity)
	if (tweenCompleted) {
		//소리 재생
	}
})
```

## 동시 트윈

엔티티를 초기 상태와 종료 상태 사이에서 이동, 회전, 크기 조정하려면 다음을 생성하세요. `트윈` 다음을 가진 컴포넌트를 `setMoveRotateScale` 함수를 사용하세요. 이 함수는 이 세 매개변수의 어떤 조합에도 사용할 수 있습니다.

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

Tween.setMoveRotateScale(myEntity, {
	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
})
```

이동 트윈에는 다음 정보가 필요합니다:

* `entity`: 이동할 엔티티
* `params`: 다음 매개변수들을 가진 객체
  * `위치`: *(선택 사항)* 다음과 같은 `start` 및 `end` 값을, 둘 다 `Vector3`.
  * `회전`: *(선택 사항)* 다음과 같은 `start` 및 `end` 값을, 둘 다 `Quaternion`.
  * `크기`: *(선택 사항)* 다음과 같은 `start` 및 `end` 값을, 둘 다 `Vector3`. 다음 중 최소 하나는 `위치`, `회전` 또는 `크기` 제공되어야 합니다.
  * `지속 시간`: *(필수)* 두 값 집합 사이를 전환하는 데 걸리는 밀리초 수
  * `easingFunction`: *(선택 사항)* 사용할 이징 함수. 다음을 참조하세요. [비선형 트윈](#non-linear-tweens)

엔티티는 하나의 `트윈` 컴포넌트만 가질 수 있으며, 각 트윈 컴포넌트는 한 번에 하나의 변환만 수행할 수 있습니다. 다음을 통해 `setMoveRotateScale` 트윈 타입을 사용하면 엔티티를 옆으로 이동시키면서 동시에 회전시킬 수 있지만, 이 두 동작은 같은 타임라인을 따릅니다. 전환을 서로 독립적으로 해야 한다면 우회 방법으로 부모-자식 구조의 엔티티를 사용할 수 있습니다. 예를 들어, 보이지 않는 부모 엔티티가 옆으로 이동하고, 보이는 자식이 회전하도록 만들 수 있습니다.

다음 코드 조각에서는 부모 엔티티가 회전하고 자식은 크기가 커집니다.

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

## 트윈 일시 중지

트윈을 일시 중지하려면 `playing` 속성을 false로 변경하세요. 다시 시작하려면 true로 되돌리세요.

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

계속할 필요가 없는 트윈을 끝내려면 다음을 삭제하세요. `트윈` 엔티티에서 컴포넌트를 삭제하세요. 엔티티가 또한 다음을 사용하고 있었다면 `TweenSequence` 컴포넌트도 함께 삭제하세요.

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

## 시스템 기반 트윈

Tween 컴포넌트를 사용해 엔진이 변환을 처리하게 하는 대신, 다음을 통해 이 전환을 프레임마다 점진적으로 수행하는 편을 선호할 수 있습니다. [시스템](/creator/content-creator-ko/sdk7/architecture/systems.md) 씬의 시스템. 함수가 실행될 때마다 엔티티를 조금씩 이동시키는 방식입니다.

한편으로는 매 프레임 움직임을 다시 계산할 때 더 많은 제어권을 제공합니다. 다른 한편으로는 코드가 더 복잡하고, 성능이 낮은 기기에서는 트윈이 느리게 느껴지며 각 증가분이 눈에 띌 수 있습니다.

### 시스템으로 이동

엔티티를 이동시키는 가장 쉬운 방법은 다음을 점진적으로 수정하는 것입니다. *위치* 다음에 저장된 값 `Transform` 컴포넌트를 부여해야 합니다.

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

이 예시에서는 게임 루프의 틱마다 엔티티를 0.05미터씩 이동시키고 있습니다.

`Vector3.Forward()` 는 앞으로 향하고 길이가 1미터인 벡터를 반환합니다. 이 예시에서는 그다음 이 벡터의 길이를 다음으로 1/20로 줄입니다. `Vector3.scale()`. 씬의 초당 프레임 수가 30이라면, 엔티티는 초당 1.5미터의 속도로 이동하는 것입니다.

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

### 시스템으로 회전

엔티티를 회전시키는 가장 쉬운 방법은 Transform 컴포넌트의 값을 조금씩 점진적으로 변경하고, 이를 시스템 함수의 일부로 실행하는 것입니다.

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

현재 회전을 각 증분과 결합하기 위해 다음을 사용하고 있음을 유의하세요. `Quaternion.multiply`. 쿼터니언 수학에서는 두 회전을 더하는 것이 아니라 곱해서 결합합니다. 한 쿼터니언에 다른 쿼터니언을 곱해 얻는 결과 회전은, 먼저 하나의 회전을 수행한 다음 다른 회전을 수행했을 때의 최종 회전과 동일합니다.

이 예제에서는 게임 루프의 각 틱마다 엔티티를 위쪽 방향으로 1도씩 회전합니다.

{% hint style="info" %}
**💡 팁**: 엔티티가 항상 플레이어를 향하도록 회전하게 하려면, 다음을 추가할 수 있습니다. [`빌보드` 컴포넌트](/creator/content-creator-ko/sdk7/3d/entity-positioning.md#face-the-user).
{% endhint %}

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

### 피벗 포인트를 기준으로 시스템을 통해 회전

엔티티를 회전할 때 회전은 항상 엔티티의 중심 좌표를 기준으로 합니다. 다른 좌표계를 피벗 점으로 사용해 엔티티를 회전하려면, 피벗 점을 위치로 하는 보이지 않는 두 번째 엔티티를 만들고 회전하려는 엔티티의 부모로 설정하세요.

부모 엔티티를 회전하면 자식들도 모두 부모의 위치를 피벗 점으로 사용하여 회전합니다. 다음을 유의하세요. `위치` 자식 엔티티의 위치는 부모 엔티티의 위치를 기준으로 합니다.

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

이 예시에서는 시스템이 다음을 회전하고 있음을 유의하세요. `pivotEntity` 엔티티입니다. 이것은 다음의 부모입니다. `childEntity` 엔티티를 사용해 플레이어 카메라 위치에서 앞으로 레이를 추적합니다.

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

### 지연 시간에 맞게 이동 조정

시나리오를 방문하는 플레이어가 프레임 속도를 따라가기 어려워한다고 가정해 보세요. 그러면 모든 프레임의 시간이 균등하지 않지만 각 프레임이 엔티티를 같은 양만큼 이동시키므로, 움직임이 끊겨 보일 수 있습니다.

이 고르지 않은 타이밍은 다음을 사용해 보정할 수 있습니다. `dt` 매개변수를 사용해 이동 스케일을 조정합니다.

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

위 예제는 프레임 속도가 떨어져도 이동 속도를 위의 이동 예제와 거의 같게 유지합니다. 초당 30프레임으로 실행할 때, `dt` 의 값은 1/30입니다.

회전량에 다음을 곱해 같은 방식으로 회전을 부드럽게 만들 수도 있습니다. `dt`.

### 시스템을 통해 두 점 사이를 이동

엔티티가 두 점 사이를 부드럽게 이동하게 하려면 다음을 사용하세요. *lerp* (선형 보간) 알고리즘을 사용하세요. 이 알고리즘은 게임 개발에서 매우 잘 알려져 있으며, 정말 유용합니다.

그 `lerp()` 함수는 세 개의 매개변수를 받습니다:

* 시작 위치를 나타내는 벡터
* 대상 위치를 나타내는 벡터
* 양, 즉 이동할 비율을 0에서 1 사이의 값으로 나타낸 것입니다.

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

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

선형 보간 알고리즘은 제공된 양에 맞는 두 벡터 사이 경로의 중간 지점을 찾습니다.

예를 들어, 시작 벡터가 *(0, 0, 0)* 이고 대상 벡터가 *(10, 0, 10)*:

* 양을 0으로 설정하면 *(0, 0, 0)*
* 양을 0.3으로 설정하면 *(3, 0, 3)*
* 양을 1로 설정하면 *(10, 0, 10)*

이를 구현하려면 `lerp()` 씬에서 다음을 생성하는 것을 권장합니다. [사용자 지정 컴포넌트](/creator/content-creator-ko/sdk7/architecture/custom-components.md) 필요한 정보를 저장하기 위해서입니다. 또한 각 프레임에서 점진적인 이동을 구현하는 시스템을 정의해야 합니다.

```ts
// 사용자 지정 컴포넌트 정의
const MoveTransportData = {
	start: Schemas.Vector3,
	end: Schemas.Vector3,
	fraction: Schemas.Float,
	speed: Schemas.Float,
}

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

// 시스템 정의
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)

// 엔티티 생성
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://3980763956-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-821f48b2d4a6afbb451813d7df73aa4196228c0f%2Flerp-move.gif?alt=media)

### 시스템을 통해 두 각도 사이를 회전

두 각도 사이를 부드럽게 회전하려면 다음을 사용하세요. *slerp* (*구면* 선형 보간) 알고리즘입니다. 이 알고리즘은 매우 유사하지만 *lerp*, 쿼터니언 회전을 처리합니다.

그 `slerp()` 함수는 세 개의 매개변수를 받습니다:

* 그 [쿼터니언](https://en.wikipedia.org/wiki/Quaternion) 시작 회전의 각도
* 그 [쿼터니언](https://en.wikipedia.org/wiki/Quaternion) 대상 회전의 각도
* 양, 즉 이동할 비율을 0에서 1 사이의 값으로 나타낸 것입니다.

{% hint style="info" %}
**💡 팁**: 회전 값을 다음으로 전달할 수 있습니다. [오일러](https://en.wikipedia.org/wiki/Euler_angles) 도를 사용하여(0에서 360 사이) `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)
```

이를 씬에 구현하려면, 다음으로 들어가는 데이터를 저장하는 것을 권장합니다. `Slerp()` 함수의 데이터를 [사용자 지정 컴포넌트](/creator/content-creator-ko/sdk7/architecture/custom-components.md)에 저장하세요. 또한 각 프레임에서 점진적인 회전을 구현하는 시스템을 정의해야 합니다.

```ts
// 사용자 지정 컴포넌트 정의
const RotateSlerpData = {
	start: Schemas.Quaternion,
	end: Schemas.Quaternion,
	fraction: Schemas.Float,
	speed: Schemas.Float,
}

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

// 시스템 정의
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)

// 엔티티 생성
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" %}
**📔 참고**: 대신 회전 값을 오일러 각 `Vector3` 값으로 나타내고 다음을 사용할 수 있습니다. `Lerp()` 함수도 사용할 수 있지만, 그러면 다음에서 변환이 필요하다는 뜻입니다. `Vector3` 를 `Quaternion` 매 프레임마다. 회전 값은 내부적으로 `Transform` 컴포넌트에 쿼터니언으로 저장되므로, 씬에서 쿼터니언을 사용하는 것이 더 효율적입니다.
{% endhint %}

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

이 작업을 수행하는 더 간단하지만 덜 효율적인 방법은 다음을 활용합니다. `Quaternion.rotateTowards` 함수를 사용하며, 사용자 지정 컴포넌트를 전혀 사용하지 않습니다.

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

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

위 예제에서 `Quaternion.rotateTowards` 세 개의 인수를 받습니다: 초기 회전, 원하는 최종 회전, 그리고 프레임당 최대 증가량입니다. 이 경우 최대 증가량이 `dt * 10` 도이므로, 회전은 약 9초에 걸쳐 수행됩니다.

시스템이 회전이 완료되었는지도 확인하고, 완료되면 엔진에서 시스템을 제거한다는 점에 유의하세요. 그렇지 않으면 회전이 완료된 뒤에도 시스템이 매 프레임마다 계속 계산을 수행하게 됩니다.

### 시스템을 통해 두 크기 사이에서 스케일 변경

엔티티의 크기를 비율을 바꾸지 않고 부드럽게 변경하려면 다음을 사용하세요. *lerp* (선형 보간) 알고리즘을 `Scalar` 객체.

반대로, 축을 서로 다른 비율로 변경하고 싶다면 다음을 사용하세요. `Vector3` 시작 스케일과 대상 스케일을 나타내기 위해 다음을 사용한 다음, *lerp* 함수를 `Vector3`.

그 `lerp()` 함수를 `Scalar` 객체는 세 개의 매개변수를 받습니다:

* 시작 스케일에 대한 숫자
* 대상 스케일에 대한 숫자
* 양, 즉 스케일링할 비율을 0에서 1 사이의 값으로 나타낸 것입니다.

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

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

씬에서 이 lerp를 구현하려면, 필요한 정보를 저장할 사용자 지정 컴포넌트를 생성하는 것을 권장합니다. 또한 각 프레임에서 점진적인 스케일 조정을 구현하는 시스템을 정의해야 합니다.

```ts
// 사용자 지정 컴포넌트 정의
const ScaleTransportData = {
	start: Schemas.Number,
	end: Schemas.Number,
	fraction: Schemas.Float,
	speed: Schemas.Float,
}

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

// 시스템 정의
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)

// 엔티티 생성
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://3980763956-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-860402e1bd825057ab2b47d06e95ba6061dc5327%2Flerp-scale.gif?alt=media)

### 시스템을 통해 두 점 사이를 불규칙한 속도로 이동

lerp 방법을 사용하는 동안 이동 속도를 비선형으로 만들 수 있습니다. 이전 예제에서는 매 프레임마다 주어진 양만큼 lerp 값을 증가시켰지만, 수학 함수를 사용해 수치를 지수적으로 증가시키거나 다른 측정값으로 변화시켜 색다른 이동 속도를 만들 수도 있습니다.

사인 함수처럼 반복 결과를 내는 함수를 사용해 왔다 갔다 하는 움직임을 표현할 수도 있습니다.

이러한 비선형 전환은 종종 씬에 많은 생동감을 불어넣습니다. 곡선을 따라 가속하거나 서서히 감속하는 움직임은 객체나 캐릭터의 특성을 많이 드러낼 수 있습니다. 바운스 효과를 추가하는 수학 함수를 활용할 수도 있습니다.

```ts
// 사용자 지정 컴포넌트 정의
const MoveTransportData = {
	start: Schemas.Vector3,
	end: Schemas.Vector3,
	fraction: Schemas.Float,
	speed: Schemas.Float,
}

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

// 시스템 정의
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)
	}
}

// lerp 비율을 지수 곡선에 매핑
function interpolate(t: number) {
	return t * t
}

engine.addSystem(LerpMove)

// 엔티티 생성
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,
})
```

위 예제는 앞서 보여드린 선형 lerp 예제와 동일하지만, `비율` 필드가 매 틱마다 비선형 값으로 매핑됩니다. 이 비선형 값은 `lerp` 함수를 계산하는 데 사용되며, 결과적으로 지수 곡선을 따르는 움직임이 만들어집니다.

위에서 보인 것과 같은 방식으로, 선형 전환을 곡선에 매핑하여 회전이나 스케일의 전환도 마찬가지로 매핑할 수 있습니다.

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

### 시스템을 통해 경로 따르기

엔티티가 벡터 배열을 순환하며 각 지점 사이를 lerp 이동해 더 복잡한 경로를 따라가게 할 수 있습니다.

```ts
// 사용자 지정 컴포넌트 정의
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
)

// 시스템 정의
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)

// 엔티티 생성
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,
})
```

위 예제는 네 개의 3D 벡터로 이루어진 3D 경로를 정의합니다.  `PathTransportData` 사용자 지정 컴포넌트는 *lerp* 위 예제의 사용자 지정 컴포넌트에서 사용된 것과 동일한 데이터를 보유하지만, `경로` 배열과, 경로의 모든 지점을 담은 `pathTargetIndex` 필드를 추가해 현재 사용 중인 경로의 구간을 추적합니다.

시스템은 *lerp* 예제의 시스템과 매우 비슷하지만, lerp 작업이 완료되면 `target` 및 `origin` 필드들을 새 값으로 설정합니다. 경로의 끝에 도달하면 경로의 첫 번째 값으로 돌아갑니다.

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

## 텍스처 트윈

텍스처를 부드럽게 슬라이드시키려면 다음을 사용하세요. `트윈` 다음을 가진 컴포넌트를 `setTextureMove` 함수를 실행 중이라고 가정합니다.

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

텍스처 트윈은 다음 정보를 사용합니다:

* `entity`: 텍스처를 이동시킬 엔티티
* `start`: 시작 위치를 나타내는 Vector2
* `end`: 종료 위치를 나타내는 Vector2
* `지속 시간`: 두 위치 사이를 이동하는 데 걸리는 밀리초 수

다음 선택적 매개변수도 사용할 수 있습니다:

* `movementType`: (선택 사항) 이동이 offset 필드에서 이뤄질지 tiling 필드에서 이뤄질지를 정의합니다. 기본값은 offset입니다.
* `easingFunction`: 사용할 이징 함수를 지정합니다. 다음을 참조하세요. [비선형 트윈](#non-linear-tweens).

## 일정한 텍스처 이동

텍스처를 계속 슬라이드시키려면 다음을 사용하세요. `트윈` 다음을 가진 컴포넌트를 `setTextureMoveContinuous` 함수를 실행 중이라고 가정합니다.

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

연속 텍스처 트윈은 다음 정보를 사용합니다:

* `entity`: 텍스처를 이동시킬 엔티티
* `방향`: 이동을 위한 Vector2
* `속도`: 엔티티가 초당 몇 단위만큼 이동할지

다음 선택적 매개변수도 사용할 수 있습니다:

* `movementType`: 이동이 offset 필드에서 이뤄질지 tiling 필드에서 이뤄질지를 정의합니다. 기본값은 offset입니다.
* `지속 시간`: 이동을 유지할 밀리초 수입니다. 이 시간이 지나면 이동이 멈춥니다.

텍스처 트윈에 대해 더 알아보려면 [Texture Tweens](/creator/content-creator-ko/sdk7/3d/materials.md#texture-tweens) 섹션.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.decentraland.org/creator/content-creator-ko/sdk7/3d/move-entities.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
