آموزش استفاده از صدا و انیمیشن در فریم ورک React native

دوشنبه 25 بهمن 1400

استفاده از صوت و انیمیشن در فریم ورک React native بسیار ساده و راحت است ولی شما باید به صورت اصولی از آنها استفاده کنید. ما در این مطلب قصد داریم نحوه استفاده از صوت و انیمیشن را در فریم ورک React native مورد بررسی قرار دهیم و نکاتی را درباره این موضوع به شما آموزش دهیم.

 آموزش استفاده از صدا و انیمیشن در فریم ورک React native

شما با مطالعه این مطلب می آموزید که چگونه در اپلیکیشن هایی که با استفاده از فریم ورک React native می سازید از صوت و انیمیشن استفاده کنید. علاوه بر این در ادامه این مطلب اطلاعاتی درباره داده های ماندگار React-Native AsyncStorage را نیز به دست خواهید آورد.

اضافه کردن صدا به اپلیکیشن های ساخته شده با فریم ورک React native

همانطور که احتمالا می دانید ما یک پوشه /music و یک پوشه /SFX در پروژه خود داریم ولی تا به این جای کار چندان از آنها استفاده نکرده ایم. شما می توانید موسیقی های مدنظر خود را از منابع مختلف دانلود و استخراج کنید تا در پروژه خود از آنها استفاده کنید. ما در این پروژه از API داخلی  Expo برای کار کردن با موسیقی در فریم ورک React native استفاده خواهیم کرد. بنابراین ما کار خود را در Home/index.js برای اضافه کردن تم منو اصلی آغاز خواهیم کرد.

در اولین گام باید Audio API را از ExpoKit ایمپورت کنیم:

import { Audio } from 'expo';

در ادامه باید music را import  کرده و آن را در componentWillMount() اجرا کنیم:

async componentWillMount() {
  this.backgroundMusic = new Audio.Sound();
  try {
    await this.backgroundMusic.loadAsync(
      require("../../assets/music/Komiku_Mushrooms.mp3")
    );
    await this.backgroundMusic.setIsLoopingAsync(true);
    await this.backgroundMusic.playAsync();
    // Your sound is playing!
  } catch (error) {
    // An error occurred!
  
}

با اجرای این قطعه کد مشاهده می کنیم که موسیقی به درستی بارگذاری می شود. در ادامه موسیقی با استفاده از یک حلقه تنظیم شده و این قطعه کد شروع به پخش کردن آن به صورت ناهمزمان می کند.

در طول اجرای این روند اگر خطایی رخ دهد آن را در بخش مربوط به catch مدیریت خواهیم کرد. برای این کار شما می توانید تنها به کاربر اطلاع دهید که خطایی رخ داده است و دلیل خطا را نیز برای او نمایش دهید.

نکاتی درباره قطعه کد بالا

در بخش onPlayPress کافی است که به سادگی یک خط را پیش از navigation به صورت زیر اضافه کنیم:

this.backgroundMusic.stopAsync();

اگر در زمان رفتن به یک صفحه دیگر پخش موسیقی را متوقف نکنید پخش موسیقی در صفحات بعدی نیز ادامه پیدا خواهد کرد.

نکته بسیار جالب دیگری که وجود دارد این است که شما می توانید به سادگی موسیقی های پس زمینه را نیز به بازی های خود اضافه کنید تا جذابیت های بازی خود را چند برابر کنید.

همه چیز را با SFX در فریم ورک React native تقویت کنید

در کنار موسیقی جلوه های صوتی نیز می توانند نقش مهمی در سرگرم کننده شدن یک بازی داشته باشند. یک افکت صوتی در منوی اصلی( زمانی که کاربر روی یک دکمه می زند) و 6 افکت نیز در صفحه بازی خواهیم داشت. اجازه دهید تا کار خود را با منوی اصلی SFX آغاز کنیم و از آنجا شما می توانید بقیه موارد را به تنهایی به صفحه بازی خود اضافه کنید.

ما تنها به چند خط کد نیاز داریم تا یک شی buttonFX که در واقع یک شی از  Audio.Sound() می باشد ایجاد کنیم. در ادامه فایل های صوتی مد نظر خود را به همان بلاک try-catch اضافه می کنیم که فایل های موسیقی مربوط به موسیقی پس زمینه در آن قرار دارد.

async componentWillMount() {
   this.backgroundMusic = new Audio.Sound();
   this.buttonFX = new Audio.Sound();
   try {
     await this.backgroundMusic.loadAsync(
       require("../../assets/music/Komiku_Mushrooms.mp3")
     );
     await this.buttonFX.loadAsync(
       require("../../assets/sfx/button.wav")
     );
    ...

برای پخش افکت صوتی شما تنها به یک خط کد نیاز خواهید داشت. در بالای مدیریت کننده رویداد onPlayPress قطعه کد زیر را اضافه خواهیم کرد:

onPlayPress = () => {
   this.buttonFX.replayAsync();
   ...

به نحوه استفاده از replayAsync به جای playAsync دقت کنید. دلیل این موضوع این است ممکن است ما قصد داشته باشیم از این افکت صوتی در بیش از یک مورد استفاده کنیم و اگر ما از playAsync استفاده کرده و چندین بار آن را اجرا کنیم این قطعه کد افکت صوتی را تنها بار اول برای ما پخش خواهد کرد. در ادامه مشاهده خواهیم کرد که این کار نیز برای ما بسیار مفید خواهد بود.

سایر افکت های صوتی

حال شما باید 6 افکت صوتی زیر را به ترتیب در صفحه بازی خود قرار دهید:

-          Button tap

assets/sfx/button.wav/../..

زمانی اجرا می شود که دکمه exit فشرده شود.

-          Tile tap – correct

assets/sfx/tile_tap.wav/../..

در داخل onTilePress/good tile اجرا می شود.

-          Tile tap – wrong

assets/sfx/tile_wrong.wav/../..

در داخل بلاک onTilePress/wrong tile اجرا می شود. 

-          Pause – in

assets/sfx/pause_in.wav/../..

در داخل nBottomBarPress/case "INGAME" اجرا می شود.

-          Pause – out

assets/sfx/pause_out.wav/../..

در داخل بلاک onBottomBarPress/case "PAUSED" اجرا می شود.

-          Lose

assets/sfx/lose.wav/../..

در داخل بلاک پی در پی if (this.state.timeLeft <= 0) اجرا می شود.

همچنین موسیقی پس زمینه را با استفاده از this.backgroundMusic.stopAsync(); متوقف می کند.

فراموش نکنید که زمانی که دوباره بازی را راه اندازی می کنید موسیقی را نیز مجددا پخش کنید. شما می توانید این کار را با اضافه کردن this.backgroundMusic.replayAsync(); به بلاک onBottomBarPress/case "LOST" انجام دهید.

مقدمه ای بر انیمیشن های فریم ورک React native

انیمیشن ها در فریم ورک React native یک موضوع گسترده هستند، به همین علت نیز ما در این مطلب تنها می توانیم بخش کوچکی از آنها را پوشش دهیم. به طور کلی ما در فریم ورک React native می توانیم از طیف گسترده ای از انیمیشن ها استفاده کنیم( مثلا می توانیم کاری کنیم زمانی که کاربر یک دکمه را می زند دکمه تکان بخورد). با این حال ما در این آموزش قصد داریم تنها یک انیمیشن را به شما آموزش دهیم. این انیمیشن زمانی که کاربر روی کاشی اشتباهی می رود کل محیط را می لرزاند.

مزایای این نوع انیمیشن در فریم ورک React native و پروژه ما

این نوع انیمیشن در پروژه بازی ما می تواند چندین مزیت را به همراه داشته باشد. اولین مزیت آن این است که نوعی مجازات برای کاربر به شمار می آید چرا که به اتمام رسیدن آن چند لحظه ای به طول می انجامد و همانطور که قبلا هم اشاره کردیم به محض این که کاربر روی کاشی اشتباهی می رود بازخورد فوری خواهد داشت. علاوه بر این باید دقت داشته باشید که این نوع انیمیشن جذاب است و جذابیت بازی شما را نیز افزایش می دهد. توجه داشته باشید که چندین فریم ورک انیمیشن برای فریم ورک React native وجود دارد که از جمله آنها می توان به react-native-animatable اشاره کرد ولی ما قصد داریم از یک API داخلی برای انجام این کار استفاده کنیم. اگر شما تاکنون با این API آشنا نشده اید بهتر است که پیش از هر چیز مستندات آن را مطالعه کنید.

اضافه کردن انیمیشن های React native به بازی

پیش از هر چیز اجازه دهید تا یک مقدار animated را در حالتی مقداردهی اولیه کنیم که بعدها نیز بتوانیم از آن در محیط خود استفاده کنیم:

state = {
  ...
  shakeAnimation: new Animated.Value(0)
};

حال برای <View> که شامل grid generator می باشد تنها کافی است که <View> را به  <Animated.View> تغییر دهیم.( فراموش نکنید که تگ پایانی را نیز به همین ترتیب تغییر دهید). حال در inline style کافی است که  left: shakeAnimation را اضافه کنیم که باعث می شود تا کدهای ما چیزی شبیه به قطعه کد زیر باشد:

<Animated.View
   style={{
     height: height / 2.5,
     width: height / 2.5,
     flexDirection: "row",
     left: shakeAnimation
  }
>
   {gameState === "INGAME" ?
   ...

اجرای قطعه کد بالا در فریم ورک React native

حال اجازه دهید تا پروژه ای که ایجاد کرده ایم را ذخیره و اجرا کنیم. توجه داشته باشید که در زمان بازی نباید هیچ تفاوتی را کاربر متوجه شود. اگر تفاوتی را متوجه می شوید باید بدانید که یک قسمت از کدها را اشتباه پیاده سازی کرده اید و باید اطمینان حاصل کنید که تمام مراحل را دقیقا دنبال کرده اید. اکنون وارد بخش onTilePress() شوید و در بخش  // wrong tile section می توانید انیمیشن سازی grid را آغاز کنید. اگر به مستندات مراجعه کنید مشاهده می کنید که در تابع اصلی توصیه شده است برای شروع انیمیشن سازی در داخل فریم ورک React native از Animated.timing() استفاده کنید.

با استفاده از این روش شما می توانید به راحتی یک مقدار را روی یک مقدار دیگر انیمیشن سازی کنید اما برای تکان دادن یک شی شما به چندین انیمیشن متصل به یکدیگر نیاز پیدا خواهید کرد که پشت سر هم و در یک دنباله پخش شوند. به عنوان مثال اصلاح این انیمیشن از 0 به 50، سپس از 50 به -50 و در ادامه مجددا به 0 می تواند یک حالت لرزش را برای شما در صفحه ایجاد کند.

نکات مهم درباره قطعه کد بالا

اگر نگاهی به مستندات بیندازید مشاهده خواهید کرد که Animated.sequence() دقیقا همین کار را برای شما انجام می دهد و دنباله ای از انیمیشن ها را برای شما پخش می کند. نکته مهمی که در این خصوص باید بدانید این است که شما می توانید تعداد نامتناهی انیمیشن را در یک آرایه قرار دهید و زمانی که play() اجرا می شود این دنباله نیز برای شما پخش خواهد شد.

علاوه بر این شما می توانید از Easing نیز استفاده کنید. برای توضیح دادن هر یک از این انیمیشن ها شما می توانید از back، bounce، ease و elastic استفاده کنید. البته پیش از استفاده اطمینان حاصل کنید که حتما مستندات آنها را بررسی کرده اید. با این حال ما هنوز به آنها نیازی نداریم چرا که باعث می شوند تا عملکرد ما به میزان زیادی کاهش پیدا کند.

دنباله انیمیشن ها

دنبال انیمیشن های ما در فریم ورک React native و در این پروژه چیزی شبیه به قطعه کد زیر خواهد بود:

Animated.sequence([
 Animated.timing(this.state.shakeAnimation, {
   toValue: 50,
   duration: 100
 }),
 Animated.timing(this.state.shakeAnimation, {
   toValue: -50,
   duration: 100
 }),
 Animated.timing(this.state.shakeAnimation, {
   toValue: 50,
   duration: 100
 }),
 Animated.timing(this.state.shakeAnimation, {
   toValue: -50,
   duration: 100
 }),
 Animated.timing(this.state.shakeAnimation, {
   toValue: 0,
   duration: 100
 })
]).start();

این قطعه کد در واقع انیمیشن لرزش را در نقاط 0، 50، -50، 50 و -50 تغییر می دهد. بنابراین ما به راحتی موفق شدیم که کل صفحه را بلرزانیم و سپس به حالت اولیه خود بازگردیم. اگر شما این فایل را ذخیره کرده، برنامه را دوباره بارگذاری کنید و سپس روی کاشی اشتباهی بروید مشاهده می کنید که مجددا این لرزش برای شما اتفاق خواهد افتاد.

انتقال انیمیشن ها از thread جاوا اسکریپت به  UI thread

انیمیشن ها بخش ضروری از هر رابط کاربری روان به شمار می آیند و ارائه آنها به کاربر با در نظر گرفتن هزینه ای که برای عملکرد برنامه دارد کاری است که هر توسعه دهنده ای باید آن را انجام دهد.

Animation API به صورت پیش فرض بر روی thread جاوا اسکریپت اجرا می شود و به همین علت نیز سایر رندرها و اجرای کدها را مسدود می کند. این به آن معناست که اگر در زمان اجرا مسدود شود انیمیشن شما فریم ها را رد می کند. به همین علت است که ما در این بخش قصد داریم انیمیشن های خود را از thread جاوا اسکریپت به UI thread منتقل کنید و خبر خوب درباره این موضوع این است که تنها با چند خط کد می توانید این کار را به راحتی هرچه تمام انجام دهید.

کدهای ما پیش از تغییر به شکل زیر هستند:

Animated.timing(this.state.shakeAnimation, {
   toValue: 0,
   duration: 100
})

کدهای ما پس از تغییر به شکل زیر خواهند بود:

Animated.timing(this.state.shakeAnimation, {
   toValue: 0,
   duration: 100,
   useNativeDriver: true
})

به همین سادگی موفق شدیم این کار را در فریم ورک React native  انجام دهیم.

داده های ماندگار – ذخیره امتیازهای بالا در فریم ورک React native

یکی دیگر از مواردی که شما باید در فریم ورک React native و برای ساخت پروژه بازی به آن دقت داشته باشید ذخیره کردن داده های ماندگار مانند بالاترین امتیاز کاربران است که پروژه هایی که با فریم ورک React native می سازید از اهمیت بسیار زیادی برخوردار خواهد بود. در فریم ورک React native شما یک سیستم ذخیره سازی کلید-مقدار بدون رمزگذاری دارید که به صورت ناهمزمان داده ها را ذخیره می کند. این سیستم AsyncStorage نام دارد. به طور کلی توصیه می کنیم برای پروژه های بزرگ در فریم ورک React native از AsyncStorage استفاده نکنید ولی در این پروژه ساده ما می توانیم از این سیستم استفاده کنیم. اگر قصد دارید از این راهکار استفاده کنید اطمینان حاصل کنید که سیستم های دیگر مانند Realm یا SQLite را نیز بررسی کرده اید.

نحوه استفاده از این سیستم در فریم ورک React native

در ابتدا باید فایلی را تحت  utils با نام storage.js یا چیزی شبیه به این ایجاد کنیم. به طور کلی دو عملیات مهم داریم که باید این کار را انجام دهیم که این دو عملیات ذخیره سازی و بازیابی اطلاعات می باشد که ما این دو عملیات را با استفاده از AsyncStorage API انجام می دهیم.

این API فریم ورک React native دارای دو متد داخلی است که این دو عبارت اند از AsyncStorage.setItem()  برای ذخیره سازی و AsyncStorage.getItem() برای بازیابی داده ها در فریم ورک React native هستند. قطعه کدی که در ادامه برای شما می آوریم این کار را به راحتی برای شما انجام می دهد:

import { AsyncStorage } from "react-native";

export const storeData = async (key, value) => {
 try {
   await AsyncStorage.setItem(`@ColorBlinder:${key}`, String(value));
 } catch (error) {
   console.log(error);
 
};

export const retrieveData = async key => {
 try {
   const value = await AsyncStorage.getItem(`@ColorBlinder:${key}`);
   if (value !== null) {
     return value;
   
 } catch (error) {
   console.log(error);
 
};

ادامه قطعه کد بالا در فریم ورک React native

با اضافه کردن قطعه کد بالا در فریم ورک React native ما در نهایت دو تابع async خواهیم داشت که می توان از آنها برای ذخیره سازی و ماندگاری داده ها در AsyncStorage استفاده کرد. اجازه دهید تا متدهای جدید خود را import کرده و دو کلید که ماندگاری حالت صفحه بازی را نشان می دهند را اضافه کنیم:

import {
 generateRGB,
 mutateRGB,
 storeData,
 retrieveData
} from "../../utilities";
...
state = {
   points: 0,
   bestPoints: 0, // < new
   timeLeft: 15,
   bestTime: 0, // < new
   ...

حال می توانیم این مقادیر را در نوار پایینی نمایش دهیم که این کار را در کنار آیکون های مربوطه در فریم ورک React native انجام خواهیم داد:

<View style={styles.bestContainer}>
 <Image
   source={require("../../assets/icons/trophy.png")}
   style={styles.bestIcon}
 />
 <Text style={styles.bestLabel}>{this.state.bestPoints}</Text>
</View>
. . .
<View style={styles.bestContainer}>
 <Image
   source={require("../../assets/icons/clock.png")}
   style={styles.bestIcon}
 />
 <Text style={styles.bestLabel}>{this.state.bestTime}</Text>
</View>

حال اجازه دهید تا در ابتدا بهترین امتیازها را ذخیره کنیم( در ادامه می توانیم بهترین زمان بازی را نیز ذخیره کنیم). ما یک دستور if داریم که بررسی می کند آیا امتیاز فعلی از امتیاز قبلی بهتر است یا خیر؟( این دقیقا زمانی است که قصد داریم بهترین امتیاز را به روزرسانی کنیم). بنابراین اجازه دهید تا این مسئله را با استفاده از قطعه کد زیر در فریم ورک React native بررسی کنیم:

if (this.state.timeLeft <= 0) {
 this.loseFX.replayAsync();
 this.backgroundMusic.stopAsync();
 if (this.state.points > this.state.bestPoints) {
   this.setState(state => ({ bestPoints: state.points }));
   storeData('highScore', this.state.points);
 
 this.setState(me{ gameState: "LOST" });
} else {
...

در زمان مقداردهی صفحه اولیه در componentWillMount() اطمینان حاصل کنید که بالاترین امتیاز اولیه را خوانده اید و آن را در state ذخیره کرده اید. بنابراین ما می توانیم در ادامه آن را به شکل زیر نمایش دهیم:

retrieveData('highScore').then(val => this.setState({ bestPoints: val || 0 }));

ذخیره سازی بیشترین زمانی که کاربر می تواند ثبت کند در فریم ورک React native

حال که موفق شدیم بالاترین امتیاز موجود در داخل بازی را ذخیره و بازیابی کنیم می توانیم با استفاده از یک تگ امتیاز بالاترین امتیاز را در هر مرحله بازیابی کرده و آن را نمایش دهیم.

قبل از این که به پایان این مطلب برسیم به یک مورد دیگر نیز احتیاج داریم که آن در واقع ذخیره بالاترین زمانی است که کاربر می تواند در داخل بازی به دست بیاورد. برای انجام این کار می توانیم از همان توابعی که در بخش قبلی برای ذخیره سازی بالاترین امتیاز استفاده کردیم استفاده کنیم. با این حال برای بررسی یک سری موارد ما رویکرد متفاوتی را پیش می گیریم و به شکل زیر عمل خواهیم کرد:

this.interval = setInterval(async () => {
 if (this.state.gameState === "INGAME") {
   if (this.state.timeLeft > this.state.bestTime) {
     this.setState(state => ({ bestTime: state.timeLeft }));
     storeData('bestTime', this.state.timeLeft);
   
. . .

این قطعه کد در واقع بررسی می کند که آیا timeLeft فعلی ما از بهترین مقدار آن بیشتر است یا خیر؟ فراموش نکنید که در قسمت بالایی componentWillMount بیشترین مقدار را ذخیره، بازیابی و به کاربر نمایش دهید.

retrieveData('highScore').then(val => this.setState({ bestPoints: val || 0 }));
retrieveData('bestTime').then(val => this.setState({ bestTime: val || 0 }));

حال همان طور که می توانید در پروژه خود که در فریم ورک React native ساخته اید= مشاهده کنید همه چیز آماده و به خوبی تنظیم شده است. تمام ویژگی های اصلی که ما به این پروژه فریم ورک React native اضافه کردیم به خوبی کار می کنند و برنامه ما نیز عملکرد بسیار خوبی را در فریم ورک React native از خود نشان می دهد. در ادامه شما می توانید ویژگی های زیاد دیگری را نیز برای کار کردن با صوت و انیمیشن به بازی های خود در فریم ورک React native اضافه کنید که باعث می شوند تا در نهایت بازی شما جذاب تر شده و کاربران لذت بیشتری را از بازی کردن آن ببرند. 

 

برنامه نویسان

نویسنده 3352 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

تاکنون هیچ کاربری از این پست تشکر نکرده است

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید