كيف تصمم لعبة جوال باستخدام HTML5
هل أنت من المهتمين بتطوير ألعاب الجوال؟ هل تريد طريقه بسيطة لدخول هذا المجال؟ لست مضطرا أن تكون خبيرا بلغات Java أو Objective-C … HTML5 + JavaScript مع شوية رياضيات تكفي لأن تصمم لعبتك الاولى.
فلنبدأ على بركة الله…
في نهاية هذا المقال ستكون قادر على:
– فهم وبشكل مبسط مبعض مبادئ برمجة الألعاب.
– التعرف على بعض المفاهيم والمصطلحات الخاصة بهذا المجال.
– بناء اللعبه التالية: يمكنك تجربتها من هنا
ولكن لماذا HTML5؟
ببساطه يمكننا القول ان HTML5 تحقق الهدف البرمجي المنشود “اكتب الكود مرة واحدة … يعمل في كل مكان” write once … run anywhere
أي تعمل على أجهزة الموبايل بنفس الجودة التي تعمل بها على أجهزة الحواسيب وباستخدام نفس الكود.
إليك بعض الأمثله عن العاب تم انشاؤها باستخدام HTML5
أمور يجب الانتباه لها:
قبل أن نبدأ يجب ان نكون على نهتم بالأمور التالية:
أولاً – الأداء:
متصفحات الانترنت بالموبايلات و بالرغم من التطوير السريع ليست داعمه بشكل كبير لمحركات الجافا سكريبت بما فيها IOS6 و Chrome beta.
ثانياً – الدقة:
ما يقلقنا هنا هو وجود تنوع كبير في الدقة لأجهزة الاندرويد والايفون والايباد.
ثالثاً – الصوت:
يجب الاشارة هنا إلى أن دعم الصوت في متصفحات الموبايل ضعيف.
خطوات العمل
ببساطه اللعبه التي سنعمل عليها تتلخص بما يلي: يجب على اللاعب أن يقوم بإصابة الفقاعات قبل أن تصل لقمة الشاشة.
سنقوم بتقسيم العمل إلى الخطوات التاليه:
-
العمل على ملائمه لعبتنا مع قياس شاشات الموبايل.
-
نظرة سريعه على convas المفيده للرسم على الشاشة.
-
التعامل مع أحداث اللمس.
-
صناعه حلقة اللعبة الأساسية.
-
التعرف على مصطلح “entities”.
-
اضافة كشف التصادم والتعامل مع بعض العلاقات الرياضيه.
أولاً – العمل على ملائمة لعبتنا مع قياس شاشات الموبايل:
كما اشرنا سابقا، هناك تنوع كبير في مقاسات الأجهزة الذكيه هذا يعني يجب علينا أن نلائم واجهة عرضنا convas مع شاشات الموبايلات viewport.هذه الملائمة ربما تسبب تراجع جودة الصورة.
يمكن أن نتعامل مع هذه المشكلة بخدعة بسيطة وهي جعل convas صغيرة مما يزيد في الأداء.
هذا الكود سيكون اساس عملنا:
<!DOCTYPE HTML>
<
html
lang
="
en
">
<
head
>
<
meta
charset
="
UTF-8
">
<
meta
name
="
viewport
"
content
="
width
=
device-width,
user-scalable
=
no, initial-scale
=
1, maximum-scale
=
1, user-scalable
=
0
"
/>
<
meta
name
="
apple-mobile-web-app-capable
"
content
="
yes
"
/>
<
meta
name
="
apple-mobile-web-app-status-bar-style
"
content
="
black-translucent
"
/>
<
style
type
="
text/css
">
body
{
margin
:
0
;
padding
:
0
;
background
:
#000
;}
canvas
{
display
:
block
;
margin
:
0 auto
;
background
:
#fff
;
}
</
style
>
</
head
>
<
body
>
<
canvas
>
</
canvas
>
<
script
>
// all the code goes here
</
script
>
</
body
>
</
html
>
ايضاحات حول الكود:
– viewport meta تخبر متصفحات الموبايل بتعطيل امكانية تغيير المستخدم او اللاعب للقياس و رسم حجم كامل رغم تصغير او تكبير الصورة.
– Apple meta تسمح للعبة أن تحفظ bookmarked.
انظر لما يلي:
// namespace our game
var
POP
=
{
// set up some initial values
WIDTH
:
320
,
HEIGHT
:
480
,
// we'll set the rest of these
// in the init function
RATIO
:
null
,
currentWidth
:
null
,
currentHeight
:
null
,
canvas
:
null
,
ctx
:
null
,
init
:
function
()
{
// the proportion of width to height
POP
.
RATIO
=
POP
.
WIDTH
/
POP
.
HEIGHT
;
// these will change when the screen is resized
POP
.
currentWidth
=
POP
.
WIDTH
;
POP
.
currentHeight
=
POP
.
HEIGHT
;
// this is our canvas element
POP
.
canvas
=
document
.
getElementsByTagName
(
'canvas'
)[
0
];
// setting this is important
// otherwise the browser will
// default to 320 x 200
POP
.
canvas
.
width
=
POP
.
WIDTH
;
POP
.
canvas
.
height
=
POP
.
HEIGHT
;
// the canvas context enables us to
// interact with the canvas api
POP
.
ctx
=
POP
.
canvas
.
getContext
(
'2d'
);
// we're ready to resize
POP
.
resize
();
},
resize
:
function
()
{
POP
.
currentHeight
=
window
.
innerHeight
;
// resize the width in proportion
// to the new height
POP
.
currentWidth
=
POP
.
currentHeight
*
POP
.
RATIO
;
// this will create some extra space on the
// page, allowing us to scroll past
// the address bar, thus hiding it.
if
(
POP
.
android
||
POP
.
ios
)
{
document
.
body
.
style
.
height
=
(
window
.
innerHeight
+
50
)
+
'px'
;
}
// set the new canvas style width and height
// note: our canvas is still 320 x 480, but
// we're essentially scaling it with CSS
POP
.
canvas
.
style
.
width
=
POP
.
currentWidth
+
'px'
;
POP
.
canvas
.
style
.
height
=
POP
.
currentHeight
+
'px'
;
// we use a timeout here because some mobile
// browsers don't fire if there is not
// a short delay
window
.
setTimeout
(
function
()
{
window
.
scrollTo
(
0
,
1
);
},
1
);
}
};
window
.
addEventListener
(
'load'
,
POP
.
init
,
false
);
window
.
addEventListener
(
'resize'
,
POP
.
resize
,
false
);
ايضاحات حول الكود:
-
خلقنا namespace للعبتنا واسميناها POP.
-
القسم الاول للتصريح عن المتحولات معظمهم من أجل convas و ctx
-
convas اداة موجوده ضمن HTML5 خاصة لرسم العناصر.
-
CTX أداة خاصة بالتعامل مع convas عن طريق Javascript convas API.
-
POP.init لنتعامل بشكل مبدئي مع عنصر convas ونحدد ابعادها 480*320.
-
resize يتم استدعاء هذا التابع عند أحداث التحميل واعادة التحجيم.
-
Convas لاتزال الى الآن بنفس الأبعاد ولكن سيتم تغيير الابعاد باستخدام CSS.
حاول تغيير ابعاد الصفحه بمتصفح الانترنت (اضغط ctrl مع +او – في أغلب المتصفحات) وسوف تر كيف تتلائم convas.
اذا كنت تجرب على جهاز موبايل سوف تلاحظ وجود شريط العنوان.بامكانك معالجة هذا الموضوع باضافة بكسلات اضافية للمستند كما يلي:
// we need to sniff out Android and iOS
// so that we can hide the address bar in
// our resize function
POP
.
ua
=
navigator
.
userAgent
.
toLowerCase
();
POP
.
android
=
POP
.
ua
.
indexOf
(
'android'
)
>
-
1
?
true
:
false
;
POP
.
ios
=
(
POP
.
ua
.
indexOf
(
'iphone'
)
>
-
1
||
POP
.
ua
.
indexOf
(
'ipad'
)
>
-
1
)
?
true
:
false
;
// this will create some extra space on the
// page, enabling us to scroll past
// the address bar, thus hiding it.
if
(
POP
.
android
||
POP
.
ios
)
{
document
.
body
.
style
.
height
=
(
window
.
innerHeight
+
50
)
+
'px'
;
}
ثانيا – نظرة سريعه على convas المفيده للرسم على الشاشة.
الآن يجب أن نحجم convas (عنصر الرسم الذي نعمل عليه) بحجم viewport، دعنا نضيف امكانية لرسم بعض الأشكال.
فيما يلي سنضيف كلاس Draw الذي سيسمح لنا بمسح الشاشة، رسم مستطيل ودائرة وإضافة نص.
نضيف الكود التالي إلى نهاية المكان المخصص لكود Javascript
// abstracts various canvas operations into
// standalone functions
POP
.
Draw
=
{
clear
:
function
()
{
POP
.
ctx
.
clearRect
(
0
,
0
,
POP
.
WIDTH
,
POP
.
HEIGHT
);
},
rect
:
function
(
x
,
y
,
w
,
h
,
col
)
{
POP
.
ctx
.
fillStyle
=
col
;
POP
.
ctx
.
fillRect
(
x
,
y
,
w
,
h
);
},
circle
:
function
(
x
,
y
,
r
,
col
)
{
POP
.
ctx
.
fillStyle
=
col
;
POP
.
ctx
.
beginPath
();
POP
.
ctx
.
arc
(
x
+
5
,
y
+
5
,
r
,
0
,
Math
.
PI
*
2
,
true
);
POP
.
ctx
.
closePath
();
POP
.
ctx
.
fill
();
},
text
:
function
(
string
,
x
,
y
,
size
,
col
)
{
POP
.
ctx
.
font
=
'bold '
+
size
+
'px Monospace'
;
POP
.
ctx
.
fillStyle
=
col
;
POP
.
ctx
.
fillText
(
string
,
x
,
y
);
}
};
ايضاحات حول الكود:
-
Draw يملك طرق لتنظيف الشاشة ورسم المستطيلات والدوائر والنص.
-
من أهم فوائد تجريد هذه العمليات جعلنا غير مضطرين لتذكر استدعاء convas API مثلا يمكننا رسم دائرة بسطر واحد بدل بخمسة أسطر.
دعنا نقوم باختبار بسيط، اضف الكود السابق بنهاية التابع POP.init ويجب أن تر الأشكال مرسومه باداة convas.
// include this at the end of POP.init function
POP
.
Draw
.
clear
();
POP
.
Draw
.
rect
(
120
,
120
,
150
,
150
,
'green'
);
POP
.
Draw
.
circle
(
100
,
100
,
50
,
'rgba(255,0,0,0.5)'
);
POP
.
Draw
.
text
(
'
Welcome to APPCODE.me
'
,
100
,
100
,
10
,
'#000'
);
ايضاحات حول الكود:
-
بالسطر الأول نستدعي تابع مسح الشاشة.
-
بالسطر الثاني نستدعي تابع لاضافة مستطيل أخضر اللون.
-
بالسطر الثالث نستدعي تابع لاضافة دائرة حمراء اللون.
-
بالسطر الرابع نستدعي تابع لاضافة نص “Welcome to APPCODE.me”.
ثالثا – التعامل مع أحداث اللمس:
كوجود دعم لأحداث click لدينا طرق تدعمها متصفحات الموبايل للتعامل مع أحداث اللمس touch.
أحداث اللمس touchstart و touchmove , touchend تحتوي مصفوفه اللمس والتي يحتوي كل عنصر منها على مكان اللمس وبياناته بحيث نصل للمسة الأولى من خلال e.touch[0] .
أضف الكود التالي لتابع POP.init الذي يقوم باضافة مستمعي هذه أحداث اللمس.
// listen for clicks
window
.
addEventListener
(
'click'
,
function
(
e
)
{
e
.
preventDefault
();
POP
.
Input
.
set
(
e
);
},
false
);
// listen for touches
window
.
addEventListener
(
'touchstart'
,
function
(
e
)
{
e
.
preventDefault
();
// the event object has an array
// named touches; we just want
// the first touch
POP
.
Input
.
set
(
e
.
touches
[
0
]);
},
false
);
window
.
addEventListener
(
'touchmove'
,
function
(
e
)
{
// we're not interested in this,
// but prevent default behaviour
// so the screen doesn't scroll
// or zoom
e
.
preventDefault
();
},
false
);
window
.
addEventListener
(
'touchend'
,
function
(
e
)
{
// as above
e
.
preventDefault
();
},
false
);
ربما نلاحظ بأن الكود بالأعلى يمرر بيانات الحدث لمثال input. دعنا نقوم بمايلي:
// + add this at the bottom of your code,
// before the window.addEventListeners
POP
.
Input
=
{
x
:
0
,
y
:
0
,
tapped
:
false
,
set
:
function
(
data
)
{
this
.
x
=
data
.
pageX
;
this
.
y
=
data
.
pageY
;
this
.
tapped
=
true
;
POP
.
Draw
.
circle
(
this
.
x
,
this
.
y
,
10
,
'red'
);
}
};
الآن يجب أن تظهر دائرة حمراء مكان نقر المؤشر يمكنك تجريب ذلك.
لدينا مشكلة وهي عدم ظهور هذه الدوائر صحيح؟
السبب ببساطه اختلاف القياس بين convas و الشاشة ولتلافي هذه المشكلة يمكن أن نقوم بالخطوات التاليه:
– أضف الكود التالي الى بداية البرنامج مع قسم الاعلان عن المتحولات.
// let's keep track of scale
// along with all initial declarations
// at the start of the program
scale
:
1
,
// the position of the canvas
// in relation to the screen
offset
=
{
top
:
0
,
left
:
0
},
– نضيف مايلي إلى التابع الخاص بالتحجيم. وبالضبط بعد تحديد طول وعرض convas.
// add this to the resize function.
POP
.
scale
=
POP
.
currentWidth
/
POP
.
WIDTH
;
POP
.
offset
.
top
=
POP
.
canvas
.
offsetTop
;
POP
.
offset
.
left
=
POP
.
canvas
.
offsetLeft
;
– نضيف مايلي لطريقة set الموجوده في POP.Input:
this
.
x
=
(
data
.
pageX
-
POP
.
offset
.
left
)
/
POP
.
scale
;
this
.
y
=
(
data
.
pageY
-
POP
.
offset
.
top
)
/
POP
.
scale
;
رابعاً – صناعة حلقة اللعبة الأساسية:
الألعاب عادة تدور بالخطوات التالية:
– جلب مدخلات المستخدم.
– نحديث التصادمات وخصائص اللعبة.
– إرسال المخرجات على الشاشة.
– كرر ماسبق.
مبدأ عم الألعاب بشكل عام هي رسم واجهات Frames متعدده مع الزمن حتى يبدو الأمر وكأنه مستمر.
سنضيف الكود التالي الذي يقوم بتحديث الواجهات Frame الى بداية كود الجافا سكريبت.
// http://paulirish.com/2011/requestanimationframe-for-smart-animating
// shim layer with setTimeout fallback
window
.
requestAnimFrame
=
(
function
(){
return
window
.
requestAnimationFrame
||
window
.
webkitRequestAnimationFrame
||
window
.
mozRequestAnimationFrame
||
window
.
oRequestAnimationFrame
||
window
.
msRequestAnimationFrame
||
function
(
callback
){
window
.
setTimeout
(
callback
,
1000
/
60
);
};
})();
والآن الكود التالي يمثل هيكل الحلقه الأساسية للعبة سنضيفه لنهاية POP.init.
// Add this at the end of POP.init;
// it will then repeat continuously
POP
.
loop
();
// Add the following functions after POP.init:
// this is where all entities will be moved
// and checked for collisions, etc.
update
:
function
()
{
},
// this is where we draw all the entities
render
:
function
()
{
POP
.
Draw
.
clear
();
},
// the actual loop
// requests animation frame,
// then proceeds to update
// and render
loop
:
function
()
{
requestAnimFrame
(
POP
.
loop
);
POP
.
update
();
POP
.
render
();
}
ايضاحات حول الكود:
-
POP.loop يستدعي طرقنا POP.update و POP.render.
-
RequestAnimFrame يؤكد بأن الحلقة تستدعى بشكل منفصل 60 واجهة frame بالثانية.
-
لاحظ أننا بشكل مسبق استمعنا (تعاملنا مع) لاحداث اللمس والنقر الذي نصل له عن طريق POP.Input class.
-
مشكلتنا الآن أننا بحاجه لنتذكر ماذا رسم على الشاشة واين؟؟؟
خامسا – التعرف على مصطلح الكائنات Entities:
سنضيف بالبداية مصفوفة كائنات للاحتفاظ بتحركات كل الكائنات لدينا لقسم التصريح عن المتحولات ببداية برنامجنا.
هذه المصفوفه سوف تحتوي على مؤشرات لكل من اللمس، الدوائر، وكل الأشياء الديناميكية التي نريد اضافتها للعبة.
// put this at start of program
entities
:
[],
دعنا نخلق كلاس Touch المسؤول عن رسم، حذف وتلاشي الدوائر عند حدوث التصادم.
POP
.
Touch
=
function(
x
,
y
)
{
this
.
type
=
'touch'
;
// we'll need this later
this
.
x
=
x
;
// the x coordinate
this
.
y
=
y
;
// the y coordinate
this
.
r
=
5
;
// the radius
this
.
opacity
=
1
;
// initial opacity; the dot will fade out
this
.
fade
=
0.05
;
// amount by which to fade on each game tick
this
.
remove
=
false
;
// flag for removing this entity. POP.update
// will take care of this
this
.
update
=
function
()
{
// reduce the opacity accordingly
this
.
opacity
-=
this
.
fade
;
// if opacity if 0 or less, flag for removal
this
.
remove
=
(
this
.
opacity
<
0
)
?
true
:
false
;
};
this
.
render
=
function
()
{
POP
.
Draw
.
circle
(
this
.
x
,
this
.
y
,
this
.
r
,
'rgba(255,0,0,'
+
this
.
opacity
+
')'
);
};
};
ايضاحات حول الكود:
-
كلاس Touch يضع عدد من الخصائص عند التهيئة الأولية.
-
الأبعاد x,y تمرر كمعاملات ثم نهيء نصف القطر this.r بقيمه 5 بكسل.
-
نهيء الشفافية ب 1 و معدل التناقص 0.05.
-
علم ramove الذي يخبر الحلقة الأساسية بلعبتنا قيما إذا كنا نريد إزالة هذا الكائن.
-
هذا الكلاس يملك طريقتين رئيسيتين update و render سوف نستدعهم في القسم المخصص بحلقة لعبتنا.
-
سننشئ مثال جديد من كلاس Touch في حلقة لعبتنا ثم نحركهم باستخدام طريقة update.
// POP.update function
update
:
function
()
{
var
i
;
// spawn a new instance of Touch
// if the user has tapped the screen
if
(
POP
.
Input
.
tapped
)
{
POP
.
entities
.
push
(
new
POP
.
Touch
(
POP
.
Input
.
x
,
POP
.
Input
.
y
));
// set tapped back to false
// to avoid spawning a new touch
// in the next cycle
POP
.
Input
.
tapped
=
false
;
}
// cycle through all entities and update as necessary
for
(
i
=
0
;
i
<
POP
.
entities
.
length
;
i
+=
1
)
{
POP
.
entities
[
i
].
update
();
// delete from array if remove property
// flag is set to true
if
(
POP
.
entities
[
i
].
remove
)
{
POP
.
entities
.
splice
(
i
,
1
);
}
}
},
ايضاحات حول الكود:
-
اذا كانت POP.Input.tapped = true عندها سنضيف غرض جديد من POP.Touch لمصفوفة كائناتنا.
-
ثم نكرر خطواتنا خلال مصفوفة كائناتنا مستدعين طريقة update من أجل كل كائن.
-
أخيرا اذا كان الكائن مؤشر عليه remove سيتم حذفه.
-
ثم نرسلهم للشاشة عن طريق التابع render.
-
بشكل مشابه لتابع update سوف نمر على كل الكائنات ونستدعي طريقة render لرسمهم.
// POP.render function
render
:
function
()
{
var
i
;
POP
.
Draw
.
rect
(
0
,
0
,
POP
.
WIDTH
,
POP
.
HEIGHT
,
'#036'
);
// cycle through all entities and render to canvas
for
(
i
=
0
;
i
<
POP
.
entities
.
length
;
i
+=
1
)
{
POP
.
entities
[
i
].
render
();
}
},
الآن سنضيف كلاس Bubble المسؤول عن إنشاء وتعويم الفقاعات للأعلى.
POP
.
Bubble
=
function()
{
this
.
type
=
'bubble'
;
this
.
x
=
100
;
this
.
r
=
5
;
// the radius of the bubble
this
.
y
=
POP
.
HEIGHT
+
100
;
// make sure it starts off screen
this
.
remove
=
false
;
this
.
update
=
function
()
{
// move up the screen by 1 pixel
this
.
y
-=
1
;
// if off screen, flag for removal
if
(
this
.
y
<
-
10
)
{
this
.
remove
=
true
;
}
};
this
.
render
=
function
()
{
POP
.
Draw
.
circle
(
this
.
x
,
this
.
y
,
this
.
r
,
'rgba(255,255,255,1)'
);
};
};
ايضاحات حول الكود:
-
كلاس POP.Bubble مشابه جدا لكلاس Touch الاختلاف الرئيسي بكونه لا يخفي الفقاعات بل يرفعهم للأعلى (هذا التأثير ينجز بتعديل مكان y). هنا يجب أن نفحص فيما اذا كانت الفقاعات خارج الشاشة ليتم ازالتها عن طريق العلم remove.
-
لاحظ نحن كان بإمكاننا أن نخلق كلاس أساسي Entity ونورث خصائصه لكلا Bubble و Touch.
// Add at the start of the program
// the amount of game ticks until
// we spawn a bubble
nextBubble
:
100
,
// at the start of POP.update
// decrease our nextBubble counter
POP
.
nextBubble
-=
1
;
// if the counter is less than zero
if
(
POP
.
nextBubble
<
0
)
{
// put a new instance of bubble into our entities array
POP
.
entities
.
push
(
new
POP
.
Bubble
());
// reset the counter with a random value
POP
.
nextBubble
=
(
Math
.
random
()
*
100
)
+
100
;
}
بالكود السابق أضفنا مؤقت لحلقة لعبتنا التي تخلق فقاعات بأماكن مختلفه. عند بداية اللعبة نضع الفقاعه الجديدة بقيمة 100. وتتناقص هذه القيمه إلى أن نصل 0 نولد فقاعه جديدة بعداد جديد.
سادسا – إضافة كشف التصادم والتعامل مع بعض العلاقات الرياضية:
إلى الآن لم نتكلم عن كشف التصادمات يمكننا إضافتهم بتابع بسيط يعتمد على علاقات رياضية حصلنا عليها من عالم الرياضيات brush up on at Wolfram MathWorld.
// this function checks if two circles overlap
POP
.
collides
=
function
(
a
,
b
)
{
var
distance_squared
=
(
((
a
.
x
-
b
.
x
)
*
(
a
.
x
-
b
.
x
))
+
((
a
.
y
-
b
.
y
)
*
(
a
.
y
-
b
.
y
)));
var
radii_squared
=
(
a
.
r
+
b
.
r
)
*
(
a
.
r
+
b
.
r
);
if
(
distance_squared
<
radii_squared
)
{
return
true
;
}
else
{
return
false
;
}
};
// at the start of POP.update, we set a flag for checking collisions
var
i
,
checkCollision
=
false
; // we only need to check for a collision
// if the user tapped on this game tick
// and then incorporate into the main logic
if
(
POP
.
Input
.
tapped
)
{
POP
.
entities
.
push
(
new
POP
.
Touch
(
POP
.
Input
.
x
,
POP
.
Input
.
y
));
// set tapped back to false
// to avoid spawning a new touch
// in the next cycle
POP
.
Input
.
tapped
=
false
;
checkCollision
=
true
;
}
// cycle through all entities and update as necessary
for
(
i
=
0
;
i
<
POP
.
entities
.
length
;
i
+=
1
)
{
POP
.
entities
[
i
].
update
();
if
(
POP
.
entities
[
i
].
type
===
'bubble'
&&
checkCollision
)
{
hit
=
POP
.
collides
(
POP
.
entities
[
i
],
{
x
:
POP
.
Input
.
x
,
y
:
POP
.
Input
.
y
,
r
:
7
});
POP
.
entities
[
i
].
remove
=
hit
;
}
// delete from array if remove property
// is set to true
if
(
POP
.
entities
[
i
].
remove
)
{
POP
.
entities
.
splice
(
i
,
1
);
}
}
بالوضع الحالي الفقاعات مملة لذلك سنعطي كل فقاعه سرعة مختلفة.
POP
.
Bubble
=
function()
{
this
.
type
=
'bubble'
;
this
.
r
=
(
Math
.
random
()
*
20
)
+
10
;
this
.
speed
=
(
Math
.
random
()
*
3
)
+
1
;
this
.
x
=
(
Math
.
random
()
*
(
POP
.
WIDTH
)
-
this
.
r
);
this
.
y
=
POP
.
HEIGHT
+
(
Math
.
random
()
*
100
)
+
100
;
this
.
remove
=
false
;
this
.
update
=
function
()
{
this
.
y
-=
this
.
speed
;
// the rest of the class is unchanged
لأكثر استمتاعا باللعبه دعنا نجعلهم ينتقلون من جانب لآخر بشكل جيبي حتى تزداد صعوبة الاصابة.
// the amount by which the bubble
// will move from side to side
this
.
waveSize
=
5
+
this
.
r
;
// we need to remember the original
// x position for our sine wave calculation
this
.
xConstant
=
this
.
x
;
this
.
remove
=
false
;
this
.
update
=
function
()
{
// a sine wave is commonly a function of time
var
time
=
new
Date
().
getTime
()
*
0.002
;
this
.
y
-=
this
.
speed
;
// the x coordinate to follow a sine wave
this
.
x
=
this
.
waveSize
*
Math
.
sin
(
time
)
+
this
.
xConstant
;
// the rest of the class is unchanged
الآن يلزمنا إظهار بعض الاحصائيات التي توصف مستوى تقدم اللاعب.
// this goes at the start of the program
// to track players's progress
POP
.
score
=
{
taps
:
0
,
hit
:
0
,
escaped
:
0
,
accuracy
:
0
},
الآن في كلاس Bubble نزيد قيمة المتحول POP.score.escaped عند كل خروج لفقاعه من الشاشة ( حتما يتم الخروج عند عدوم حدوث الإصابة).
// in the bubble class, when a bubble makes it to
// the top of the screen
if
(
this
.
y
<
-
10
)
{
POP
.
score
.
escaped
+=
1
; // update score
this
.
remove
=
true
;
}
في حلقة التعديل الاساسية سنزيد POP.score.hit والذي يمثل عدد الفقاعات التي أصابها اللاعب.
// in the update loop
if
(
POP
.
entities
[
i
].
type
===
'bubble'
&&
checkCollision
)
{
hit
=
POP
.
collides
(
POP
.
entities
[
i
],
{
x
:
POP
.
Input
.
x
,
y
:
POP
.
Input
.
y
,
r
:
7
});
if
(
hit
)
{
POP
.
score
.
hit
+=
1
;
}
POP
.
entities
[
i
].
remove
=
hit
;
}
حتى تكون إحصائيتنا صحيحة نحن بحاجة لتسجيل كل نقرة يقوم بها اللاعب.
// and record all taps
if
(
POP
.
Input
.
tapped
)
{
// keep track of taps; needed to
// calculate accuracy
POP
.
score
.
taps
+=
1
;