در این نوشته هدفم اینه که آرایه رو در یه پروژهی ساده استفاده کنم که کاربردش مشخص بشه ولی این رو در نظر داشته باشید که این نوشته ادامه سه نوشتهی قبلیه پس اگه اونها رو نخوندید چیزهایی که اینجا میخونید احتمالن گیج کننده باشه پس اول این لینک رو بعد این لینک رو بخونید و اگه فکر میکنید لازمه در مورد متد بیشتر بدونید این لینک رو بخونید. طبق روندی هم که تا الان داشتیم فایل پروژه رو به گیت هاب آپلود کردم میتونید اون رو از این لینک دانلود کنید یا با نرمافزار گیت هاب دسکتاپ بازش کنید که بهتره چون آپدیتهای بعدی رو هم میتونید راحت بگیرید. اسم فایل هم SpeedControlArray ﻪ که در پوشهی Scenes قرار داره.
اما داستان چیه؟ در این پروژه قراره کدهایی رو بنویسیم که در محیط یونیتی این امکان رو به ما بده که هر تعداد موضوع رو که لازم دیدیم بهش وصل کنیم که بتونیم تغییراتی در اونها بدیم مثل عوض کردن رنگ اونها و سرعت و جهت چرخش شون رو هم بتونیم تعیین کنیم.
ابتدا با یک مدل شروع میکنیم و یکی از فنهایی که در صحنه هست مثلا فن شماره یک رو برای این کار در نظر میگیریم. در قسمت Assets یه سکریپت جدید میسازیم و اسم SpeedControl رو بهش میدیم و بعد اون رو در ویژوال استودیو باز و سه متغیر زیر رو اضافه میکنیم.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class speedcntltest : MonoBehaviour
{
public GameObject rotatingFans;
public Color colorField;
public float fanRotationSpeed;
void Start()
{
}
void Update()
{
}
}
از متغیر اول برای کنترل چرخش مدل فنها استفاده میکنیم و به این دلیل که تقریبا هر چیزی در صحنهی یونیتی یه GameObject ﻪ پس برای دسترسی به کامپوننت کنترل کننده چرخش مدل فن اسم کلاس GameObject رو قرار میدیم و بعد اسم متغیر. این که میگم هر چیزی در صحنهی یونیتی یه GameObject ﻪ رو میتوانید مشابه System.Object در فریم ورک داتنت در نظر بگیرید که در اون هر چیزی از System.Object میاد در صحنه یونیتی هم GameObject یه کلاسه پایه برای همه موضوعاته. در نمودار زیر اسم تعدادی از مهمترین موضوعات یونیتی که از کلاس GameObject بدست میان مشخص شده که البته تعدادشون بیشتره:
خط شماره هشت قراره یه فیلد رو به محیط یونیتی اضافه کنه که بتونیم باهاش رنگ رو انتخاب کنیم که در نوشته قبلی توضیح دادم. خط نهم هم یه متغیر از نوع اعشاریه که قراره سرعت چرخش فنها رو باهاش کنترل کنیم. هر سهی این متغیرها دسترسیشون از نوع public ﻪ برای اینکه در محیط یونیتی بهشون دسترسی داشته باشیم.
تا اینجا رو ذخیره کنید و برگردید محیط یونیتی. در قسمت Hierarchy یه GameObject بسازید و سکریپت رو بهش وصل کنید. نتیجه باید مشابه تصویر زیر باشه:
اول مدل فن رو به قسمت Rotating Fans وصل و بعد سه خط زیر رو به متد Update اضافه کنید:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class speedcntltest : MonoBehaviour
{
public GameObject rotatingFans;
public Color colorField;
public float fanRotationSpeed;
void Start()
{
}
void Update()
{
var myFanRenderer = rotatingFans.GetComponent<Renderer>();
myFanRenderer.material.SetColor("_Color", colorField);
rotatingFans.transform.Rotate(0.0f, 0.0f, fanRotationSpeed);
}
}
اینکه چرا این سه خط رو به Update اضافه کنیم نه به متد Start دلیلش اینه که بعد زدن دکمه Play در یونیتی بتونیم رنگ و چرخش رو همچنان کنترل کنیم در ضمن خط سوم باید در متد Update باشه برای این که چرخش تداوم داشته باشه وگرنه اگه در Start قرارش بدید همون اول مقداری که براش تعیین کردید مثلا ۶۰ درجه اون هم قبل از فریم اولِ اجرا میچرخه و بعد ثابت میمونه.
در مورد دو خط ۱۸ و ۱۹ در نوشتههای اول و دوم توضیح دادم که خب کارشون تغییر رنگه. در خط سوم به متغیر چرخش در کامپوننت transform مدلی که در محیط یونیتی به سکریپت مون وصل کردیم داریم میگیم که برای چرخش محور z مقدار وارد شده در Fan Rotation Speed رو استفاده کن یعنی همون مقداری که ما در محیط یونیتی امکان تعیین شو داریم. فایل رو ذخیره کنید و برگردید یونیتی و دکمه Play رو بزنید.
تنها چیزی که کمی عجیبه سرعت چرخش مدل ماست که ممکنه روی سیستم شما سرعتش خیلی زیاد باشه حتی اگه عدد یک باشه. اون هم دلیلش اینه که اینجا سرعت چرخش بر اساس زمان واقعی مثلا یک درجه بر ثانیه نیست بلکه بر اساس قدرت کارت گرافیکه. یعنی اینکه هر قدر کارت گرافیک شما بعد از زدن دکمهی Play فریمهای بیشتری رو بتونه در یک ثانیه نمایش بده سرعت چرخش فن هم به همون نسبت زیاد میشه. این رو با فعال کردن گزینه Stats در پنجره Game میتونید ببینید.
برای حلش باید عدد چرخش ما در چیزی به اسم Time.deltaTime ضرب بشه. حالا این کارش چیه؟ کارش اینه که حساب میکنه ببینه از فریم قبل تا فرم جاری چقدر زمان بر اساس ثانیه گذشته که این عدد معمولن کوچکتر از صفره و ثابت نیست و هر قدر کارت گرافیک قویتر باشه عدد کوچکتره. اگه این عدد رو در مقدار چرخش مثلا یک درجه ضرب کنیم که مدل ما در یک ثانیه یک درجه بچرخه میزان چرخش برای هر فریم مشخص میشه.
rotatingFans.transform.Rotate(0.0f, 0.0f, fanRotationSpeed * Time.deltaTime);
تا اینجا کارهای لازم برای یه مدل رو انجام دادیم ولی برای بیشتر از یه مدل چی؟ یه راه اینه که به تعداد مدلهای صحنه به خطهای کد اضافه کنیم مثلا الان که پنج خط داریم برای سه مدل باقی مونده ۱۵ خط اضافه کنیم و فقط اسمها رو تغییر بدیم. اگه مدلها بیشتر شد چی؟ خب این مشخصه که درست نیست. اینجاست که آرایه یا انگلیسیش Array وارد میشود. کاربردش اینه که به جای یه چیز یه تعداد از یه چیزی رو میشه توش ذخیره کرد مثلا به جای یه سرعت چرخش یه تعداد سرعت چرخش متفاوت داخلش ذخیره کنیم به همین سادگی. برای تغییر متغیرهایی که الان داریم به نوع آرایهای کافیه بعد از تعیین نوع متغیر دو علامت براکت باز و بسته بزارید مثل نمونه زیر:
public GameObject[] rotatingFans;
public Color[] colorField;
public float[] fanRotationSpeed;
با این کار به جای ۱۲ خط فقط برای تعریف متغیرهای چهار مدل فن فقط سه خط داریم ولی حالا اگه دقت کنید میبینید که خطهای داخل Update خطاهایی رو نشون میدن. دلیلش هم اینه که نوع متغیر اونها با متغیرهای بالا متفاوته. فکر کنم درست حدس زدید به اونها هم باید دو تا براکت اضافه کنیم ولی بعد از اضافه کردن براکتها یه خطای کوچیک زیر اونها باقی میمونه. اگه داخل براکتهای این سه خط ۱۸، ۱۹ و ۲۰ مثلا صفر بزاریم مشکل حل میشه ولی چرا؟ دلیلش اینه که وقتی یه متغیر آرایهای مثلا از نوع GameObject تعریف میکنیم متغیر ما اسم هر کدوم از مدلها رو میاد به ترتیبی که بهش نسبت دادیم داخل خودش ذخیره میکنه مثلا اسم مدل اول رو در جایگاه شماره صفر (همیشه اولین جایگاه آرایه با شماره صفر شروع میشه) و اسم مدل دوم رو در جایگاه شماره یک و بقیه رو هم به همین ترتیب جاگذاری میکنه حالا برای اینکه بگیم کدوم مدل یه کاری روش انجا بشه داخل براکتها شماره جایگاه رو مینویسیم. به همین دلیل که وقتی صفر رو میزارید داخل براکتها ویژوال استودیو دیگه خطا نمیده.
var myFanRenderer = rotatingFans[0].GetComponent<Renderer>();
myFanRenderer.material.SetColor("_Color", colorField[0]);
rotatingFans[0].transform.Rotate(0.0f, 0.0f, fanRotationSpeed[0]*Time.deltaTime);
ذخیره کنید و برگردید محیط یونیتی میبینید که متغیرها تغییر کردن و جلوی هر کدوم یه مقدار صفره که در اصل تعداد جایگاهها رو مشخص میکنه. چون چهار مدل، چهار امکان انتخاب رنگ و چهار سرعت چرخش میخوایم داشته باشیم هر سه رو چهار بدید.
بعد از تغییر مقدار صفر به چهار برای هر متغیر چهار جایگاه ایجاد میکنه
در این مرحله مدل فنها رو به قسمت Rotating Fans اضافه کنید و یه رنگ و یه سرعت چرخش دلخواه برای هر کدوم تعیین کنید. دکمه Play رو بزنید. میبینید که تنها یکی از فنها داره کار میکنه. یه راه اینه که مثل نمونه پایین خطها رو کپی کنیم و فقط شماره جایگاهها رو تغییر بدیم.
void Update()
{
var myFanRendererA = rotatingFans[0].GetComponent<Renderer>();
myFanRendererA.material.SetColor("_Color", colorField[0]);
rotatingFans[0].transform.Rotate(0.0f, 0.0f, fanRotationSpeed[0] * Time.deltaTime);
var myFanRendererB = rotatingFans[1].GetComponent<Renderer>();
myFanRendererB.material.SetColor("_Color", colorField[1]);
rotatingFans[1].transform.Rotate(0.0f, 0.0f, fanRotationSpeed[1] * Time.deltaTime);
var myFanRendererC = rotatingFans[2].GetComponent<Renderer>();
myFanRendererC.material.SetColor("_Color", colorField[2]);
rotatingFans[2].transform.Rotate(0.0f, 0.0f, fanRotationSpeed[2] * Time.deltaTime);
var myFanRendererD = rotatingFans[3].GetComponent<Renderer>();
myFanRendererD.material.SetColor("_Color", colorField[3]);
rotatingFans[3].transform.Rotate(0.0f, 0.0f, fanRotationSpeed[3] * Time.deltaTime);
}
هر چند روش خوبی نیست و سه خط کد ما به ۱۲ خط افزایش پیدا کرد ولی میبینید که کار میکنه. ولی روش بهتر چیه؟ روش بهتر استفاده از امکانیه به اسم حلقه فُر یا For Loop که خطهای کد داخل خودش رو به تعداد مشخصی مدام اجرا میکنه. توضیح کامل در مورد لوپها رو اینجا بخونید. حالا مثل نمونهی زیر کدها رو تغییر بدید:
void Update()
{
for (int i = 0; i < rotatingFans.Length; i++)
{
var myFanRenderer = rotatingFans[i].GetComponent<Renderer>();
myFanRenderer.material.SetColor("_Color", colorField[i]);
rotatingFans[i].transform.Rotate(0.0f, 0.0f, fanRotationSpeed[i] * Time.deltaTime, Space.Self);
}
}
فکر کنم توضیح مربوط به لوپها رو خونده باشید پس حالا فقط یه نکته در مورد کدهای بالا بگم. در سه خط ۲۰، ۲۱ و ۲۲ داخل براکتهای جلوی آرایهها این بار به جای شمارهی جایگاه متغیر i رو که داخل پرانتزهای لوپ به عنوان شمارنده تعریف کردیم رو گذاشتیم تا با این کار در هر بار اجرای لوپ مقدار اون تغییر کنه و کدهای اون سه خط روی GameObject بعدی اعمال بشه. مثلا در بار اول اجرای لوپ چون مقدار شمارنده صفره پس تغییر رنگ و تعیین سرعت چرخش بر روی مدل فن شماره یک در محیط یونیتی اعمال میشه این هم به این دلیله که در جایگاه شمارهی صفر اسم مدل فن یک ذخیره شده. بعد شمارنده خودش رو آپدیت میکنه و مقدار اون از صفر به یک تغییر میکنه. این بار نوبت مدل دوم در جایگاه شمارهی یک میرسه. این کار تا آخرین مدل متصل به سکریپت ما ادامه پیدا میکنه.