Ir al contenido
charmotion

Máquina de estados de Rive: inputs y triggers

Por el equipo de Charmotion · Actualizado el

Ilustración de portada de la guía Máquina de estados de Rive: inputs, triggers y estados de ánimo, con personajes de demostración de Charmotion

Una máquina de estados (state machine) de Rive es un grafo dentro de un archivo .riv que decide qué animación se reproduce y cuándo. Los estados contienen animaciones, las transiciones los conectan y las condiciones de las transiciones leen valores que define tu código. Rive ahora recomienda Data Binding (view models) para esos valores; los inputs antiguos de tipo number, boolean y trigger siguen funcionando.

Puntos clave

  • Los estados contienen animaciones; las transiciones con condiciones deciden cuándo pasar de uno a otro.
  • Los inputs son de tres tipos: number, boolean y trigger.
  • Usa number o boolean para loops persistentes y triggers para reacciones puntuales.
  • Los inputs están obsoletos en favor de Data Binding, pero los archivos existentes siguen funcionando.

¿Qué es una máquina de estados (state machine) de Rive?

Una máquina de estados es la capa de lógica de un archivo de Rive. Se ubica sobre las animaciones de la línea de tiempo y responde una pregunta en cada fotograma: ¿qué debería reproducirse ahora?

  • Los estados suelen contener una sola animación, como un loop en reposo (idle), o una mezcla de varias.
  • Las transiciones son flechas entre estados. Cada una puede tener una duración, lo que hace que Rive funda la animación saliente con la entrante en lugar de cortar.
  • Las condiciones de las transiciones leen valores desde fuera de la animación, como "mood es igual a 5" o "se disparó success".
  • Estados especiales: Entry se ejecuta primero, Any State permite que una transición se dispare desde cualquier estado en el que esté la máquina en ese momento, y Exit termina una capa.
  • Las capas (layers) permiten ejecutar varios grafos de estados a la vez, por ejemplo, el movimiento del cuerpo en una capa y el parpadeo en otra.

Tu app nunca reproduce animaciones por nombre. Cambia valores, y la máquina de estados reacciona. Así la lógica de animación se queda dentro del archivo, donde el diseñador puede ajustarla sin cambiar el código.

Las máquinas de estados también pueden estabilizarse (settle): cuando el runtime detecta que nada va a cambiar, deja de avanzar, lo que ahorra trabajo en los estados finales estáticos.

¿Qué son los inputs de tipo number, boolean y trigger?

Los inputs son la forma original en que el código se comunica con una máquina de estados. Hay tres tipos:

TipoContieneUso habitual
NumberUn número decimal (float) que asignas y leesElegir entre muchos estados, niveles, progreso
Booleantrue o falseUn estado de encendido/apagado, como hovered o muted
TriggerUn evento momentáneo que disparasReacciones puntuales: success, error, tap

Trigger vs boolean es la duda más común. Un boolean sigue en true hasta que lo pones en false, así que sirve para condiciones que duran. Un trigger lo consume la transición que lo lee, así que se dispara una vez y se restablece solo. Usa un trigger cuando, de otro modo, pondrías un boolean en true y enseguida otra vez en false.

Un input de tipo number es la forma más limpia de elegir entre muchos loops mutuamente excluyentes. Un solo número mood con valores de 0 a 9 reemplaza diez booleans que tendrías que mantener coherentes por tu cuenta.

En la web, la API heredada devuelve los inputs desde stateMachineInputs(). Los triggers tienen fire(); los numbers y booleans tienen una propiedad value:

import { Rive } from "@rive-app/webgl2";

const r = new Rive({
  src: "/character.riv",
  canvas: document.querySelector("canvas"),
  stateMachine: "CharacterStateMachine",
  autoplay: true,
  onLoad: () => {
    r.resizeDrawingSurfaceToCanvas();
    const inputs = r.stateMachineInputs("CharacterStateMachine");
    const mood = inputs.find((i) => i.name === "mood");
    const success = inputs.find((i) => i.name === "success");
    mood.value = 5;   // loading loop
    success.fire();   // one-shot reaction
  },
});

Consulta los inputs dentro de onLoad; antes de que cargue el archivo, no existen. Fíjate en la opción en singular stateMachine, que reemplazó a stateMachines en la versión 2.41.0 del runtime web.

¿En qué se diferencian los estados en loop de los triggers puntuales?

La mayoría de los personajes interactivos necesitan dos tipos de movimiento:

  • Los loops representan una condición continua: idle, loading, listening, sleeping. Deben reproducirse mientras la condición sea verdadera, así que se seleccionan con un valor persistente (un number o un boolean).
  • Las reacciones puntuales (one-shots) representan un evento: un pago se completó, una carga falló, el usuario dijo que sí. Se reproducen una vez y luego deben volver al loop que esté activo, así que se disparan con triggers.

La conexión habitual es una transición desde Any State hacia cada reacción puntual, condicionada por su trigger, y una transición de salida al final de la reacción que vuelve al loop correspondiente al valor numérico actual. Hecho así, tu código puede fijar el loop y disparar reacciones de forma independiente, sin llevar la cuenta de lo que se está reproduciendo.

Seis reacciones puntuales: éxito, error, sí, no, entrada y salida
Las reacciones puntuales se disparan con triggers y vuelven al estado de ánimo actual al terminar.

En la práctica importan dos detalles. Primero, dale a las transiciones entre loops una duración de mezcla corta para que los cambios de estado de ánimo se vean suaves en lugar de saltar. Segundo, decide si un cambio de loop debe interrumpir una reacción puntual. Normalmente no: si una solicitud termina y disparas success y vuelves a poner el estado de ánimo en idle en el mismo fotograma, igual quieres que la reacción de éxito se reproduzca completa.

¿Los inputs de la máquina de estados de Rive están obsoletos?

Sí. Rive marcó como obsoletos los inputs de las máquinas de estados, y la escucha de eventos en runtime, en favor de Data Binding. Su documentación de runtimes ahora abre las páginas de inputs con un aviso de obsolescencia: usa Data Binding en proyectos nuevos y planifica la migración de los existentes.

Obsoleto no significa roto. La guía de migración de Rive indica que no necesitas actualizar los archivos existentes y que los inputs y eventos seguirán funcionando como se espera. Sin embargo, el soporte varía según el runtime:

  • Web, React, Android y Flutter: las API de inputs siguen funcionando.
  • Apple: los inputs solo funcionan en el runtime heredado de Apple. El nuevo runtime de Apple solo admite Data Binding.
  • React Native: el runtime Nitro muestra advertencias para los métodos de inputs en la v0.5 y los elimina en la v0.6.

Para migrar un archivo, ábrelo en el editor de Rive, elige Convert Inputs to View Models en el menú principal (el ícono de tres líneas) y luego actualiza tu código para asignar valores en el view model en lugar de en los inputs.

¿Qué es Data Binding en Rive?

Data Binding conecta un view model, un conjunto tipado de propiedades definido en el editor, con cualquier elemento del archivo: condiciones de la máquina de estados, texto, colores, posiciones. Tu código obtiene una instancia del view model y asigna sus propiedades.

En comparación con los inputs, las propiedades de un view model admiten más tipos. Los inputs se limitan a numbers, booleans y triggers; los view models suman strings, enums, colores, view models anidados, listas, imágenes y artboards. Las propiedades también se pueden observar, así que la animación puede devolver valores a tu código.

En la web, el equivalente del ejemplo con inputs se ve así:

const r = new Rive({
  src: "/character.riv",
  canvas: document.querySelector("canvas"),
  stateMachine: "CharacterStateMachine",
  autoplay: true,
  autoBind: true,
  onLoad: () => {
    const vmi = r.viewModelInstance;
    vmi.number("mood").value = 5;
    vmi.trigger("success").trigger();
    vmi.number("mood").on((event) => console.log(event));
  },
});

Con autoBind: true, el runtime crea y vincula por ti la instancia predeterminada del view model. Los nombres de propiedades de este ejemplo suponen un archivo cuyo view model tiene las propiedades mood y success. Un archivo que solo tiene inputs no tiene view model, así que este código no encontraría nada; para esos archivos, usa la API de inputs.

¿Cómo se corresponde el input mood de Charmotion con los estados?

Los archivos de Charmotion siguen el patrón de loop más trigger descrito arriba, con un input de tipo number y seis triggers en una máquina de estados llamada CharacterStateMachine.

moodLoopmoodLoop
0idle5loading
1happy6listening
2wave7talking
3thinking8sleeping
4sad9attention

Los cambios de estado de ánimo se mezclan en 220 ms. Los triggers success, error, yes, no, enter y exit reproducen cada uno una reacción puntual y luego vuelven al estado de ánimo que esté fijado. exit deja al personaje oculto hasta que se dispara enter. Los cambios de estado de ánimo nunca cortan una reacción puntual. Una segunda máquina, LaunchStateMachine, reproduce una intro de inicio una vez y luego pasa a reposo (idle).

success: un trigger que se reproduce una vez y luego vuelve al estado de ánimo actual.

Cada archivo de Charmotion viene preparado de las dos formas: elige CharacterViewModelMachine con autoBind: true y asigna viewModelInstance.number("mood") o dispara viewModelInstance.trigger("success") para Data Binding, o elige la máquina predeterminada CharacterStateMachine y usa la API de inputs de arriba. Ambas máquinas contienen los mismos 10 loops y 6 reacciones puntuales.

Preguntas frecuentes

¿Cómo cambio de animación con una máquina de estados de Rive?

Cambia el valor que escuchan las transiciones de la máquina de estados. Asigna un input de tipo number o boolean, o una propiedad del view model, y la transición correspondiente pasa al nuevo estado. No llamas a las animaciones por nombre; las condiciones de transición del archivo deciden qué se reproduce cuando cambia un valor.

¿Qué diferencia hay entre un trigger y un boolean en Rive?

Un trigger se dispara una vez y se restablece solo después de que una transición lo consume, así que sirve para eventos puntuales como success o tap. Un boolean mantiene su valor hasta que lo cambias, así que sirve para condiciones duraderas como hovered o muted. Usa triggers para eventos y booleans para estados.

¿Tengo que migrar mis archivos de Rive a Data Binding?

No, al menos no de inmediato. Rive indica que los archivos existentes con inputs siguen funcionando. Migra cuando necesites el nuevo runtime de Apple, React Native Nitro v0.6 o posterior, o tipos de propiedades más completos. El comando Convert Inputs to View Models del editor se encarga del archivo; después actualizas tu código de runtime.

¿Por qué mi código no encuentra un input de la máquina de estados?

Normalmente porque el archivo no terminó de cargar o porque un nombre no coincide exactamente. Consulta los inputs en el callback onLoad, comprueba que pasaste el nombre correcto de la máquina de estados y compara los nombres de los inputs carácter por carácter, incluidas mayúsculas y espacios. Los archivos convertidos a view models ya no exponen inputs.

Respuestas rápidas de esta guía

Fuentes

Guías relacionadas