> 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" %}
**💡 팁**: 다음에서 [Creator Hub의 Scene Editor](/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
)
```

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

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

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

* `easingFunction`: 사용할 이징 함수입니다. 다음을 참조하세요 [비선형 tween](#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
)
```

이 회전 tween에는 다음 정보가 필요합니다:

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

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

* `easingFunction`: 사용할 이징 함수입니다. 다음을 참조하세요 [비선형 tween](#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
)	

```

이 크기 조절 tween에는 다음 정보가 필요합니다:

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

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

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

## 현재 위치에서 이동

tween이 활성화되어 있는 동안 엔진은 엔티티의 `Transform` 컴포넌트 값을 실시간으로 업데이트합니다. 즉, 언제든지 엔티티의 현재 위치를 그 `Transform` 컴포넌트에서 읽어와 그 값을 새로운 tween의 `start` 으로 사용할 수 있다는 뜻입니다. 그러면 엔티티는 현재 어디에 있든 그 위치에서 목표 지점으로 이동합니다.

```ts
pointerEventsSystem.onPointerDown(
	{
		entity: myEntity,
		opts: { button: InputAction.IA_POINTER, hoverText: '이동' },
	},
	() => {
		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` (또는 다른 tween 함수 중 하나)를 이미 실행 중인 tween이 있는 엔티티에 호출하면 실행 중인 tween이 새 tween으로 대체됩니다. 다음이 엔티티의 실시간 위치에서 읽히므로 `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: '여기로 이동' },
	},
	() => moveTo(Vector3.create(2, 1, 2))
)

pointerEventsSystem.onPointerDown(
	{
		entity: padB,
		opts: { button: InputAction.IA_POINTER, hoverText: '여기로 이동' },
	},
	() => moveTo(Vector3.create(14, 1, 14))
)
```

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

{% hint style="warning" %}
**📔 참고**: 클릭이나 웨이포인트 도달처럼 가끔 발생하는 이벤트에만 이렇게 재지정하세요. 예를 들어 플레이어를 따라가는 엔티티처럼 대상이 계속 변한다면, 매 프레임마다 새 tween을 만들지 마세요. 엔티티가 눈에 띄게 끊깁니다. 다음을 참조하세요. [움직이는 대상 따라가기](#follow-a-moving-target).
{% endhint %}

## 비선형 tween

트윈은 다른 **이징 함수** 을 따라 시간에 따른 변화 속도에 영향을 줄 수 있습니다.  **선형** 함수는 변화 속도가 처음부터 끝까지 일정함을 의미합니다. 시작과 끝이 얼마나 천천히 시작하는지와 그 정도에 따라 서로 다른 모양의 곡선을 그리는 다양한 옵션이 있습니다. 하나의 **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
)
```

이 연속 회전 tween에는 다음 정보가 필요합니다:

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

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

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

## 일정한 이동

엔티티를 같은 방향으로 계속 이동시키려면, 다음을 사용하세요. `트윈` 컴포넌트를 `setMoveContinuous` 함수.

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

이 연속 이동 tween에는 다음 정보가 필요합니다:

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

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

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

이 연속 이동 tween에는 다음 정보가 필요합니다:

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

`setMoveContinuous` 은 목적지가 계속 바뀔 때도 적절한 도구입니다. 예를 들어 플레이어를 따라가는 엔티티가 그렇습니다. 대신 매 프레임마다 새 `setMove` tween을 만들어 플레이어의 최신 위치를 목표로 삼고 싶어질 수 있지만, 그러면 눈에 띄는 끊김이 생깁니다.

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

`setMoveContinuous` 은 시작점 대신 방향과 속도를 주기 때문에 이 문제를 아예 피합니다. 엔진은 계속 엔티티의 실제 위치를 사용하므로, 방향을 바꾸려고 tween을 교체해도 엔티티가 점프하지 않습니다.

```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) {
		// 충분히 가까움: 연속 tween에는 목적지가 없으므로 직접 중지합니다
		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은 스스로 끝나지 않으므로, 다음을 제거할 때까지 엔티티는 계속 움직입니다 `트윈` 컴포넌트를 `Tween.deleteFrom()`, 또는 선택적 `지속 시간` 이 다할 때까지입니다. 또한 매 프레임마다 다시 조준하면 매 프레임마다 엔진에 업데이트를 보내게 됩니다. 이를 피하려면 `setMoveContinuous` 방향이 의미 있게 바뀌었을 때만 다시 호출하세요.
{% endhint %}

## Tween 시퀀스

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

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

### 앞뒤로 이동

플랫폼이 두 위치 사이를 계속 앞뒤로 이동하게 하려면, `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 })
```

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

### 경로 따라가기

여러 지점을 가진 더 복잡한 경로를 엔티티가 따라가게 하려면, 다음의 tween 정의 목록을 제공하세요. `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을 정의할 때는 다음의 더 장황한 형식을 사용해야 한다는 점에 유의하세요. `Tween.Mode.Move`또는 `Tween.Mode.Rotate`또는 `Tween.Mode.Scale` 를 사용해 tween을 정의하세요. 이 더 장황한 형식에서는 다음을 지정해야 합니다:

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

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

* `start`: tween의 시작 값
* `end`: tween의 종료 값

## 트윈 완료 시

다음을 사용하세요 `tweenSystem.tweenCompleted` 를 사용해 tween이 완료되었는지 감지하세요. 이는 tween이 끝날 때 동작을 수행해야 할 때 유용할 수 있습니다. 예를 들어 엘리베이터 문을 여는 데 사용할 수 있습니다.

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

## 동시 tween

엔티티를 초기 상태와 종료 상태 사이에서 이동, 회전, 크기 조절하려면, 다음을 생성하세요. `트윈` 컴포넌트를 `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
})
```

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

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

엔티티는 하나의 `트윈` 컴포넌트만 가질 수 있으며, 각 tween 컴포넌트는 한 번에 하나의 변형만 수행할 수 있습니다. 이를 통해 `setMoveRotateScale` tween 타입을 사용하면 엔티티가 옆으로 이동하면서 동시에 회전하게 할 수 있지만, 이 두 동작은 동일한 타임라인을 따릅니다. 서로 독립적인 전환이 필요하다면, 임시 해결책으로 부모-자식 구조의 엔티티를 사용할 수 있습니다. 예를 들어, 옆으로 이동하는 보이지 않는 부모 엔티티와 회전하는 보이는 자식 엔티티를 둘 수 있습니다.

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

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

## tween 일시 중지

tween을 일시 중지하려면, `playing` 속성을 false로 변경하세요. 다시 재개하려면 true로 다시 변경합니다.

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

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

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

## 시스템 기반 tween

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

한편으로는, 매 프레임마다 움직임을 다시 계산할 때 더 많은 제어권을 얻을 수 있습니다. 다른 한편으로는 코드가 더 복잡해지고, 성능이 낮은 기기를 사용하는 플레이어는 각 증분을 알아차리며 tween이 지연되는 것처럼 느낄 수 있습니다.

### 시스템을 통한 이동

엔티티를 이동시키는 가장 쉬운 방법은 다음의 *위치* 값을 점진적으로 변경하는 것입니다 `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미터인 벡터를 반환합니다. 이 예시에서는 이 벡터를 `Vector3.scale()`를 사용해 길이의 1/20로 줄입니다. 씬이 초당 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\&token=22d02b05-a757-47f7-a2a8-20239219c328)

### 시스템을 통한 회전

엔티티를 회전시키는 가장 쉬운 방법은 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\&token=1d4332ad-29a1-4977-a13d-1c4e1777aa71)

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

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

부모 엔티티를 회전하면 자식들은 모두 부모의 위치를 피벗 포인트로 사용해 회전합니다. 다음을 참고하세요. `위치` 자식 엔티티의 위치는 부모 엔티티의 위치를 기준으로 합니다.

```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\&token=a22ecc83-1398-4936-88ff-fe29799e00b4)

### 지연 시간을 기준으로 이동 조정

씬을 방문한 플레이어가 프레임 속도를 따라잡는 데 어려움을 겪고 있다고 가정해 봅시다. 그러면 모든 프레임의 시간 간격이 균일하지 않지만 각 프레임이 엔티티를 같은 양만큼 이동시키므로, 이동이 끊겨 보일 수 있습니다.

이러한 불균일한 타이밍은 다음을 사용하여 보정할 수 있습니다. `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()` 함수는 세 개의 매개변수를 받습니다:

* 원점 위치를 위한 벡터
* 목표 위치를 위한 벡터
* amount: 0에서 1 사이의 값으로, 이동할 비율을 나타냅니다.

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

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

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

예를 들어, 원점 벡터가 *(0, 0, 0)* 이고 목표 벡터가 *(10, 0, 10)*:

* amount를 0으로 사용하면 *(0, 0, 0)*
* amount를 0.3으로 사용하면 *(3, 0, 3)*
* amount를 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\&token=648743af-b3c3-4503-9c9c-c81670b839fe)

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

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

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

* 이 [사원수](https://en.wikipedia.org/wiki/Quaternion) 원점 회전을 위한 각도
* 이 [사원수](https://en.wikipedia.org/wiki/Quaternion) 목표 회전을 위한 각도
* amount: 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\&token=cfab88d5-ac0d-4cc1-aa75-c866ec8a8a80)

이보다 더 간단하지만 덜 효율적인 접근 방식은 다음을 활용합니다. `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의 (선형 보간) 알고리즘의 `Scalar` 객체에 사용합니다.

반대로 축별 비율을 다르게 변경하려면 `Vector3` 원점 스케일과 목표 스케일을 나타내는 데 사용한 다음, 다음의 *lerp* 함수를 `Vector3`.

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

* 원점 스케일을 위한 숫자
* 목표 스케일을 위한 숫자
* amount: 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\&token=094e5b91-4fab-4223-941c-c2107ad96c5d)

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

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 예시와 같지만, `fraction` 필드를 매 틱마다 비선형 값에 매핑합니다. 이 비선형 값은 `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\&token=3ba54949-0936-4dc5-a55d-9feebbbff2ca)

### 시스템을 통해 경로 따라가기

엔티티가 벡터 배열을 반복하면서 각 지점 사이를 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* 예시에는 `path` 배열과, 경로의 모든 지점을 담고 있는 배열, 그리고 `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\&token=ab11f133-f26e-40a6-b7a9-740c70c05cd7)

## 텍스처 트윈

텍스처를 부드럽게 슬라이드시키려면 다음을 사용하세요. `트윈` 컴포넌트를 `setTextureMove` 함수.

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

텍스처 트윈은 다음 정보를 받습니다:

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

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

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

## 상수 텍스처 이동

텍스처를 계속해서 슬라이드시키려면 다음을 사용하세요. `트윈` 컴포넌트를 `setTextureMoveContinuous` 함수.

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

연속 텍스처 트윈은 다음 정보를 받습니다:

* `엔티티`: 텍스처를 이동시킬 엔티티
* `방향`: 이동을 위한 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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
