Rive Animasyon Sürecimiz: Arapça Karakterleri Canlandırmak
Amal, tüm karakter animasyonları için Rive (eski adıyla Flare) kullanıyor — dudak senkronlu konuşma, avatar kişiselleştirme, geri bildirim reaksiyonları ve oyun karakterleri dahil. Rive’yi Lottie veya sprite sayfaları yerine seçmemizin sebebi; çalışma zamanında durum makinelerini desteklemesi, programatik manipülasyonu ve 60fps GPU hızlandırmalı render’ı tek ve kompakt bir dosyada sunmasıdır.
Animasyon Varlık Kütüphanesi
Temel Karakterler
lip-sync-amal-01.riv
- Ana Amal karakteri (tam vücut ve sadece yüz versiyonları)
- Her ağız pozisyonu için birden fazla artboard (fonem eşleştirme için)
- Durumlar: boşta, konuşma, hata, kutlama, uyuma
- Dosya boyutu: 1.2 MB (sprite sayfalarına kıyasla 50+ MB yerine)avatar.riv
- Özelleştirilebilir kullanıcı avatarı (3 artboard)
1. Tam vücut: baş, gövde, uzuvlar ve kıyafetler
2. Sadece baş: kontrol paneli ve ebeveyn uygulaması için
3. Kelebek dost: ödül animasyonu
- Bileşen bazlı: baş şekli, saç, gözler, kıyafetler, aksesuarlar, renkler
- Dosya boyutu: 2.4 MBcoin-01.riv&coins-01.riv
- Ödül animasyonları (yüzen ve toplanan paralara yönelik)
- Tek madeni para: 150 KB
- Çoklu paralar: 300 KBcute-monster-final.riv
- Çoklu duygu durumları olan geri bildirim karakteri
- Durumlar: mutlu (doğru cevap), kafası karışık (yanlış), düşünüyor (işleme), kutlama (seriyi devam ettirme)
- Dosya boyutu: 1.8 MB
Android-Özel Optimizasyon
- 16KB sayfa hizalaması için özel NDK derlemesi (Rive NDK-r28)
- Standart derlemeye göre ikili dosya boyutunu %8 azaltır
- Android 12+ agresif bellek yönetimi ile uyumluluğu garanti eder
Dudak Senkronizasyonu Süreci (Teknik Detaylar)
Adım 1: TTS Ses Üretimi ve Konuşma İşaretlerinin Çıkarılması
# 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' # WaveNet sesi
)
audio_config = texttospeech.AudioConfig(
audio_encoding=texttospeech.AudioEncoding.MP3,
effects_profile_id=['small-bluetooth-speaker-class-device'] # Çocuk hoparlörü
)
# Konuşma işaretleri (fonem zamanlaması) talebi
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)
# Yanıtta:
# - audio_content: MP3 verisi
# - timepoints: [{character, byte_pos, time_ms}, ...]
return {
'audio': response.audio_content,
'speech_marks': response.timepoints # Fonem düzeyinde zaman damgaları
}
Adım 2: Fonemlerin Rive Ağız Durumlarına Haritalanması
lip_sync_avatar.json Arapça fonemleri ağız pozisyonlarına eşler:
{
"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 } },
...
]
}
Adım 3: LipSyncController Oynatmayı Yönetir
// 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) {
// 1. Rive karakterini yükle
riveCharacter.loadRiveFile('lip-sync-amal-01.riv');
// 2. TTS çıktıdan konuşma işaretlerini yükle
mapper = LipSyncMapper.fromJson(loadJsonAsset('lip_sync_avatar.json'));
// 3. Ağız animasyonunu çalıştırarak sesi oynat
audioPlayer.play(AudioSource.file(audioPath));
// 4. Her ses pozisyonunda ağız durumunu güncelle
audioPlayer.onPositionChanged.listen((Duration position) {
String phoneme = mapper.phonemeAtTime(position.inMilliseconds);
String riveState = mapper.riveStateForPhoneme(phoneme);
riveCharacter.setStateInput('mouth_state', riveState);
});
}
}
Adım 4: RiveCharacterController Karakter Yaşam Döngüsünü Yönetir
// Karakterin tüm animasyon durumlarını yönetir (sadece ağız değil)
class RiveCharacterController extends GetxController {
States: boşta → hazırlık → konuşma → boşta → hata/kutlama
void startExercise() {
// Karakter geçişleri: boşta → hazırlık (dinlemeye hazır)
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 Kişiselleştirme Sistemi
Bileşen Tabanlı Mimari
Çocuklar avatarlarını parçalardan kişiselleştirir:
{
"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 Adlandırılmış Elemanlar Haritası (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+ eleman eşleştirmesi
};
Çocuk "yuvarlak baş + mavi gömlek" seçtiğinde uygulama şunları yapar:
1. Rive elemanı Head_Round etkinleştirilir
2. Rive elemanı Shirt_Blue etkinleştirilir
3. Diğer tüm baş şekilleri ve gömlek renkleri devre dışı bırakılır
4. Çocuğun kişiselleştirilmiş avatarı uygulamanın her yerinde görünür
Neden Alternatifler Değil de Rive?
| Özellik | Rive | Lottie | Sprite Sayfaları | Video |
|---|---|---|---|---|
| Durum makineleri | ✓ | ✗ | ✗ | ✗ |
| Çalışma zamanı kontrolü | ✓ (tam) | Parçalı | Manuel | ✗ (pasif) |
| Dosya boyutu | 1-2 MB | 2-3 MB | 50+ MB | 100+ MB |
| Performans | 60fps GPU | 30fps CPU | 60fps GPU | Değişken |
| İnteraktivite | ✓ Tam | ✓ Parçalı | ✓ Tam | ✗ Yok |
| Öğrenme eğrisi | Orta | Kolay | Kolay | Kolay |
| Bakım | Tek .riv dosya | Tek JSON | Yüzlerce resim | Tek video |
Rive kazandı: Programatik kontrol, durum makineleri ve kompaktlık, mobil uygulamada ihtiyaç duyduğumuz özelliklerdir.
Performans Optimizasyonu
- Karakter ön yükleme: Uygulama açılışında .riv dosyalarını yükleyin, her egzersizde değil
- GPU desteği: Rive, uygun cihazlarda otomatik GPU kullanır, eski cihazlarda CPU geri dönüşümü
- Bellek havuzu: Rive denetleyicilerini ekranlar arasında yeniden kullanarak çöp toplama gecikmelerini önleyin
- Sıkıştırma: Rive dosyaları zaten sıkıştırılmıştır, ekstra optimizasyona gerek yoktur
Sonuç: Snapdragon 662+ (2019 orta segment telefonlar) üzerinde 60fps animasyonlar sağlanmıştır.
Sıkça Sorulan Sorular
S: Adobe Animate’den Rive’ye animasyon aktarabilir miyim?
C: Doğrudan değil. Biz Rive'nin kendi editörü (rive.app) ile çalışıyoruz. Animatörler Rive'de tasarlar, Animate veya After Effects değil. İş akışı: Rive’de karakter tasarlanır → .riv olarak dışa aktarılır → Flutter uygulamasına entegre edilir.
S: Farklı beden tipleri veya engeller nasıl yönetiliyor?
C: Avatar kişiselleştirme; zayıf, atletik, yuvarlak beden tipi seçenekleri ve gözlük, işitme cihazları, hareket destekleri gibi aksesuarlar içerir. Böylece tüm çocuklar kendilerini temsil edilmiş hisseder.
S: Çocuk avatarını beğenmezse ne olur?
C: İstediği zaman avatarı kişiselleştirebilir. Uygulama tek tip bir görünüm dayatmaz — çocuklar tam yaratıcı özgürlüğe sahiptir.



