> 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-es/scenes-sdk7/fundamentos-del-contenido-3d/3d-model-animations.md).

# Animaciones de modelos 3D

Cómo animar modelos 3D en tu escena

Los modelos 3D en *.glTF* y *.glb* formatos pueden incluir tantas animations como quieras en ellos. Las animations le dicen al mesh cómo moverse, especificando una serie de *keyframes* que se distribuyen a lo largo del tiempo; luego el mesh pasa de una pose a otra para simular un movimiento continuo.

La mayoría de las animations de modelos 3D son [*skeletal animations*](https://en.wikipedia.org/wiki/Skeletal_animation). Estas animations simplifican la geometría compleja del modelo en una "stick figure", conectando cada vertex del mesh al *bone* más cercano en el *skeleton*. Los modelers ajustan el skeleton en diferentes poses, y el mesh se estira y se dobla para seguir estos movimientos.

Como alternativa, *vertex animations* animan un modelo sin necesidad de un skeleton. Estas animations especifican directamente la posición de cada vertex en el modelo. Decentraland también admite estas animations.

Consulta [Animations](https://github.com/decentraland/docs/blob/main/creator/3d-modeling/animations.md) para obtener detalles sobre cómo crear animations para un modelo 3D. Lee [Shape components](/creator/content-creator-es/scenes-sdk7/fundamentos-del-contenido-3d/shape-components.md) para obtener instrucciones sobre cómo importar un modelo 3D a una scene.

{% hint style="info" %}
**💡 Consejo**: Por lo general, las animations son mejores para mover algo en su lugar, no para cambiar la posición de una entity. Por ejemplo, puedes establecer una animation para mover los pies de un personaje en su lugar, pero para cambiar la ubicación de la entity es mejor usar el componente Transform. Consulta [Positioning entities](/creator/content-creator-es/scenes-sdk7/fundamentos-del-contenido-3d/move-entities.md) para obtener más detalles.
{% endhint %}

## Comprobar si un modelo 3D tiene animations

No todos los archivos *glTF* incluyen animations. Para ver si hay alguna disponible, puedes hacer lo siguiente:

* Si usas [VS Code](https://code.visualstudio.com/)(recomendado), instala la extensión *GLTF Tools* y visualiza allí el contenido de un archivo glTF.
* Abre el sitio [Babylon Sandbox](https://sandbox.babylonjs.com/) y arrastra el archivo glTF (y cualquier *.jpg* o *.bin* dependencias) al navegador.
* Abre el sitio *.glTF* archivo con un editor de texto y desplázate hacia abajo hasta que encuentres *"animations":*.

{% hint style="info" %}
**💡 Consejo**: En *skeletal* animations, el nombre de una animation suele estar compuesto por el nombre de su armature, un guion bajo y el nombre de su animation. Por ejemplo `myArmature_animation1`.
{% endhint %}

## Reproducción automática

Si un modelo 3D incluye alguna animations, el comportamiento predeterminado es que la primera de ellas siempre se reproduce en un loop.

Para evitar este comportamiento, añade un `Animator` component a la entity que tiene el modelo, y luego gestiona explícitamente la reproducción de las animations. Si un `Animator` component está presente en la entity, todas las animations pasan por defecto a un estado `playing: false` y deben reproducirse manualmente.

{% hint style="info" %}
**💡 Consejo**: En el [Scene Editor](/creator/content-creator-es/scene-editor/primeros-pasos/about-editor.md), puedes añadir un **Animator** component visualmente. Consulta [Add Components](/creator/content-creator-es/scene-editor/construir/components.md#add-components). También puedes controlar las animations de forma sin código mediante **Actions**, consulta [Make any item smart](/creator/content-creator-es/scene-editor/interactividad/make-any-item-smart.md).
{% endhint %}

## Gestionar animations explícitamente

Un `Animator` component se usa para acceder a todas las animations de la entity y puede usarse para indicar explícitamente a la entity que reproduzca o detenga una animation. El `Animator` component incluye un array de `states`, esta lista debe incluir un objeto por cada una de las animations que el modelo 3D puede realizar. Un solo `Animator` puede incluir tantos states como sean necesarios.

```ts
// Crear entity
const shark = engine.addEntity()

// Añadirle un modelo 3D
GltfContainer.create(shark, {
	src: 'models/shark.glb',
})

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

Cada `state` object registra si una animation se está reproduciendo actualmente.

{% hint style="warning" %}
**📔 Nota**: El `Animator` component debe importarse mediante

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

Consulta [Imports](/creator/content-creator-es/scenes-sdk7/primeros-pasos/coding-scenes.md#imports) para ver cómo gestionar esto fácilmente.
{% endhint %}

## Obtener una animation

Obtén un clip del `Animator` por nombre usando la función `.Animator.getClip()` . Esta función devuelve una versión mutable del objeto de state de la animation.

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

`Animator.getClip` requiere los siguientes parámetros:

* `entity`: La entity del `Animator` component que quieres consultar.
* `clipName`: Cadena con el nombre del clip que quieres obtener.

`Animator.getClip` obtiene una versión mutable del state de la animation, así que puedes modificar libremente los valores que devuelve esta función.

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

{% hint style="warning" %}
**📔 Nota**: Si intentas usar `Animator.getClip()` para obtener un clip que no aparece en el `Animator` component, se produce un error. Usa `Animator.getClipOrNull()` si prefieres obtener una `null` respuesta en ese caso, en lugar de un error.
{% endhint %}

## Reproducir una animation

El `.playing` campo de un state de animation determina si la animation se está reproduciendo actualmente. Ten en cuenta que varias animations pueden reproducirse en un solo modelo 3D al mismo tiempo.

Usa la función `Animator.playSingleAnimation()` en un objeto `AnimationState` .

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

Si la entity estaba reproduciendo otras animations, `Animator.playSingleAnimation` las detiene.

`Animator.playSingleAnimation` requiere los siguientes parámetros:

* `entity`: La entity del `Animator` component que quieres afectar.
* `clipName`: Cadena con el nombre del clip que quieres reproducir.
* `resetCursor`: *(opcional)* Si *true*, reproduce la animation desde el inicio, incluso si la animation estaba previamente en pausa. Si *false*, seguirá reproduciendo la animation desde donde se pausó. Predeterminado: *true*.

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

La siguiente tabla resume cómo `Animator.playSingleAnimation()` se comporta, usando diferentes valores para la `resetCursor` propiedad:

|                                 | `resetCursor` = *false*                       | `resetCursor` = *true* (predeterminado) |
| ------------------------------- | --------------------------------------------- | --------------------------------------- |
| **Reproduciéndose actualmente** | No tiene efecto.                              | Se reproduce desde el inicio.           |
| **Pausado**                     | Se reanuda desde el último frame reproducido. | Se reproduce desde el inicio.           |
| **Finalizado (sin loop)**       | Se reproduce desde el inicio.                 | Se reproduce desde el inicio.           |

## Animations en loop

De forma predeterminada, las animations se reproducen en un loop que repite la animation para siempre.

Cambia esta configuración ajustando la `loop` propiedad en el `state` .

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

Si `loop` se establece en *false*, la animation se reproduce solo una vez y luego se detiene, quedándose en la postura del último frame.

## Detener una animation

Para detener todas las animations que una entity esté reproduciendo, usa `Animator.stopAllAnimations()`.

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

`Animator.stopAllAnimations` requiere los siguientes parámetros:

* `entity`: La entity del `Animator` component que quieres afectar.
* `resetCursor`: *(opcional)* Si *true*, vuelve a la postura del primer frame de la animation. Si *false*, permanece en pausa en su postura actual. Predeterminado: *true*.

{% hint style="warning" %}
**📔 Nota**: Al reproducir una animation con `Animator.playSingleAnimation`, esta función se encarga de detener todas las demás animations en segundo plano. En ese caso, no necesitas detener explícitamente otras animations.
{% endhint %}

Cuando una animation termina de reproducir una animation sin loop, por defecto el modelo 3D permanece en la última postura que tenía. La propiedad `shouldReset` controla qué ocurre cuando una animation detenida se reproduce de nuevo: si *true*, la animation se restaura a su estado inicial (su primer frame, o su último frame si se reproduce con una `speed`) cada vez que pasa de detenida a reproducirse. Si *false* (el valor predeterminado), se reanuda desde donde estaba.

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

También puedes usar `Animator.stopAllAnimations()` en cualquier momento para volver a establecer explícitamente la postura en el primer frame de la animation.

{% hint style="warning" %}
**📔 Nota**: Restablecer la postura es un cambio brusco. Si quieres que el modelo transicione suavemente a otra postura, reproduce la otra animation y haz un blend entre ambas ajustando gradualmente sus propiedades de `weight` . Consulta [Animation weight](#animation-weight).
{% endhint %}

## Detectar cuándo termina una animation

Cuando una animation sin loop termina de reproducirse, el engine vuelve a establecer la propiedad `playing` de ese state de animation a *false*. El código de tu scene puede leer este valor para saber cuándo terminó la animation, por ejemplo para encadenar otra animation justo después.

```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')
		// encadenar la siguiente animation
		Animator.playSingleAnimation(shark, 'swim')
	}

	wasPlaying = isPlaying
})
```

{% hint style="warning" %}
**📔 Nota**: Al consultar el state de animation en cada frame, léelo siempre mediante `Animator.get()` (solo lectura). No uses `Animator.getClip()` o `Animator.getMutable()` para consultar: estas devuelven una versión mutable del component, lo que lo marca como cambiado en cada frame y provoca trabajo de sincronización innecesario.

El `playing` La propiedad solo cambia al estado opuesto por el engine cuando la animation termina por sí sola. Las animations en loop se reproducen hasta que se detienen, así que nunca cambian la propiedad por sí mismas, y las animations con `speed` establecido en 0 nunca terminan.
{% endhint %}

{% hint style="warning" %}
**📔 Nota**Esta función solo es compatible con el cliente de escritorio.
{% endhint %}

## Gestionar múltiples animations

Si un modelo 3D tiene varias animations empaquetadas en él, un solo `Animator` component puede encargarse de todas ellas.

```ts
// Crear entity
const shark = engine.addEntity()

// Añadirle un modelo 3D
GltfContainer.create(shark, {
	src: 'models/shark.glb'
})

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

En el ejemplo anterior, dos animations se gestionan mediante `state` objetos separados, y luego ambos se asignan al mismo `Animator` component.

Cada bone de una animation solo puede verse afectado por una animation a la vez, a menos que estas animations tengan un `weight` que sume un valor de 1 o menos.

Si una animation solo afecta a las piernas de un personaje, y otra solo afecta a la cabeza del personaje, entonces pueden reproducirse al mismo tiempo sin problema. Pero si ambas afectan a las piernas del personaje, entonces debes reproducir solo una a la vez, o reproducirlas con valores de `weight` menor peso.

Si en el ejemplo anterior, la animation `bite` solo afecta a la boca del shark, y la animation `swim` solo afecta a los bones de la columna del shark, entonces ambas pueden reproducirse al mismo tiempo.

{% hint style="warning" %}
**📔 Nota**: `Animator.playSingleAnimation()` detiene todas las demás animations que la entity está reproduciendo actualmente. Para reproducir varias animations al mismo tiempo, modifica la `playing` propiedad en los animation states manualmente.
{% endhint %}

## Velocidad de la animation

Cambia la velocidad a la que se reproduce una animation cambiando la `speed` propiedad. El valor de la speed es 1 por defecto.

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

Establece la speed por debajo de 1 para reproducirla más despacio, por ejemplo a 0.5 para reproducirla a media speed. Establécela por encima de 1 para reproducirla más rápido, por ejemplo a 2 para reproducirla al doble de speed.

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

swimAnim.speed = 0.5
```

## Animation weight

El `weight` La propiedad weight permite que un solo model lleve a cabo varias animations a la vez, calculando un promedio ponderado de todos los movimientos involucrados en la animation. El valor de `weight` determina cuánta importancia se le dará a esa animation en el promedio.

De forma predeterminada, `weight` es igual a *1*. El valor de `weight` no puede ser mayor que *1*.

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

El `weight` valor de todas las animations activas en una entity debe sumar 1 en todo momento. Si suma menos de 1, el promedio ponderado usará la posición predeterminada del armature para la parte restante del cálculo.

Por ejemplo, en el ejemplo de código anterior, estamos reproduciendo la animation, que solo tiene un *swim* weight `weight` de *0.2*. Este movimiento de natación será bastante sutil: solo el 20 % de la intensidad que define la animation. El 80 % restante del cálculo toma valores de la postura predeterminada del armature.

El `weight` La propiedad weight puede usarse de formas interesantes; por ejemplo, la `weight` propiedad weight de *swim* podría establecerse en proporción a la rapidez con la que nada el shark, así no necesitas crear varias animations para nado rápido y lento.

También podrías cambiar el `weight` valor gradualmente al iniciar y detener una animation para darle una transición más natural y evitar saltos desde la pose predeterminada hasta la primera pose de la animation.

{% hint style="warning" %}
**📔 Nota**: El `weight` valor añadido de todas las animations que actúan sobre un bone de un modelo 3D no puede ser mayor que 1. Si más de una animation afecta a los mismos bones al mismo tiempo, deben tener su weight configurado con valores que sumen menos de 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-es/scenes-sdk7/fundamentos-del-contenido-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.
