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

۱۴۱ بازدید
آخرین به‌روزرسانی: ۰۸ شهریور ۱۴۰۲
زمان مطالعه: ۸ دقیقه
تمرین ساخت شیئ در جاوا اسکریپت (بخش دوم) — راهنمای کاربردی

این مقاله در واقع یک آزمون ارزیابی است که در آن انتظار می‌رود شما بتوانید از دموی توپ‌های جهنده که در بخش قبلی با استفاده از Canvas API ساختیم به عنوان یک نقطه شروع استفاده کرده و با بهره‌گیری از مفاهیم اشیای جاوا اسکریپت برخی ویژگی‌های جدید و جذاب را به این توپ‌ها اضافه کنید.

997696

شروع

برای شروع ابتدا کدهای زیر را در فایل‌هایی با نام مشخص شده در یک دایرکتوری جدید روی سیستم خود کپی کنید.

فایل index-finished.html

1<!DOCTYPE html>
2<html>
3  <head>
4    <meta charset="utf-8">
5    <title>Bouncing balls</title>
6    <link rel="stylesheet" href="style.css">
7  </head>
8
9  <body>
10    <h1>bouncing balls</h1>
11    <canvas></canvas>
12
13    <script src="main-finished.js"></script>
14  </body>
15</html>

فایل style.css

1html, body {
2  margin: 0;
3}
4
5html {
6  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
7  height: 100%;
8}
9
10body {
11  overflow: hidden;
12  height: inherit;
13}
14
15h1 {
16  font-size: 2rem;
17  letter-spacing: -1px;
18  position: absolute;
19  margin: 0;
20  top: -4px;
21  right: 5px;
22
23  color: transparent;
24  text-shadow: 0 0 4px white;
25}

فایل main-finished.js

1// setup canvas
2
3var canvas = document.querySelector('canvas');
4var ctx = canvas.getContext('2d');
5
6var width = canvas.width = window.innerWidth;
7var height = canvas.height = window.innerHeight;
8
9// function to generate random number
10
11function random(min,max) {
12  var num = Math.floor(Math.random()*(max-min)) + min;
13  return num;
14}
15
16// define Ball constructor
17
18function Ball(x, y, velX, velY, color, size) {
19  this.x = x;
20  this.y = y;
21  this.velX = velX;
22  this.velY = velY;
23  this.color = color;
24  this.size = size;
25}
26
27// define ball draw method
28
29Ball.prototype.draw = function() {
30  ctx.beginPath();
31  ctx.fillStyle = this.color;
32  ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
33  ctx.fill();
34};
35
36// define ball update method
37
38Ball.prototype.update = function() {
39  if((this.x + this.size) >= width) {
40    this.velX = -(this.velX);
41  }
42
43  if((this.x - this.size) <= 0) {
44    this.velX = -(this.velX);
45  }
46
47  if((this.y + this.size) >= height) {
48    this.velY = -(this.velY);
49  }
50
51  if((this.y - this.size) <= 0) {
52    this.velY = -(this.velY);
53  }
54
55  this.x += this.velX;
56  this.y += this.velY;
57};
58
59// define ball collision detection
60
61Ball.prototype.collisionDetect = function() {
62  for(var j = 0; j < balls.length; j++) {
63    if(!(this === balls[j])) {
64      var dx = this.x - balls[j].x;
65      var dy = this.y - balls[j].y;
66      var distance = Math.sqrt(dx * dx + dy * dy);
67
68      if (distance < this.size + balls[j].size) {
69        balls[j].color = this.color = 'rgb(' + random(0,255) + ',' + random(0,255) + ',' + random(0,255) +')';
70      }
71    }
72  }
73};
74
75// define array to store balls and populate it
76
77var balls = [];
78
79while(balls.length < 25) {
80  var size = random(10,20);
81  var ball = new Ball(
82    // ball position always drawn at least one ball width
83    // away from the adge of the canvas, to avoid drawing errors
84    random(0 + size,width - size),
85    random(0 + size,height - size),
86    random(-7,7),
87    random(-7,7),
88    'rgb(' + random(0,255) + ',' + random(0,255) + ',' + random(0,255) +')',
89    size
90  );
91  balls.push(ball);
92}
93
94// define loop that keeps drawing the scene constantly
95
96function loop() {
97  ctx.fillStyle = 'rgba(0,0,0,0.25)';
98  ctx.fillRect(0,0,width,height);
99
100  for(var i = 0; i < balls.length; i++) {
101    balls[i].draw();
102    balls[i].update();
103    balls[i].collisionDetect();
104  }
105
106  requestAnimationFrame(loop);
107}
108
109
110
111loop();

شرح پروژه

دموی توپ‌های جهنده‌ای که در بخش قبل ساختیم بسیار جالب بود، اما اکنون می‌خواهیم آن را با افزودن یک دایره شیطانی که از سوی کاربر کنترل می‌شود، تعامل‌پذیرتر کنیم. این دایره شیطانی توپ‌هایی که به درونش بیفتد را می‌بلعد. همچنین می‌خواهیم مهارت‌های ساخت شیء شما را از طریق ایجاد یک شیء ()Shape که توپ‌ها و دایره شیطانی از آن به ارث می‌رسند ارزیابی کنیم. در نهایت می‌خواهیم یک شمارنده امتیاز نیز برای ردگیری تعداد توپ‌هایی که روی صفحه باقی‌مانده است بسازیم.

در تصویر زیر ایده‌ای از آن چه قرار است در آخر این مقاله ساخته باشیم، به دست می‌آورید:

اشیای جاوا اسکریپت

همچنین برای این که ایده بهتری از برنامه نهایی داشته باشید می‌توانید به این لینک (+) مراجعه کنید. البته از شما انتظار داریم که سورس کد این مثال را نگاه نکنید و خودتان کار را به پیش ببرید.

مراحل تکمیل پروژه

در این بخش مراحلی که باید انجام دهید را توضیح داده‌ایم.

ایجاد اشیای جدید

قبل از هر چیز باید سازنده ()Ball قبلی را طوری تغییر دهید که به یک سازنده ()Shape تبدیل شود و یک سازنده ()Ball جدید به آن اضافه کنید:

  1. سازنده ()Shape باید به همان روشی که سازنده ()Ball در مقاله قبلی انجام داده بود، به تعریف مشخصه‌های x ،y ،velX و velY بپردازد، اما مشخصه‌های color و size به روش متفاوتی تعریف خواهند شد.
  2. در این سازنده جدید باید مشخصه‌هایی به نام exists وجود داشته باشد که برای ردگیری وجود یا عدم وجود توپ در برنامه استفاده می‌شود. این مشخصه در مواردی که دایره توپ‌ها را می‌بلعد به کار می‌آید و می‌بایست نوع بولی (true/false) داشته باشد.
  3. سازنده ()Ball باید مشخصه‌های x ،y ،velX ،velY و exists را از سازنده ()Shape به ارث ببرد.
  4. همچنین باید مشخصه‌های color و size را به همان روشی که از سوی سازنده ()Ball تعریف شده بود در سازنده ()Shape تعریف کنیم.
  5. به خاطر داشته باشید که prototype و constructor سازنده ()Ball را به طرز متناسبی تنظیم کنید.

تعاریف متدهای ()draw() ،update و ()collisionDetect می‌توانند به همان روشی که در مطلب قبلی تعریف کردیم باقی بمانند.

همچنین باید یک پارامتر جدید به فراخوانی سازنده (...) ()new Ball اضافه کنید. پارامتر exists باید پنجمین پارامتر و دارای مقدار true باشد.

در این مرحله کد را بارگذاری مجدد کنید. عملکرد آن به وسیله شیءهایی که بازطراحی کرده‌ایم، باید مانند دموی قبلی باشد.

تعریف کردن ()EvilCircle

اینک زمان آن رسیده است که شخصیت منفی داستان یعنی ()EvilCircle را طراحی کنیم. در این داستان تنها یک دایره شیطانی به عنوان شخصیت منفی وجود دارد، اما به هر حال باید آن را به وسیله سازنده‌ای که از ()Shape به ارث می‌رسد تعریف کنیم. شما ممکن است بخواهید در ادامه دایره دیگری به برنامه اضافه کنید که کنترل آن دست بازیکن دیگری باشد و یا چند دایره شیطانی داشته باشید که از سوی رایانه کنترل می‌شوند. البته شما احتمالاً نمی‌خواهید همه دنیا را با استفاده از یک دایره شیطانی منفرد ببلعید، اما در این مطلب ارزیابی به همان یک دایره اکتفا می‌کنیم.

سازنده ()EvilCircle باید x ،y ،velX ،velY و exists را از ()Shape به ارث ببرد، اما velX و velY همواره باید برابر با 20 باشند.

این کار با کدی مانند زیر ممکن است:

1Shape.call(this، x، y، 20، 20، exists);

این کد همچنین باید مشخصه‌های خود را به صورت زیر تعریف کند:

  • color — 'white'
  • size — 10

یک بار دیگر به خاطر داشته باشید که باید مشخصه‌هایی که به ارث می‌رسند را به صورت پارامتر در سازنده تعریف کنید و مشخصه‌های prototype و constructor را نیز به طور متناسبی تعیین کنید.

تعریف کردن متدهای ()EvilCircle

()EvilCircle باید چهار متد داشته باشد که هر کدام را در ادامه توضیح داده‌ایم:

متد ()draw

این متد همان منظوری را دنبال می‌کند که متد ()draw شیء ()Ball داشت. یعنی وهله‌ای از شیء را روی بوم ترسیم می‌کند. روش کار آن نیز به صورت مشابه است و از این رو می‌توانید تعریف Ball.prototype.draw را کپی کرده و در ادامه تغییرهای زیر را در آن ایجاد کنید:

ما می‌خواهیم دایره شیطانی تو پر نباشد بلکه صرفاً یک لبه بیرونی داشته باشد. این وضعیت از طریق به‌روزرسانی fillStyle و ()fill به strokeStyle و ()stroke ممکن خواهد بود.

همچنین می‌خواهیم که ضخامت لبه این دایره کمی بیشتر باشد تا بتوان دایره شطانی را راحت‌تر مشاهده کرد. این وضعیت از طریق تنیم مقدار linewidth در جایی پس از فراخوانی ()beginPath ممکن خواهد بود.

متد ()checkBounds

این متد همان کاری را انجام می‌دهد که بخش اول تابع ()update برای شیء ()Ball اجرا می‌کرد، یعنی بررسی می‌کند که آیا دایره شیطانی با لبه صفحه برخورد می‌کند یا نه و از این کار ممانعت می‌کند. در این مورد نیز می‌توانید بخش زیادی از تعریف Ball.prototype.update را کپی کنید، اما چند تغییر را به صورت زیر باید در آن ایجاد نمایید:

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

درون گزاره‌های ()if، اگر تست‌ها مقدار true بازگشت دهند، لازم نیست velX/velY به‌روزرسانی شوند، چون ما می‌خواهیم به جای آن مقدار x/y را تغییر دهیم تا دایره شیطانی با یک جهش خفیف به صفحه بازگردد. افزودن یا کسر کردن مشخصه size دایره شیطانی نیز می‌تواند مفید باشد.

متد ()setControls

این متد یک شنونده رویداد onkeydown به شیء window اضافه می‌کند به طوری که وقتی کلید خاصی روی کیبورد فشرده شود، می‌توانیم دایره را به اطراف جابجا کنیم. قطعه کد زیر را می‌توانید درون تعریف متد قرار دهید:

1var _this = this;
2window.onkeydown = function(e) {
3    if (e.keyCode === 65) {
4      _this.x -= _this.velX;
5    } else if (e.keyCode === 68) {
6      _this.x += _this.velX;
7    } else if (e.keyCode === 87) {
8      _this.y -= _this.velY;
9    } else if (e.keyCode === 83) {
10      _this.y += _this.velY;
11    }
12  }

بنابراین هر زمان که کلیدی فشرده می‌شود، مشخصه keyCode شیء رویداد مورد بررسی قرار می‌گیرد تا مشخص شود کدام کلید فشرده شده است. اگر این کلید یکی از کلیدهای تعریف شده باشد در این صورت دایره شیطانی در جهت‌های چپ/راست/بالا/پایین حرکت می‌کند.

به عنوان نکته جانبی تلاش کنید بدانید که کدهای کلید تعیین شده به کدام کلیدهای کیبورد نگاشت می‌شوند. نکته جانبی دوم نیز این است که بررسی کنید چرا باید کدی مانند زیر داشته باشیم؟

1var _this = this;

و اقدام به تعیین موقعیت درون آن بکنیم؟ راهنمایی: این مورد به دامنه‌بندی مرتبط است.

متد ()collisionDetect

این متد‌ به روشی کاملاً مشابه متد ()collisionDetect در شیء ()BAll عمل می‌کند و از این رو می‌توانید آن کد را به عنوان مبنایی برای این متد جدید کپی کنید. اما چند تفاوت کوچک وجود دارند که در ادامه توضیح می‌دهیم:

در گزاره if بیرونی دیگر نیازی به بررسی این که توپ موجود در مرحله تکرار کنونی همان توپی است که بررسی می‌شود نداریم، چون دیگر چنین توپی وجود ندارد و با یک دایره شیطانی طرف هستیم! در عوض باید بررسی کنیم که آیا توپی که بررسی می‌شود وجود دارد یا نه. اگر توپ وجود نداشته باشد، به این معنی است که قبلاً از سوی دایره بلیعده شده است و از این رو نیازی به بررسی مجدد آن نداریم.

در گزاره if داخلی دیگر نیازی به تغییر دادن رنگ‌های اشیا در زمان تشخیص تصادم نداریم. در عوض باید هر توپی را که با دایره شیطانی برخورد می‌کند به صورت ناموجود علامت‌گذاری کنیم. آیا روش انجام این کار را می‌دانید؟

قرار دادن دایره شیطانی در برنامه

اکنون کار تعریف کردن دایره شیطانی به پایان رسیده است و باید عملاً آن را در صفحه ببینیم. به این منظور باید تغییراتی در تابع ()loop خود ایجاد کنیم.

  • قبل از هر چیز، یک وهله جدید از دایره شیطانی (با تعیین پارامترها) می‌سازیم سپس متد ()setControls آن را فراخوانی می‌کنیم. دقت کنید که انجام این دو کار تنها یک بار صورت می‌گیرد و در هر تکرار حلقه نباید مجدداً اجرا شود.
  • درزمانی که حلقه شروع به بررسی همه توپ‌ها و فراخوانی تابع‌های ()draw() ،update و ()collisionDetect برای هر کدام از آن‌ها می‌کند، باید مطمئن شویم که این کار صرفاً برای توپ‌های موجود یعنی آن‌هایی که قبلاً از سوی دایره شیطانی بلعیده نشده‌اند انجام می‌یابد.
  • متدهای ()draw() ،update و ()collisionDetect وهله دایره شیطانی را در هر بار تکرار حلقه فراخوانی کنید.

پیاده‌سازی شمارنده امتیاز

برای پیاده‌سازی بخش شمارنده امتیاز باید مراحل زیر را طی کنید:

  • در فایل HTML یک عنصر <p> درست زیر عنصر <h1> اضافه کنید که شامل متن « :Ball count» باشد.
  • در فایل CSS قاعده زیر را به انتهای فایل اضافه کنید:
1p {
2  position: absolute;
3  margin: 0;
4  top: 35px;
5  right: 5px;
6  color: #aaa;
7}
  • در فایل جاوا اسکریپت، به‌روزرسانی‌های زیر را اجرا کنید:
    • متغیری بسازید که یک ارجاع به پاراگراف نگهداری کند.
    • شماره تعداد توپ‌های روی صفحه را به هر نوعی که دوست دارید حفظ کنید.
    • هر بار که توپی به صفحه اضافه می‌شود، شماره را افزایش دهید و آن را روی صفحه به‌روزرسانی کنید.
    • هر بار که توپی از سوی دایره شیطانی بلعیده می‌شود، شماره را کاهش داده و آن را روی صفحه به‌روزرسانی کنید.

نکات و سرنخ‌ها

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

سخن پایانی

اگر این بخش ارزیابی را به صورت قسمتی از این دوره کامل از سلسله مقالات آموزش جاوا اسکریپت مطالعه می‌کنید، اینک باید بتوانید کار خود را در اختیار یک مربی قرار دهید تا به شما نمره بدهد. اگر این سری مقالات را به صورت خودآموز دنبال می‌کنید، می‌توانید کدهایی که نوشته‌اید را با نسخه کامل شده (+) مقایسه کنید و کار خود را مورد ارزیابی قرار دهید.

برای مطالعه قسمت بعدی این مجموعه مطلب آموزشی می‌توانید روی لینک زیر کلیک کنید:

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۰ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
developer.mozilla
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *