Анимация персонажей на арабском с помощью Rive
5 мин. чтенияMohammad Shaker

Анимация персонажей на арабском с помощью Rive

Amal использует Rive для анимации арабских персонажей: с синхронизацией губ, кастомизацией аватаров и игровыми реакциями.

Engineering

Короткий ответ

Amal использует Rive для анимации арабских персонажей: с синхронизацией губ, кастомизацией аватаров и игровыми реакциями.

Наша технология анимации Rive: оживление арабских персонажей

Amal использует Rive (ранее Flare) для всей анимации персонажей — включая синхронизацию губ с речью, кастомизацию аватаров, реакции и игровых персонажей. Мы выбрали Rive вместо Lottie или спрайт-листов, потому что он поддерживает машины состояний во время выполнения, программное управление и аппаратное ускорение GPU с 60 fps, всё в одном компактном файле для каждого персонажа.

Библиотека анимационных ресурсов

Основные персонажи

  • lip-sync-amal-01.riv
    • Главный персонаж Amal (варианты с полным телом и только лицом)
    • Несколько артбордов для разных положений рта (для фонем)
    • Состояния: бездействие, речь, ошибка, празднование, сон
    • Размер файла: 1.2 МБ (против 50+ МБ у спрайт-листов)
  • avatar.riv
    • Кастомизируемый аватар пользователя (3 артборда)
      • Полное тело: голова, торс, конечности с одеждой
      • Только голова: для панели и родительского приложения
      • Бабочка компаньон: анимация награды
    • Компонентная система: форма головы, волосы, глаза, одежда, аксессуары, цвета
    • Размер файла: 2.4 МБ
  • coin-01.riv и coins-01.riv
    • Анимации наград (монеты плавающие, собираемые)
    • Одна монета: 150 КБ
    • Несколько монет: 300 КБ
  • cute-monster-final.riv
    • Персонаж для обратной связи с несколькими эмоциональными состояниями
    • Состояния: счастлив (правильный ответ), смущён (ошибка), задумчив (обработка), празднует (серия)
    • Размер файла: 1.8 МБ

Оптимизация для Android

  • Собственная сборка NDK (Rive NDK-r28) для 16КБ выравнивания страниц
  • Уменьшает размер бинарника на 8% по сравнению со стандартной сборкой
  • Обеспечивает совместимость с агрессивным управлением памятью в Android 12 и выше

Технический процесс синхронизации губ (lip-sync)

Шаг 1: Генерация TTS аудио и извлечение речевых меток

# src/services/tts_client.py
from google.cloud import texttospeech

def generate_speech_with_marks(text: str, language: str = 'ar-SA'): client = texttospeech.TextToSpeechClient()

synthesis_input = texttospeech.SynthesisInput(text=text)
voice = texttospeech.VoiceSelectionParams(
    language_code=language,
    name='ar-SA-Neural2-A'  # Voice WaveNet
)
audio_config = texttospeech.AudioConfig(
    audio_encoding=texttospeech.AudioEncoding.MP3,
    effects_profile_id=['small-bluetooth-speaker-class-device']  # Детская колонка
)

request = texttospeech.SynthesizeSpeechRequest(
    input=synthesis_input,
    voice=voice,
    audio_config=audio_config,
    enable_text_to_speech_as_cloud_service=True
)

response = client.synthesize_speech(request=request)

# Ответ содержит:
# - аудиоданные в MP3
# - временные метки phoneme-level

return {
    'audio': response.audio_content,
    'speech_marks': response.timepoints
}

Шаг 2: Сопоставление фонем с состояниями рта в Rive

lip_sync_avatar.json содержит карту фонем арабского языка к положениям рта:

{
  "phoneme_map": {
    "ا": { "rive_state": "mouth_a_open", "duration_ms": 200 },
    "ب": { "rive_state": "mouth_lips_closed", "duration_ms": 150 },
    "ع": { "rive_state": "mouth_pharyngeal", "duration_ms": 250 },
    "ق": { "rive_state": "mouth_uvular", "duration_ms": 180 },
    ...
  },
  "mouth_positions": [
    { "id": "mouth_a_open", "blend_values": { "jaw_open": 0.8, "lips_rounded": 0.2 } },
    { "id": "mouth_lips_closed", "blend_values": { "jaw_open": 0.1, "lips_rounded": 0.9 } },
    ...
  ]
}

Шаг 3: LipSyncController управляет воспроизведением

// lib/src/modules/animations/controllers/lip_sync_controller.dart
class LipSyncController extends GetxController {
  late Rive riveCharacter;
  late AudioPlayer audioPlayer;
  late LipSyncMapper mapper;

void playWithLipSync(String text, String audioPath) { riveCharacter.loadRiveFile('lip-sync-amal-01.riv'); mapper = LipSyncMapper.fromJson(loadJsonAsset('lip_sync_avatar.json')); audioPlayer.play(AudioSource.file(audioPath)); audioPlayer.onPositionChanged.listen((Duration position) { String phoneme = mapper.phonemeAtTime(position.inMilliseconds); String riveState = mapper.riveStateForPhoneme(phoneme); riveCharacter.setStateInput('mouth_state', riveState); }); } }

Шаг 4: RiveCharacterController управляет состояниями персонажа

// Управляет полным состоянием анимации персонажа (не только рот)
class RiveCharacterController extends GetxController {
  // Состояния: idle → prepare → speaking → idle → error/celebration

void startExercise() { character.setStateInput('state_machine', 'prepare'); }

void childSpeaks(String recognizedText, double accuracy) { character.setStateInput('state_machine', 'speaking'); lipSyncController.playFeedback(recognizedText); }

void onFeedbackComplete(bool wasCorrect) { if (wasCorrect) { character.setStateInput('state_machine', 'celebrate'); playRewardAnimation(); } else { character.setStateInput('state_machine', 'error'); playEncouragingPhrase(); } } }

Система кастомизации аватара

Компонентная архитектура

Дети могут настраивать аватар, выбирая из частей:

{
  "avatar_customization": {
    "head_shapes": [
      { "id": "round", "rive_element": "head_round" },
      { "id": "oval", "rive_element": "head_oval" },
      { "id": "square", "rive_element": "head_square" }
    ],
    "hair_styles": [
      { "id": "ponytail", "rive_element": "hair_ponytail" },
      { "id": "braids", "rive_element": "hair_braids" },
      { "id": "straight", "rive_element": "hair_straight" }
    ],
    "colors": {
      "skin_tone": ["light", "medium", "dark"],
      "hair_color": ["black", "brown", "blonde", "red"],
      "shirt_color": ["blue", "pink", "green", "yellow", "purple"],
      "accent_color": ["red", "orange", "green", "blue"]
    }
  }
}

Матчинг названных Rive элементов (avatar_customization_rive_names.dart):

const avatarRiveNames = {
  'head_round': 'Head_Round',
  'head_oval': 'Head_Oval',
  'hair_ponytail': 'Hair_Ponytail',
  'shirt_blue': 'Shirt_Blue',
  'shirt_pink': 'Shirt_Pink',
  // ... более 50 элементов
};

При выборе «круглая голова + синяя рубашка» приложение:

  1. Включает элемент Rive Head_Round
  2. Включает элемент Shirt_Blue
  3. Отключает остальные формы головы и цвета рубашек
  4. Персональный аватар ребёнка отображается во всех частях приложения

Почему мы выбрали Rive вместо альтернатив

ФункцияRiveLottieСпрайт-листыВидео
Машины состояний
Управление в реальном времени✓ (полное)ЧастичноеРучное✗ (пассивное)
Размер файла1-2 МБ2-3 МБ50+ МБ100+ МБ
Производительность60fps GPU30fps CPU60fps GPUПеременная
Интерактивность✓ Полная✓ Частичная✓ Полная✗ Нет
Кривая обученияСредняяЛёгкаяЛёгкаяЛёгкая
ПоддержкаОдин .riv файлОдин JSONСотни изображенийОдно видео

Победа за Rive благодаря программному управлению, поддержке машин состояний и компактности для мобильных приложений.

Оптимизация производительности

  • Предзагрузка персонажей — загрузка .riv файлов при старте приложения, а не в каждом упражнении
  • GPU-рендеринг — Rive автоматически использует аппаратное ускорение, с fallback на CPU на старых устройствах
  • Повторное использование Rive контроллеров для сокращения пауз из-за сборщика мусора
  • Файлы Rive уже сжаты — дополнительная компрессия не требуется

Результат: стабильная анимация с 60 fps на Snapdragon 662 и выше (средний сегмент 2019 года).

Часто задаваемые вопросы

В: Можно ли экспортировать анимации из Adobe Animate в Rive?
О: Нет напрямую. Мы используем нативный редактор Rive (rive.app). Аниматоры создают персонажей, экспортируют .riv и затем интегрируют в приложение Flutter.

В: Как вы учитываете разные типы телосложения и особенности детей?
О: В системе кастомизации есть варианты телосложения (стройное, атлетическое, круглое) и аксессуары (очки, слуховые аппараты, средства передвижения), обеспечивая представительство для всех детей.

В: Что если ребёнку не понравится его аватар?
О: Можно менять его в любое время. Приложение не навязывает внешний вид — дети имеют полный творческий контроль.

ПоделитьсяTwitterLinkedInWhatsApp

Похожие статьи