> 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/blob/main/creator/3d-modeling/animations.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" %}
**💡 팁**:  *골격* 애니메이션에서 애니메이션 이름은 종종 armature 이름, 밑줄, 그리고 애니메이션 이름으로 구성됩니다. 예를 들어 `myArmature_animation1`.
{% endhint %}

## 자동 재생

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

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

{% hint style="info" %}
**💡 팁**:  [Scene Editor](/creator/content-creator-ko/scene-editor/get-started/about-editor.md), 여기에서 다음을 시각적으로 추가할 수 있습니다 **Animator** 컴포넌트. 다음을 참조하세요 [컴포넌트 추가](/creator/content-creator-ko/scene-editor/build/components.md#add-components). 또한 코드 없이 다음을 통해 애니메이션을 제어할 수 있습니다 **액션**, 다음을 참조하세요 [아무 항목이나 스마트하게 만들기](/creator/content-creator-ko/scene-editor/interactivity/make-any-item-smart.md).
{% endhint %}

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

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

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

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

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

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

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

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

참조 [가져오기](/creator/content-creator-ko/sdk7/getting-started/coding-scenes.md#imports) 에서 이를 쉽게 처리하는 방법을 확인하세요.
{% endhint %}

## 애니메이션 가져오기

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

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

`Animator.getClip` 에는 다음 매개변수가 필요합니다:

* `entity`: 다음의 엔티티 `Animator` 를 조회하고자 하는 컴포넌트입니다.
* `clipName`: 가져오려는 클립의 이름을 담은 문자열입니다.

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

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

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

## 애니메이션 재생

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

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

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

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

`Animator.playSingleAnimation` 에는 다음 매개변수가 필요합니다:

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

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

다음 표는 `Animator.playSingleAnimation()` 가 다양한 값에 따라 어떻게 동작하는지 요약합니다 `resetCursor` 속성:

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

## 반복 애니메이션

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

다음을 설정하여 이 설정을 변경하세요 `loop` 속성을 `상태` 객체에 적용하세요.

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

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

## 애니메이션 중지

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

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

`Animator.stopAllAnimations` 에는 다음 매개변수가 필요합니다:

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

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

반복되지 않는 애니메이션이 재생을 마치면, 기본적으로 3D 모델은 마지막 자세를 유지합니다.  `shouldReset` 속성은 중지된 애니메이션이 다시 재생될 때 어떤 일이 일어나는지 제어합니다: 만약 *true*애니메이션은 초기 상태(또는 음수 `speed`speed로 재생 중일 때는 마지막 프레임)로 복원됩니다. 만약 *false* (기본값)이라면, 이전 위치에서 재개됩니다.

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

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

{% hint style="warning" %}
**📔 참고**: 자세를 재설정하는 것은 급격한 변화입니다. 모델이 다른 자세로 부드럽게 전환되게 하려면 다른 애니메이션을 재생하고, 두 애니메이션의 `weight` 속성을 점진적으로 조정하여 두 애니메이션을 블렌딩하세요. 다음을 참조하세요. [애니메이션 가중치](#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` 속성은 애니메이션이 스스로 끝날 때만 엔진에 의해 변경됩니다. 반복 애니메이션은 중지될 때까지 재생되므로 스스로 이 속성을 바꾸지 않으며, 다음을 가진 애니메이션은 `speed` 0으로 설정되면 절대 끝나지 않습니다.
{% endhint %}

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

## 여러 애니메이션 처리

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

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

위 예시에서는 두 애니메이션이 각각 별도의 `상태` 객체로 처리된 다음, 둘 다 같은 `Animator` 컴포넌트에 할당됩니다.

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

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

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

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

## 애니메이션 속도

다음을 변경하여 애니메이션 재생 속도를 조절하세요 `speed` 속성입니다. speed의 기본값은 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
```

## 애니메이션 가중치

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

기본적으로 `weight` 은 다음과 같습니다 *1*.  `weight` 의 값은 *1*.

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

애니메이션의 `weight` 엔티티에서 활성화된 모든 애니메이션의 값의 합은 항상 1이어야 합니다. 합이 1보다 작으면 가중 평균 계산의 나머지 부분에는 armature의 기본 위치가 사용됩니다.

예를 들어, 위 코드 예시에서는 다음 애니메이션을 재생하고 있습니다 *swim* 애니메이션은 단 하나의 `weight` 의 *0.2*. 이 수영 동작은 꽤 미묘하게 나타납니다. 애니메이션이 정의한 강도의 20%만 적용됩니다. 나머지 80%의 계산은 armature의 기본 자세 값을 사용합니다.

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

또한 다음을 변경할 수도 있습니다 `weight` 값을 애니메이션 시작과 종료 시 점진적으로 바꾸어 더 자연스러운 전환을 주고, 기본 자세에서 애니메이션의 첫 자세로 갑자기 튀는 현상을 피할 수 있습니다.

{% hint style="warning" %}
**📔 참고**: 추가된 `weight` 3D 모델의 뼈에 영향을 주는 모든 애니메이션의 값의 합은 1을 초과할 수 없습니다. 여러 애니메이션이 동시에 같은 뼈에 영향을 주는 경우, weight 값들의 합이 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 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/3d-model-animations.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.
