हमारी Rive एनीमेशन पाइपलाइन: अरबी पात्रों को जीवंत बनाना
Amal ऐप में सभी पात्र एनिमेशन के लिए Rive (पूर्व में Flare) का उपयोग किया जाता है — लिप-सिंक स्पीच, अवतार अनुकूलन, प्रतिक्रिया अभिव्यक्तियाँ, और गेम पात्र। हमने Lottie या स्प्राइट शीट्स के बजाय Rive चुना क्योंकि यह रनटाइम स्टेट मशीनों, प्रोग्रामैटिक मैनिपुलेशन, और GPU-तीव्र रेंडरिंग (60fps) का समर्थन करता है, और प्रत्येक पात्र के लिए एक कॉम्पैक्ट फाइल प्रदान करता है।
एनीमेशन एसेट लाइब्रेरी
कोर पात्र
lip-sync-amal-01.riv- मुख्य Amal पात्र (पूर्ण शरीर और केवल चेहरे के वेरिएंट)
- प्रत्येक मुँह की स्थिति के लिए कई आर्टबोर्ड (फोनिम मैपिंग के लिए)
- स्टेट्स: निष्क्रिय, बोलना, त्रुटि, उत्सव, सोना
- फाइल साइज: 1.2 एमबी (स्प्राइट शीट्स के 50+ एमबी के मुकाबले)
avatar.riv- कस्टमाइज़ेबल उपयोगकर्ता अवतार (3 आर्टबोर्ड)
- पूर्ण शरीर: सिर, धड़, अंगों सहित कपड़े
- सिर्फ सिर: डैशबोर्ड और पैरेंट ऐप के लिए
- तितली साथी: इनाम एनिमेशन
- कॉम्पोनेन्ट-आधारित: सिर की आकृति, बाल, आंखें, कपड़े, सहायक उपकरण, रंग
- फाइल साइज: 2.4 एमबी
- कस्टमाइज़ेबल उपयोगकर्ता अवतार (3 आर्टबोर्ड)
coin-01.rivएवंcoins-01.riv- इनाम एनिमेशन (सिक्के तैरते और इकट्ठे होते)
- एक सिक्का: 150 केबी
- कई सिक्के: 300 केबी
cute-monster-final.riv- प्रतिक्रिया वर्ण जिसमें कई भावात्मक अवस्थाएँ शामिल हैं
- स्टेट्स: खुश (सही उत्तर), उलझन में (गलत), सोच रहा (प्रोसेसिंग), जश्न मना रहा (स्ट्रिक)
- फाइल साइज: 1.8 एमबी
एंड्रॉइड-विशिष्ट अनुकूलन
- कस्टम NDK बिल्ड (Rive NDK-r28) 16KB पेज संरेखण कम्प्लायंस के लिए
- स्टैंडर्ड बिल्ड के मुकाबले बाइनरी साइज 8% कम
- Android 12+ की सख्त मेमोरी प्रबंधन के साथ संगतता सुनिश्चित करता है
लिप-सिंक पाइपलाइन (तकनीकी विवरण)
चरण 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' # WaveNet voice
)
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)
# प्रतिक्रिया में शामिल हैं:
# - audio_content: MP3 बाइट्स
# - timepoints: [{character, byte_pos, time_ms}, ...]
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) {
// चरण 1: Rive पात्र लोड करें
riveCharacter.loadRiveFile('lip-sync-amal-01.riv');
// चरण 2: TTS आउटपुट से स्पीच मार्क्स लोड करें
mapper = LipSyncMapper.fromJson(loadJsonAsset('lip_sync_avatar.json'));
// चरण 3: मुँह की एनिमेशन ड्राइव करते हुए ऑडियो चलाएं
audioPlayer.play(AudioSource.file(audioPath));
// चरण 4: हर ऑडियो फ्रेम पर मुँह की स्थिति अपडेट करें
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 {
States: idle → prepare → speaking → idle → error/celebration
void startExercise() {
// पात्र संक्रमण: idle → prepare (सुनने के लिए तैयार)
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 Named Elements Mapping (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+ तत्व मैपिंग
};
जब बच्चा "गोल सिर + नीली शर्ट" चुनता है, तो ऐप:
- Rive एलिमेंट
Head_Roundको सक्षम करता है - Rive एलिमेंट
Shirt_Blueको सक्षम करता है - अन्य सभी सिर के आकार और शर्ट रंगों को अक्षम करता है
- बच्चा का व्यक्तिगत अवतार पूरी ऐप में दिखता है
क्यों Rive दूसरों से बेहतर है?
| विशेषता | Rive | Lottie | स्प्राइट शीट्स | वीडियो |
|---|---|---|---|---|
| स्टेट मशीनें | ✓ | ✗ | ✗ | ✗ |
| रनटाइम कंट्रोल | पूर्ण | आंशिक | मैनुअल | नहीं |
| फाइल साइज | 1-2 MB | 2-3 MB | 50+ MB | 100+ MB |
| परफॉर्मेंस | 60fps GPU | 30fps CPU | 60fps GPU | परिवर्तनीय |
| इंटरैक्टिविटी | पूर्ण | आंशिक | पूर्ण | नहीं |
| लर्निंग कर्व | मध्यम | आसान | आसान | आसान |
| मेंटेनेंस | एक .riv फाइल | एक JSON | सैंकड़ों इमेज | एक वीडियो |
Rive इसलिए बेहतर है क्योंकि हमें प्रोग्रामैटिक कंट्रोल, स्टेट मशीन, और मोबाइल ऐप के लिए कॉम्पैक्ट फाइल की जरूरत है।
प्रदर्शन अनुकूलन
- पात्र प्रीलोड करें: ऐप स्टार्टअप पर .riv फाइलें लोड करें, प्रत्येक अभ्यास पर नहीं।
- GPU रेंडरिंग: Rive उपलब्ध GPUs का उपयोग करता है, पुराने डिवाइसों पर CPU पर चलते हैं।
- मेमोरी पूलिंग: स्क्रीन के बीच Rive कंट्रोलर्स को पुन: उपयोग करें ताकि गारबेज संग्रहण रोका जा सके।
- कंप्रेशन: Rive फाइलें पहले से ही संपीड़ित होती हैं; अतिरिक्त ऑप्टिमाइज़ेशन की ज़रूरत नहीं।
नतीजा: Snapdragon 662+ (2019 के मिड-रेंज फोन) पर 60fps एनिमेशन।
अक्सर पूछे जाने वाले प्रश्न (FAQ)
प्रश्न: क्या Adobe Animate से Rive में सीधे एनिमेशन निर्यात कर सकते हैं?
उत्तर: नहीं। हम Rive का देशी एडिटर (rive.app) उपयोग करते हैं। एनिमेटर Rive में डिजाइन करते हैं, Animate या After Effects में नहीं। वर्कफ़्लो है: Rive में पात्र डिज़ाइन करें → .riv एक्सपोर्ट करें → Flutter ऐप में इंटीग्रेट करें।
प्रश्न: अलग-अलग शरीर प्रकार या विकलांगताएं कैसे संभालते हैं?
उत्तर: अवतार कस्टमाइजेशन में शरीर के प्रकार (पतला, मजबूत, गोल), चश्मा, सुनने के उपकरण, और मोबिलिटी एड्स जैसे सहायक उपकरण शामिल हैं ताकि सभी बच्चे प्रतिनिधित्व देख सकें।
प्रश्न: अगर बच्चे को अपना अवतार पसंद न आए तो?
उत्तर: वे कभी भी अनुकूलित कर सकते हैं। ऐप बच्चों को रचनात्मक पूरी स्वतंत्रता देता है, कोई ज़बरदस्ती नहीं।



