Cara Kami Animasi Karakter Arab dengan Rive
Baca 5 mntMohammad Shaker

Cara Kami Animasi Karakter Arab dengan Rive

Amal gunakan Rive untuk animasi karakter Arab: sinkron bibir, avatar, reaksi, dan game. Rive unggul dari Lottie karena dukungan mesin status dan GPU.

Engineering

Jawaban Singkat

Amal gunakan Rive untuk animasi karakter Arab: sinkron bibir, avatar, reaksi, dan game. Rive unggul dari Lottie karena dukungan mesin status dan GPU.

Amal menggunakan Rive (sebelumnya Flare) untuk semua animasi karakter — termasuk sinkronisasi bibir dengan suara, kostumisasi avatar, reaksi umpan balik, dan karakter game. Kami memilih Rive dibanding Lottie atau sprite sheets karena mendukung mesin status saat runtime, manipulasi programatik, dan rendering GPU akselerasi 60fps, semua dalam satu file kompak per karakter.

Perpustakaan Aset Animasi

Karakter Inti

  • lip-sync-amal-01.riv
    • Karakter utama Amal (varian full-body dan hanya wajah)
    • Beberapa artboard per posisi mulut (untuk pemetaan fonem)
    • Status: diam, berbicara, kesalahan, perayaan, tidur
    • Ukuran file: 1,2 MB (dibanding >50 MB untuk sprite sheets)
  • avatar.riv
    • Avatar pengguna yang dapat dikustomisasi (3 artboard)
      1. Full-body: kepala, badan, tangan & kaki dengan pakaian
      2. Hanya kepala: untuk dashboard dan aplikasi orang tua
      3. Teman kupu-kupu: animasi hadiah
    • Berbasis komponen: bentuk kepala, rambut, mata, pakaian, aksesori, warna
    • Ukuran file: 2,4 MB
  • coin-01.riv & coins-01.riv
    • Animasi hadiah (koin melayang dan terkumpul)
    • Koin tunggal: 150 KB
    • Banyak koin: 300 KB
  • cute-monster-final.riv
    • Karakter umpan balik dengan berbagai emosi
    • Status: senang (jawaban benar), bingung (salah), berpikir (proses), merayakan (streak)
    • Ukuran file: 1,8 MB

Optimalisasi Khusus Android

  • Build NDK khusus (Rive NDK-r28) untuk kepatuhan penyelarasan halaman 16KB
  • Memperkecil ukuran biner sebesar 8% dibanding build standar
  • Memastikan kompatibilitas dengan manajemen memori agresif di Android 12+

Proses Sinkronisasi Bibir (Lip-Sync) Teknis

Langkah 1: Pembuatan Audio TTS + Ekstraksi Tanda Bicara

# 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'  # Suara WaveNet
)
audio_config = texttospeech.AudioConfig(
    audio_encoding=texttospeech.AudioEncoding.MP3,
    effects_profile_id=['small-bluetooth-speaker-class-device']  # Speaker anak
)

# Minta tanda bicara (waktu fonem)
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)

# Respons mencakup:
# - audio_content: byte MP3
# - timepoints: [{character, byte_pos, time_ms}, ...]

return {
    'audio': response.audio_content,
    'speech_marks': response.timepoints  # Timestamp tingkat fonem
}

Langkah 2: Pemetaan Fonem ke Status Mulut Rive

{
  "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 } },
    ...
  ]
}

Langkah 3: LipSyncController Mengatur Pemutaran

// 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) { // Langkah 1: Muat karakter Rive riveCharacter.loadRiveFile('lip-sync-amal-01.riv');

// Langkah 2: Muat tanda bicara dari output TTS
mapper = LipSyncMapper.fromJson(loadJsonAsset('lip_sync_avatar.json'));

// Langkah 3: Putar audio sambil menjalankan animasi mulut
audioPlayer.play(AudioSource.file(audioPath));

// Langkah 4: Setiap frame audio, update posisi mulut
audioPlayer.onPositionChanged.listen((Duration position) {
  String phoneme = mapper.phonemeAtTime(position.inMilliseconds);
  String riveState = mapper.riveStateForPhoneme(phoneme);
  
  riveCharacter.setStateInput('mouth_state', riveState);
});

} }

Langkah 4: RiveCharacterController Mengelola Siklus Hidup

// Mengelola status animasi karakter penuh (bukan hanya mulut)
class RiveCharacterController extends GetxController {
  States: idle → prepare → speaking → idle → error/celebration

void startExercise() { // Transisi karakter: idle → prepare (siap mendengar) 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(); } } }

Sistem Kostumisasi Avatar

Arsitektur Berbasis Komponen

Anak-anak mengkustomisasi avatar mereka dari bagian-bagian:

{
  "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"]
    }
  }
}

Pemetaan Elemen Bernama 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+ pemetaan elemen
};

Ketika anak memilih "kepala bulat + baju biru," aplikasi akan:

  1. Mengaktifkan elemen Rive Head_Round
  2. Mengaktifkan elemen Rive Shirt_Blue
  3. Menonaktifkan semua bentuk kepala lain dan warna baju lain
  4. Avatar yang dipersonalisasi tampil di seluruh aplikasi

Mengapa Memilih Rive Dibanding Alternatif

FiturRiveLottieSprite SheetsVideo
Mesin status
Kontrol runtime✓ (penuh)SebagianManual✗ (pasif)
Ukuran file1-2 MB2-3 MB50+ MB100+ MB
Performa60fps GPU30fps CPU60fps GPUVariabel
Interaktivitas✓ Penuh✓ Sebagian✓ Penuh✗ Tidak ada
Kurva belajarSedangMudahMudahMudah
PemeliharaanSatu file .rivSatu JSONRatusan gambarSatu video

Rive unggul karena kami butuh kontrol programatik, mesin status, dan ukuran kompak untuk aplikasi mobile.

Optimasi Performa

  • Pramuat karakter: muat file .riv saat mulai aplikasi, bukan tiap latihan
  • Rendering GPU: Rive otomatis pakai GPU jika tersedia, fallback CPU di perangkat lama
  • Pooling memori: gunakan ulang controller Rive antar layar agar tidak terjadi jeda pengumpulan sampah
  • Kompressi: file Rive sudah terkompres; tidak perlu optimasi tambahan

Hasil: animasi 60fps lancar di Snapdragon 662+ (ponsel kelas menengah 2019).

FAQ

Tanya: Bisakah saya ekspor animasi dari Adobe Animate ke Rive?
Jawab: Tidak langsung. Kami gunakan editor asli Rive (rive.app). Animator mendesain di Rive, bukan Animate atau After Effects. Alur kerjanya: desain karakter di Rive → ekspor .riv → integrasi ke aplikasi Flutter.

Tanya: Bagaimana menangani tipe tubuh berbeda atau disabilitas?
Jawab: Kostumisasi avatar menyediakan opsi tipe tubuh (langsing, atletik, bulat) dan aksesori (kacamata, alat bantu dengar, alat mobilitas). Ini memastikan semua anak merasa terwakili.

Tanya: Bagaimana jika anak tidak suka avatarnya?
Jawab: Anak bisa mengkustomisasi kapan saja. Aplikasi tidak memaksa satu tampilan tertentu — anak memiliki kendali penuh atas kreativitas mereka.

Artikel Terkait