تمرین ساخت شیئ در جاوا اسکریپت (بخش اول) – راهنمای کاربردی


در بخشهای قبلی این سری مقالات راهنمای جامع جاوا اسکریپت با مبانی نظری شیئهای جاوا اسکریپت و جزییات ساختار آنها آشنا شدیم و بدین ترتیب یک نقطه شروع قابل اتکا برای آغاز به دست آوریم. در این مقاله وارد یک تمرین عملی میشویم و تجربه عملی بیشتری در زمینه ساخت شیئ در جاوا اسکریپت به دست میآوریم که منجر به ساخت شیئی جالب و رنگارنگ خواهد شد.
برای مطالعه قسمت قبلی این مجموعه مطلب آموزشی میتوانید به پست کار با JSON در جاوا اسکریپت — راهنمای کاربردی مراجعه کنید.
پیشنیازهای ساخت شیئ در جاوا اسکریپت
- سواد مقدماتی رایانه
- درک ابتدایی از HTML و CSS
- آشنایی با مبانی جاوا اسکریپت و مقدماتی شیئگرایی در آن
هدف از مطالعه این مطلب، کسب تجربه عملی برای کار با اشیا و تکنیکهای شیئگرایی در دنیای واقعی است.
جنباندن توپها
در این مقاله، یک دمو از «توپهای جنبنده» میسازیم تا نشان دهیم که اشیا در جاوا اسکریپت تا چه میزان میتوانند مفید باشند. توپهای کوچک ما روی صفحه به حرکت درمیآیند و زمانی که با هم برخورد کنند رنگشان تغییر پیدا میکند. نمونه کامل شده این پروژه چیزی مانند تصویر زیر خواهد بود:
در این مثال از Canvas API برای ترسیم توپها روی صفحه استفاده میکنیم و از API دیگری به نام requestAnimationFrame نیز برای متحرکسازی کل نمایش استفاده خواهیم کرد. در هر حال لازم نیست هیچ دانش قبلی از این API-ها داشته باشید. امیدواریم زمانی که این مقاله را به پایان میبرید، علاقهمند باشید این موضوعات را بیشتر کاوش کنید. در این مسیر از برخی اشیای عالی استفاده میکنیم و چند تکنیک زیبا مانند روش برخورد توپ با دیوار و جهش یافتن یا بررسی این که با هم برخورد کردهاند یا نه که به نام تشخیص تصادم نامیده میشود را معرفی خواهیم کرد.
سرآغاز
در آغاز کار کپیهای محلی از فایلهای زیر روی سیستم خود ایجاد کنید.
فایل index.html
این فایل یک سند کاملاً ساده HTML است که دارای یک عنصر <a>، یک عنصر <canvas> برای ترسیم توپها روی آن و عناصری برای بهکارگیری CSS و جاوا اسکریپت است.
فایل style.css
این فایل حاوی سبکهای سادهای است که به طور عمده برای سبکبندی موقعیت <h1> و رها شدن از شر نوارهای اسکرول یا حاشیه پیرامون صفحه (جهت زیباتر ساختن آن) استفاده میشود.
فایل main.js
در این فایل برخی کدهای جاوا اسکریپت قرار دارند که عنصر <canvas> را تنظیم میکنند و یک تابع عمومی ارائه میکند که از آن استفاده خواهیم کرد. بخش نخست اسکریپت مانند زیر است:
این اسکریپت ارجاعی به عنصر <canvas> ایجاد میکند و سپس روش ()getContext را روی آن فراخوانی کرده و ساختاری ارائه میدهد که میتوان از آن برای شروع رسم استفاده کنیم. متغیر حاصل (ctx) شیئی است که به صورت مستقیم ناحیه رسم بوم را نمایش میدهد و امکان ترسیم اشکال 2 بعدی را روی آن فراهم میسازد.
سپس متغیرهایی را که width و height نام دارند، تنظیم میکنیم و به ترتیب عرض و ارتفاع عنصر بوم را روی آنها تنظیم میکنیم. این عناصر با مشخصههای canvas.width و canvas.height نمایش مییابند و با عرض و ارتفاع صفحه مرورگر متناسب هستند. مقادیر اخیر را میتوان از مشخصههای Window.innerWidth و Window.innerHeight به دست آورد.
در ادامه خواهید دید که چندین آرگومان را به هم زنجیر میکنیم تا همه متغیرها سریعتر تعیین شوند. بخش آخر اسکریپت اولیه مانند زیر است:
این تابع دو عدد را به عنوان آرگومان میگیرد و یک عدد تصادفی در بازهای بین آن دو بازگشت میدهد.
مدلسازی یک توپ در برنامه
برنامه ما شامل توپهای زیادی خواهد بود که روی صفحه به اطراف میجهند. از آنجا که این توپها رفتاری نسبتاً مشابه دارند بهتر است که آنها را با یک شیئ بازنمایی کنیم.
کار خود را با افزودن سازنده زیر به انتهای کد قبلی آغاز میکنیم.
در این تابع، پارامترهایی داریم که مشخصههای هر توپ را در برنامه ما مشخص میسازند:
- مختصات x و y: مختصات افقی و عمودی، مکانی که توپ در ابتدای آغاز به کار برنامه روی صفحه قرار دارد را تعیین میکند. این مقادیر میتوانند بین 0 (گوشه چپ-بالای صفحه) تا عرض و ارتفاع صفحه مرورگر (گوشه راست-پایین صفحه) متغیر باشند.
- سرعت افقی و عمودی (velX و velY): هر توپ دارای یک سرعت افقی و عمودی است. این مقادیر در زمان شروع به متحرکسازی توپها به مختصات x/y توپ اضافه میشوند تا توپها در هر فریم به این میزان جابجا شوند.
- Color؛ هر توپ یک رنگ دارد.
- Size: هر توپ یک اندازه دارد. این مقدار شعاع توپ را برحسب پیکسل تعیین میکند.
بدین ترتیب همه مشخصههای مورد نیاز توپها تعیین میشوند؛ اما متدها چطور؟ ما باید کاری کنیم که توپهایمان در برنامه واقعاً کاری انجام دهند.
رسم توپ
ابتدا متد ()draw زیر را به پروتوتایپ ()Ball اضافه کنید:
با استفاده از این تابع، میتوانیم به توپ خود بگوییم که با فراخوانی یک سری از اعضای context مربوط به canvas دوبعدی که قبلاً تعریف کردیم (ctx) خود را روی صفحه رسم کند. این context مانند یک صفحه کاغذ است و اینک میخواهیم به قلم خود دستور بدهیم که چیزی را روی آن رسم کند.
- ابتدا از ()beginPath برای بیان این که میخواهیم یک شکل روی کاغذ رسم کنیم بهره میگیریم.
- سپس از fillStyle برای تعریف رنگی که قرار است با آن ترسیم شود استفاده میکنیم و آن را برابر با مشخصه color قرار میدهیم.
- سپس از متد ()arc برای رسم یک شکل کمان روی کاغذ استفاده میکنیم. پارامترهای این متد به شرح زیر هستند:
- مکان x و y مرکز کمان هستند که بر اساس مشخصههای x و y توپ تعیین میشوند.
- شعاع کمان بر اساس مشخصه size توپ تعیین میشود.
- دو پارامتر آخر در واقع درجات آغازین و انتهایی کمانی از دایره هستند که میخواهیم رسم کنیم. در اینجا ما مقدار 0 و 2 * PI را ارسال میکنیم که معادل 360 درجه برحسب رادیان است. بدین ترتیب یک دایره کامل به دست میآوریم. اگر تنها 1 * PI را تعیین کرده باشید، یک نیمدایره (180 درجه) به دست میآورید.
- در آخر از متد ()fill استفاده میکنیم که در واقع اعلام میکند «ترسیم مسیر آغاز شده با متد ()beginPath را با پر کردن شکل با رنگ تعیین شده قبلی در fillStyle تکمیل کن.»
شما هم اینک میتوانید شروع به آزمودن شیئ خود بکنید. کدی را که تا به اینجا نوشتهاید ذخیره کنید و فایل HTML را در یک مرورگر بارگذاری کنید.
کنسول جاوا اسکریپت مرورگر را باز کرده و سپس صفحه را رفرش کنید به طوری که اندازه بوم به اندازه Viewport کوچکترشده در زمان باز کردن کنسول درآید.
کد زیر را بنویسید تا یک وهله جدید از شیئ توپ ایجاد شود:
اعضای آن را به صورت زیر فراخوانی کنید:
هنگامی که خط آخر را وارد میکنید باید ببینید که توپ شروع به رسم کردن خود روی بوم میکند.
بهروزرسانی دادههای توپ
ما میتوانیم توپ را در موقعیت خود رسم کنیم، اما برای این که بتوانیم در ادامه شروع به متحرکسازی آن بکنیم، باید یک تابع بهروزرسانی داشته باشیم. کد زیر را در انتهای فایل جاوا اسکریپت خود وارد کرد و یک متد ()update به پروتوتایپ ()Ball اضافه کنید.
چهار بخش نخست تابع به بررسی این نکته میپردازند که آیا توپ به لبههای بوم رسیده است یا نه. اگر چنین اتفاقی افتاده باشد، جهت سرعت مرتبط معکوس میشود تا توپ در مسیر معکوس شروع به حرکت کند. برای مثال، اگر توپ به سمت بالا حرکت میکند (velY مثبت) در این صورت سرعت عمودی طوری تغییر پیدا میکند که شروع به حرکت به سمت پایین بکند، یعنی velY منفی میشود.
بنابراین چهار حالت زیر را داریم:
- بررسی میکنیم که آیا مختصات x بزرگتر از عرض بوم است، یعنی آیا توپ به لبه راست رسیده است یا نه.
- بررسی میکنیم این که آیا مختصات x کمتر از 0 است، یعنی آیا توپ به لبه سمت چپ رسیده است یا نه.
- بررسی میکنیم این که آیا مختصات y بزرگتر از ارتفاع بوم است، یعنی آیا توپ به لبه پایینی رسیده است یا نه.
- بررسی میکنیم که آیا مختصات y کوچکتر از ارتفاع بوم است، یعنی آیا توپ به لبه فوقانی رسیده است یا نه.
در هر حالت، مشخصه size توپ را در محاسبات بررسی میکنیم، زیرا مختصات x/y در مرکز توپ هستند؛ اما ما میخواهیم مختصات لبه پیرامونی توپ را بررسی کنیم. در واقع ما نمیخواهیم قبل از این که توپ از دیوار بازجهد، نیمی از آن از دیوار گذشته باشد.
دو خط آخر مقدار velx را به مختصات x و مقدار vely را به مختصات y اضافه میکنند. بدین ترتیب توپ هر بار که متد فراخوانی میشود جابجا میشود. کار ما تا به اینجا پایان یافته است، در بخش بعدی به متحرکسازی توپها میپردازیم.
متحرکسازی توپ
اینک بخش جالب داستان فرا رسیده است.
اکنون قصد داریم شروع به افزودن توپها به بوم بکنیم و آنها را به حالت متحرک درآوریم. ابتدا باید جایی را بسازیم که همه توپها در آن ذخیره شوند و سپس آنها را روی صفحه پر کنیم. کد زیر این کار را انجام خواهد داد. این کد را به انتهای کدهای فعلی اضافه کنید:
همه برنامههایی که به متحرکسازی اشیا میپردازند، به طور کلی دارای یک حلقه انیمیشن هستند که به منظور بهروزرسانی اطلاعات در برنامه استفاده میشود و سپس نتیجه نهایی هر فریم از انیمیشن رندر میشود. این طرز کار اغلب بازیها و دیگر برنامهها از این دست است. حلقه while یک وهله جدید از ()ball ما را با استفاده از مقادیر تصادفی میسازد. خود مقادیر تصادفی به وسیله تابع ()random ایجاد میشوند و سپس از ()push برای ارسال آنها به آرایه نهایی توپها استفاده میشود، اما این اتفاق تنها در زمانی رخ میدهد که تعداد توپها کمتر از 25 باشد.
بنابراین هنگامی که توپهای روی صفحه به عدد 25 برسند دیگر توپ جدیدی ایجاد نمیشود. شما میتوانید این عدد را در بخش balls.length < 25 تغییر دهید تا توپهای بیشتری روی صفحه داشته باشید. بسته به این که رایانه یا مرورگر شما چه مقدار توان محاسباتی دارد میتوانید توپ به صفحه اضافه کنید؛ اما تعیین چند هزار توپ موجب کُند شدن نسبی انیمیشن خواهد شد.
کد زیر را به انتهای کدهای فعلی خود اضافه کنید:
در تابع ()loop کارهای زیر را انجام دهید:
رنگ fill بوم را به صورت سیاه نیمه شفاف تعیین کنید و سپس یک مستطیل از آن رنگ را با استفاده از ()fillrect روی عرض و ارتفاع بوم رسم کنید. چهار پارامتر مختلف این بوم در واقع نقاط ابتدایی و عرض و ارتفاع بوم را مشخص میسازند. منظور از رسم این مستطیل، پوشش دادن فریم قبلی پیش از رسم فریم بعدی است. اگر این کار را انجام ندهید، به جای تماشای توپهایی که روی صفحه حرکت میکنند، کِرمهایی را خواهید دید که پیرامون صفحه میخزند! رنگ این مستطیل نیمه شفاف به صورت (rgba(0،0،0،0.25 انتخاب شده است تا امکان دیدن نسبی چند فریم قبلی از خلال فریم فعلی وجود داشته باشد و به این ترتیب رد حرکت توپها در زمان حرکت بر جای بماند. اگر این مقدار را از 0.25 به 1 تغییر دهید، این رد حرکت را دیگر نخواهید دید. با تغییر دادن این مقدار میتوانید تأثیر آن را در شرایط مختلف مشاهده کنید.
حلقهای روی همه توپها در آرایه balls تعریف کنید و تابع ()draw و ()update را برای هر یک از آنها روی صفحه اجرا کنید. سپس بهروزرسانیهای لازم را برای موقعیت و سرعت هر توپ در فریم بعدی انجام دهید.
تابع را بار دیگر با استفاده از متد ()requestAnimationFrame اجرا کنید. زمانی که این تابع به صورت پیوسته اجرا شود و همان نام تابع را به صورت مرتب ارسال کند، تابع به تعداد زمان تعیین شده اجرا میشود و انیمیشن روانی ایجاد میکند. این کار عموماً به روش بازگشتی اجرا میشود، یعنی تابع هر بار که اجرا شود، خود را فراخوانی میکند و بدین ترتیب برای همیشه به صورت مکرر اجرا میشود.
در گام آخر، کد زیر را به انتهای خطوط موجود کد خود اضافه کنید. بدین ترتیب کافی است تابع را یک بار در زمان آغاز انیمیشن فراخوانی کنیم:
بدین ترتیب مقدمات کار به پایان میرسد. فایل را ذخیره کرده و صفحه را رفرش کنید تا توپهای جهنده خود را بیازمایید.
افزودن تشخیص تصادم
اینک برای سرگرمی بیشتر، یک روش تشخیص تصادم به برنامه خود اضافه میکنیم تا توپهایمان بدانند که چه زمانی با توپ دیگر برخورد میکنند.
قبل از هر چیز، تعریف متد زیر را در زیر بخشی که متد ()update را تعریف کردهاید، اضافه کنید. منظور ما بلوک Ball.prototype.update است.
این متد کمی پیچیده است و اگر آن را به طور کامل درک نمیکنید، جای نگرانی نیست. توضیح طرز کار آن به زبان ساده چنین است: برای هر توپ باید یک بررسی در مورد همه توپهای دیگر انجام دهیم و ببینیم آیا با توپ کنونی برخورد دارند یا نه. به این منظور یک حلقه for دیگر تعریف میکنیم تا روی همه توپها در آرایه []balls اجرا شود.
در ابتدای حلقه for از گزاره if استفاده میکنیم تا بفهمیم آیا توپ کنونی که حلقه روی آن قرار دارد، همان توپی است که در حال بررسی کردنش هستیم یا نه. بدیهی است که نمیخواهیم بدانیم آیا توپی با خودش برخورد کرده است یا نه. به این منظور بررسی میکنیم که آیا توپ کنونی (یعنی توپی که متد collisionDetect آن فراخوانی شده است) همان توپی است که حلقه روی آن قرار دارد (یعنی توپی که در تکرار فعلی حلقه قرار است بررسی شود). سپس از عملگر (!) برای منفی ساختن بررسی استفاده میکنیم و بنابراین کد درون گزاره if تنها زمانی اجرا میشود که این دو توپ برابر نباشند.
سپس از یک الگوریتم رایج برای بررسی وقوع تصادم بین دو دایره استفاده میکنیم. در واقع میخواهیم بدانیم آیا مساحت دو دایره همپوشانی دارند یا نه. برای کسب اطلاعات بیشتر در مورد این محاسبه به این مقاله (+) مراجعه کنید.
اگر یک تصادم تشخیص داده شود، کد درون گزاره if درونیتر اجرا میشود. در این حالت مشخصه color هر دو دایره را به صورت یک مقدار تصادفی جدید تغییر میدهیم. البته ما میتوانیم کار پیچیدهتری مانند شبیهسازی واقعگرایانه حالت برخورد دو توپ با هم را نیز اجرا کنیم؛ اما پیادهسازی چنین مثالی بسیار پیچیدهتر است. توسعهدهندگان برای پیادهسازی چنین شبیهسازیهای فیزیکی از یک کتابخانه بازی یا فیزیک مانند PhysicsJS ،matter.js ،Phaser و غیره استفاده میکنند.
همچنین باید این متد را روی هر فریم انیمیشن فراخوانی کنید. کد زیر را در زیر خط ;()balls[i].update اضافه کنید:
فایل را ذخیره کرده و صفحه را رفرش کنید تا تغییرات رنگ توپها را در حین تصادم مشاهده کنید.
نکته: اگر مشکلی در اجرای فایل خود دارید میتوانید نسخه نهایی را در ادامه مشاهده کنید و اشکال خود را بیابید:
سخن پایانی
امیدواریم از نوشتن این مثال واقعی در مورد توپهای جهندهی تصادفی با استفاده از شیئهای مختلف و تکنیکهای گوناگون شیئگرایی که آنها را در طی این سری مقالات آموزش جامع جاوا اسکریپت معرفی کردهایم، لذت برده باشید. بنابراین تجربه عملی مفیدی در زمینه استفاده از شیئها در یک مثال از دنیای واقعی کسب کردهاید.
برای مطالعه بخش بعدی این سری مفالات آموزشی روی لینک زیر کلیک کنید:
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- آموزش JavaScript ES6 (جاوا اسکریپت)
- مجموعه آموزشهای برنامهنویسی
- جاوا اسکریپت چیست؟ — به زبان ساده
- آموزش جاوا اسکریپت — مجموعه مقالات جامع وبلاگ فرادرس
==