> 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/3d-model-animations.md).

# 3D 모델 애니메이션

씬에서 3D 모델을 애니메이션하는 방법

3D 모델의 *.glTF* 및 *.glb* 형식에는 원하는 만큼 많은 애니메이션을 포함할 수 있습니다. 애니메이션은 메시에게 어떻게 움직일지 알려 주며, 일련의 *키프레임* 를 시간에 따라 배치합니다. 그러면 메시가 한 포즈에서 다른 포즈로 블렌딩되어 연속적인 움직임을 시뮬레이션합니다.

대부분의 3D 모델 애니메이션은 [*스켈레탈 애니메이션*](https://en.wikipedia.org/wiki/Skeletal_animation). 이러한 애니메이션은 모델의 복잡한 기하를 "막대인형"으로 단순화하고, 메시의 모든 정점을 가장 가까운 *본* 의 *스켈레톤*. 모델러는 스켈레톤을 다양한 포즈로 조정하고, 메시가 이러한 움직임을 따라 늘어나고 구부러집니다.

대안으로, *버텍스 애니메이션* 은 스켈레톤 없이도 모델을 애니메이션할 수 있습니다. 이러한 애니메이션은 모델의 각 정점 위치를 직접 지정합니다. Decentraland도 이러한 애니메이션을 지원합니다.

참고 [애니메이션](https://github.com/decentraland/docs-creator/blob/main/creator/3d-modeling/animations/README.md) 3D 모델용 애니메이션을 만드는 방법에 대한 자세한 내용은 다음을 읽어 보세요. [Shape 구성 요소](/creator/content-creator-ko/sdk7/3d/shape-components.md) 3D 모델을 씬으로 가져오는 방법은 다음 지침을 참조하세요.

{% hint style="info" %}
**💡 팁**: 애니메이션은 일반적으로 엔티티의 위치를 바꾸는 것보다 제자리에서 무언가를 움직이는 데 더 적합합니다. 예를 들어 캐릭터의 발을 제자리에서 움직이도록 애니메이션을 설정할 수 있지만, 엔티티의 위치를 바꾸려면 Transform 컴포넌트를 사용하는 것이 가장 좋습니다. 다음을 참조하세요. [엔티티 배치](/creator/content-creator-ko/sdk7/3d/move-entities.md) 자세한 내용은 다음을 참조하세요.
{% endhint %}

## 3D 모델에 애니메이션이 있는지 확인하기

모든 *glTF* 파일에 애니메이션이 포함되어 있는 것은 아닙니다. 사용 가능한 것이 있는지 확인하려면 다음을 수행할 수 있습니다:

* 사용하는 경우 [VS Code](https://code.visualstudio.com/)(권장), 다음을 설치하세요. *GLTF Tools* 확장 프로그램을 설치하고 그곳에서 glTF 파일의 내용을 확인하세요.
* 다음을 여세요 [Babylon Sandbox](https://sandbox.babylonjs.com/) 사이트를 열고 glTF 파일(및 모든 *.jpg* 또는 *.bin* 의존 파일)을 브라우저로 드래그하세요.
* 다음을 여세요 *.glTF* 파일을 텍스트 편집기로 열고 아래로 스크롤하여 다음을 찾을 때까지 이동하세요. *"animations":*.

{% hint style="info" %}
**💡 팁**: *스켈레탈* 애니메이션에서는 애니메이션 이름이 종종 아마추어 이름, 밑줄, 그리고 애니메이션 이름으로 구성됩니다. 예를 들면 `myArmature_animation1`.
{% endhint %}

## 자동 재생

3D 모델에 애니메이션이 포함되어 있으면 기본 동작은 그중 첫 번째가 항상 반복 재생되는 것입니다.

이 동작을 피하려면 다음을 추가하세요. `애니메이터` 모델이 있는 엔티티에 컴포넌트를 추가한 다음, 애니메이션 재생을 명시적으로 처리하세요. 만약 `애니메이터` 컴포넌트가 엔티티에 존재하면 모든 애니메이션의 기본값은 `playing: false` 상태가 되며, 수동으로 재생해야 합니다.

{% hint style="info" %}
**💡 팁**: In the [씬 편집기](/creator/content-creator-ko/scene-editor/get-started/about-editor.md)에서는 다음을 추가할 수 있습니다. **애니메이터** 컴포넌트를 시각적으로. 다음을 참조하세요. [컴포넌트 추가](/creator/content-creator-ko/scene-editor/build/components.md#add-components). 또한 코드 없이 다음을 통해 애니메이션을 제어할 수도 있습니다. **동작**, 자세한 내용은 [어떤 항목이든 스마트 아이템으로 만들기](/creator/content-creator-ko/scene-editor/interactivity/make-any-item-smart.md).
{% endhint %}

## 애니메이션을 명시적으로 처리하기

다음은 `애니메이터` 컴포넌트는 엔티티의 모든 애니메이션에 접근하는 데 사용되며, 엔티티에게 애니메이션을 재생하거나 중지하라고 명시적으로 지시하는 데 사용할 수 있습니다.  `애니메이터` 컴포넌트에는 다음 배열이 포함되어 있습니다. `상태`, 이 목록에는 3D 모델이 수행할 수 있는 각 애니메이션마다 하나의 객체가 포함되어야 합니다. 하나의 `애니메이터` 에는 필요한 만큼 많은 상태를 포함할 수 있습니다.

```ts
// 엔티티 생성
const shark = engine.addEntity()

// 여기에 3D 모델 추가
GltfContainer.create(shark, {
	src: 'models/shark.glb',
})

Animator.create(shark, {
	states: [
		{
			clip: 'swim',
			playing: true,
			loop: true,
		},
	],
})
```

각 `state` 객체는 애니메이션이 현재 재생 중인지 추적합니다.

{% hint style="warning" %}
**📔 참고**:  `애니메이터` 컴포넌트는 다음을 통해 가져와야 합니다:

> `import { Animator } from "@dcl/sdk/ecs"`

참고 [가져오기](/creator/content-creator-ko/sdk7/getting-started/coding-scenes.md#imports) 를 사용하면 이를 쉽게 처리할 수 있습니다.
{% endhint %}

## 애니메이션 가져오기

에서 클립을 가져오세요. `애니메이터` 이름으로 다음을 사용하여 `.Animator.getClip()` 함수. 이 함수는 애니메이션 상태 객체의 수정 가능한 버전을 반환합니다.

```ts
const swimAnim = Animator.getClip(sharkEntity, 'swim')
```

`Animator.getClip` 다음 매개변수를 필요로 합니다:

* `entity`: 다음의 엔티티 `애니메이터` 컴포넌트, 조회하려는 대상입니다.
* `clipName`: 가져오려는 클립의 이름을 나타내는 문자열입니다.

`Animator.getClip` 애니메이션 상태의 수정 가능한 버전을 가져오므로, 이 함수가 반환하는 값은 자유롭게 수정할 수 있습니다.

```ts
const swimAnim = Animator.getClip(sharkEntity, 'swim')
swimAnim.loop = false
```

{% hint style="warning" %}
**📔 참고**: 만약 다음을 사용하려고 하면 `Animator.getClip()` 로 다음에 나열되지 않은 클립을 가져오면 `애니메이터` 컴포넌트에 없으면 오류가 발생합니다. 다음을 사용하세요. `Animator.getClipOrNull()` 원하는 경우 `null` 오류 대신 해당 경우에 대한 반환값을 받으려면.
{% endhint %}

## 애니메이션 재생하기

그 `.playing` 애니메이션 상태의 필드는 애니메이션이 현재 재생 중인지 결정합니다. 하나의 3D 모델에서 여러 애니메이션이 동시에 재생될 수 있다는 점에 유의하세요.

사용하세요 `Animator.playSingleAnimation()` 함수를 다음에 대해 `AnimationState` 객체.

```ts
Animator.playSingleAnimation(sharkEntity, 'swim')
```

엔티티가 다른 애니메이션을 재생 중이었다면, `Animator.playSingleAnimation` 이들을 중지합니다.

`Animator.playSingleAnimation` 다음 매개변수를 필요로 합니다:

* `entity`: 다음의 엔티티 `애니메이터` 영향을 주고자 하는 컴포넌트.
* `clipName`: 재생하려는 클립의 이름을 나타내는 문자열입니다.
* `resetCursor`: *(선택 사항)* 만약 *true*, 애니메이션이 이전에 일시 중지되었더라도 처음부터 재생합니다. 만약 *false*, 일시 중지된 지점부터 계속 재생합니다. 기본값: *true*.

```ts
Animator.playSingleAnimation(sharkEntity, 'swim', false)
```

다음 표는 다음의 동작을 요약합니다. `Animator.playSingleAnimation()` 가 다음 값들을 사용했을 때 어떻게 동작하는지 `resetCursor` 속성:

|                | `resetCursor` = *false*   | `resetCursor` = *true* (기본값) |
| -------------- | ------------------------- | ---------------------------- |
| **현재 재생 중**    | 영향 없음.                    | 처음부터 재생합니다.                  |
| **일시 중지됨**     | 마지막으로 재생된 프레임부터 다시 시작합니다. | 처음부터 재생합니다.                  |
| **완료됨(반복 없음)** | 처음부터 재생합니다.               | 처음부터 재생합니다.                  |

## 반복 애니메이션

기본적으로 애니메이션은 무한히 반복되는 루프로 재생됩니다.

다음 설정을 변경하여 이를 바꾸세요. `loop` 속성 `state` 객체.

```ts
Animator.create(shark, {
	states: [
		{
			clip: 'bite',
			playing: true,
			loop: false,
		},
	],
})
```

만약 `loop` 이 *false*, 애니메이션은 한 번만 재생된 뒤 멈추며 마지막 프레임의 자세를 유지합니다.

## 애니메이션 중지하기

엔티티가 재생 중인 모든 애니메이션을 중지하려면 다음을 사용하세요. `Animator.stopAllAnimations()`.

```ts
Animator.stopAllAnimations(shark)
```

`Animator.stopAllAnimations` 다음 매개변수를 필요로 합니다:

* `entity`: 다음의 엔티티 `애니메이터` 영향을 주고자 하는 컴포넌트.
* `resetCursor`: *(선택 사항)* 만약 *true*, 애니메이션의 첫 프레임 자세로 돌아갑니다. 만약 *false*, 현재 자세에서 일시 중지된 상태로 유지됩니다. 기본값: *true*.

{% hint style="warning" %}
**📔 참고**: 다음과 함께 애니메이션을 재생할 때 `Animator.playSingleAnimation`, 이 함수가 뒤에서 다른 모든 애니메이션의 중지를 처리합니다. 그 경우 다른 애니메이션을 명시적으로 중지할 필요가 없습니다.
{% endhint %}

반복하지 않는 애니메이션의 재생이 끝나면 기본적으로 3D 모델은 마지막 자세를 유지합니다.  `shouldReset` 속성은 중지된 애니메이션이 다시 재생될 때 어떤 일이 일어날지 제어합니다. 만약 *true*, 애니메이션은 초기 상태(음수 `속도`)로 재생하는 경우에는 마지막 프레임)로 복원되며, 중지 상태에서 재생 상태로 바뀔 때마다 그렇게 됩니다. 만약 *false* (기본값), 이전 중지 지점부터 다시 재생됩니다.

```ts
Animator.create(shark, {
	states: [
		{
			clip: 'bite',
			playing: true,
			shouldReset: true,
			loop: true,
		},
	],
})
```

다음을 사용해 `Animator.stopAllAnimations()` 언제든지 자세를 애니메이션의 첫 프레임으로 명시적으로 되돌릴 수도 있습니다.

{% hint style="warning" %}
**📔 참고**: 자세를 초기화하는 것은 갑작스러운 변화입니다. 모델이 다른 자세로 부드럽게 전환되도록 하려면 다른 애니메이션을 재생하고, 두 애니메이션의 `가중치` 속성을 점진적으로 조정하여 두 애니메이션 사이를 블렌딩하세요. 다음을 참조하세요. [애니메이션 가중치](#animation-weight).
{% endhint %}

## 애니메이션이 끝났는지 감지하기

반복하지 않는 애니메이션의 재생이 끝나면 엔진은 해당 애니메이션 상태의 `playing` 속성을 다시 *false*. 씬의 코드는 이 값을 읽어 애니메이션이 언제 끝났는지 알 수 있으며, 예를 들어 바로 다음 애니메이션을 연결할 수 있습니다.

```ts
let wasPlaying = false

engine.addSystem(() => {
	const animator = Animator.get(shark)
	const biteState = animator.states.find((state) => state.clip === 'bite')
	const isPlaying = biteState?.playing ?? false

	if (wasPlaying && !isPlaying) {
		console.log('bite animation finished')
		// 다음 애니메이션을 연결
		Animator.playSingleAnimation(shark, 'swim')
	}

	wasPlaying = isPlaying
})
```

{% hint style="warning" %}
**📔 참고**: 애니메이션 상태를 매 프레임 폴링할 때는 항상 다음을 통해 읽으세요. `Animator.get()` (읽기 전용). 다음을 사용하지 마세요. `Animator.getClip()` 또는 `Animator.getMutable()` 폴링용으로는 사용하지 마세요. 이는 컴포넌트의 수정 가능한 버전을 반환하므로 매 프레임 변경된 것으로 표시되어 불필요한 동기화 작업을 발생시킵니다.

그 `playing` 속성은 애니메이션이 스스로 끝날 때에만 엔진에 의해 전환됩니다. 반복 애니메이션은 중지될 때까지 재생되므로 스스로 이 속성을 전환하지 않으며, `속도` 0으로 설정된 애니메이션은 절대 끝나지 않습니다.
{% endhint %}

{% hint style="warning" %}
**📔 참고**: 이 기능은 데스크톱 클라이언트에서만 지원됩니다.
{% endhint %}

## 여러 애니메이션 처리하기

3D 모델에 여러 애니메이션이 포함되어 있다면 하나의 `애니메이터` 컴포넌트가 모두 처리할 수 있습니다.

```ts
// 엔티티 생성
const shark = engine.addEntity()

// 여기에 3D 모델 추가
GltfContainer.create(shark, {
	src: 'models/shark.glb'
})

Animator.create(shark, {
	states:[{
			clip: "swim",
			playing: true,
			loop: true
		}, {
			clip: "bite",
			playing: true,
			loop: true
		}
	]
})
```

위 예에서는 두 애니메이션이 별도의 `state` 객체로 처리된 뒤 둘 다 동일한 `애니메이터` 컴포넌트를 부여해야 합니다.

애니메이션의 각 본은 한 번에 하나의 애니메이션만 영향을 줄 수 있습니다. 단, 이러한 애니메이션에 `가중치` 을 더한 값이 1 이하가 되는 경우는 예외입니다.

한 애니메이션이 캐릭터의 다리에만 영향을 주고 다른 애니메이션이 캐릭터의 머리에만 영향을 준다면, 문제없이 동시에 재생할 수 있습니다. 하지만 둘 다 캐릭터의 다리에 영향을 준다면, 한 번에 하나만 재생하거나 더 낮은 `가중치` 값.

위 예에서 `bite` 애니메이션이 상어의 입에만 영향을 주고, `swim` 애니메이션이 상어 척추의 본에만 영향을 준다면, 두 애니메이션은 동시에 재생할 수 있습니다.

{% hint style="warning" %}
**📔 참고**: `Animator.playSingleAnimation()` 엔티티가 현재 재생 중인 다른 모든 애니메이션을 중지합니다. 여러 애니메이션을 동시에 재생하려면 애니메이션 상태의 `playing` 속성을 수동으로 수정하세요.
{% endhint %}

## 애니메이션 속도

다음을 변경하여 애니메이션 재생 속도를 바꾸세요. `속도` 속성입니다. 속성 값은 기본적으로 1입니다.

```ts
Animator.create(shark, {
	states: [
		{
			clip: 'swim',
			playing: true,
			loop: true,
			speed: 2,
		},
	],
})
```

1보다 낮게 설정하면 더 느리게 재생되며, 예를 들어 0.5로 설정하면 절반 속도로 재생됩니다. 1보다 높게 설정하면 더 빠르게 재생되며, 예를 들어 2로 설정하면 두 배 속도로 재생됩니다.

```ts
const swimAnim = Animator.getClip(sharkEntity, 'swim')

swimAnim.speed = 0.5
```

## 애니메이션 가중치

그 `가중치` 속성은 하나의 모델이 여러 애니메이션을 동시에 수행할 수 있게 하며, 애니메이션에 포함된 모든 움직임의 가중 평균을 계산합니다.  `가중치` 은 그 애니메이션이 평균에서 얼마나 큰 비중을 차지할지를 결정합니다.

기본적으로, `가중치` 은 다음과 같습니다. *1*. `가중치` 는 다음보다 클 수 없습니다. *1*.

```ts
Animator.create(shark, {
	states: [
		{
			clip: 'swim',
			playing: true,
			loop: true,
			weight: 0.2,
		},
	],
})
```

그 `가중치` 엔티티 내 활성 애니메이션들의 값은 항상 합계가 1이 되어야 합니다. 합계가 1보다 작으면 가중 평균 계산의 남은 부분에는 아마추어의 기본 위치가 사용됩니다.

예를 들어 위 코드 예제에서는 다음을 재생하고 있습니다. *swim* 애니메이션이며, 이는 다음의 `가중치` 의 *0.2*. 이 수영 동작은 꽤 미묘하게 나타납니다. 애니메이션이 정의한 강도의 20%만 적용되기 때문입니다. 나머지 80%의 계산은 아마추어의 기본 자세 값을 사용합니다.

그 `가중치` 속성은 흥미로운 방식으로 사용할 수 있습니다. 예를 들어 `가중치` 속성은 *swim* 는 상어가 얼마나 빠르게 헤엄치는지에 비례해 설정할 수 있으므로, 빠른 수영과 느린 수영을 위한 여러 애니메이션을 만들 필요가 없습니다.

또한 다음을 변경하여 `가중치` 값을 점진적으로 바꿔 더 자연스러운 전환을 만들고, 기본 포즈에서 애니메이션의 첫 포즈로 갑자기 점프하는 것을 피할 수도 있습니다.

{% hint style="warning" %}
**📔 참고**: 추가된 `가중치` 3D 모델의 본에 작용하는 모든 애니메이션의 값은 1을 초과할 수 없습니다. 동시에 하나 이상의 애니메이션이 같은 본에 영향을 준다면, 가중치의 합이 1보다 작아야 합니다.
{% endhint %}

```ts
const swimAnim = Animator.getClip(sharkEntity, 'swim')

swimAnim.weight = 0.5
```


---

# 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/3d-model-animations.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.
