



السمعة:
- إنضم7 فبراير 2024
- المشاركات 52
- مستوى التفاعل 108
- النقاط 33
السلام عليكم ورحمة الله تعالى وبركاته
بسم الله الرحمن الرحيم
بسم الله الرحمن الرحيم
اليوم سنكمل لعبة الأفعى إن شاء الله, لقد توقفنا في كيفية إنشاء الطعام والأفعى الواجهة وغيرها ,اليوم إن شاء الله سنرى كيفية تحريك الأفعى وزيادة طولها لحظة أكلها للطعام وغيرها.

لتحريك الافعى نحتاج لدالتين أول دالة تُستدعى تلقائيًا بمجرد الضغط على التحريك من قبل المستخدم وثاني دالة تدرس تحرك الأفعى, بمعنى آخر إذا قمنا على سبيل المثال بالضغط على ↓ ستقوم الدالة الأولى بمناداة الثانية والتي ستقوم هذه الأخيرة بتحريك الأفعى, لنرى كيفية فعل هذا:
أولًا في جافا سكريبت هناك دالة تسمى بـ addEventListener والتي تستخدم لإضافة مستمع لحدث معين على عنصر html فعند حدوث الحدث المحدد سيتم تنفيذ الوظيفة أو الدالة التي قدمتها, لذا لنسمي هذه الوظيفة على سبيل المثال بkeys ولنشرع في كتابة الدالة:
كود:
document.addEventListener('keydown',keys);
//keydown هي نوع الحدث والذي يشمل احد الازرار هذه الازرار ↑ ↓ → ←
الآن لنشرع في إنشاء الدالة keys عندما يضغط المستخدم على أحد الأزرار التالية: ↑ ↓ → ←
بهذه الطريقة أنت تحدد اتجاه الأفعى لكي تضمن بأنها لا تسلك طريق آخر, لذا علينا في كل مرة أن نأكد الاتجاه الذي ستسلكه الأفعى على سبيل المثال ليكن لدينا متغير كالتالي:
كود:
let direction;
function keys();
document.addEventListener('keydown',keys);
نريد في كل مرة يضغط فيها المستخدم أحد الأزرار نقوم بتأكيد الاتجاه المدخل, فاذا قمنا بإدخال ↑ نعين direction على up وإذا قمنا بالضغط على→ نقوم بتعيينها على right , وهكذا لنحاول كتابتها على شكل كود:
كود:
function keys(event){
switch(event.key)
case'ArrowUp':
direction='up';
break;
case'ArrowRight':
direction="right"
break;
case'ArrowDown':
direction="down";
break;
case'ArrowLeft':
direction="left";
break;
}
// كما ترون اولا الدالة تاخذ حدث ثم نقوم بالتحقق من نوع الزر المضغوط ب .key وهذا لان الحدث هذا عبارة عن كائن يحتوي على احد الخصائص التالية
// ArrowUp,ArrowRight,ArrowLeft,ArrowDown
//لذا في كل مرة نعين الاتجاه حسب الزر المضغوط
لنتابع الآن بعدما حددنا الاتجاه هل ستتحرك الأفعى؟ بالطبع لا ,فهذا مجرد تعيين للاتجاهات
فكيف نحرك الأفعى ياترى؟
حسنا يمكننا اعتبار الواجهة الخاصة بنا عبارة عن معلم x/y أو يمكن اعتبارها مصفوفة ثنائية الأبعاد وهذا ما هي عليه, ولقد رأينا في المرة السابقة أن الأفعى عبارة عن كائن يحتوي على مجموعة من الأحداثيات x/y , إذًا لو كان الاتجاه (direction=right) على سبيل المثال فما علينا سوا زيادة 1 إلى x لو كانت direction=left ننقص 1 من x وهكذا لنحاول كتابة الكود لفهم أوضح ولتكن اسم الدالة الجديدة move_snake
كود:
let snake_pos=[{x:10,y:10}];// هذه الاحداثيات التي قمنا بتعيينها للافعى في المرة السابقة
function move_snake(){
const ya={...snake_pos[0]};//هنا قمنا باعلان متغير من نوع object يحتوي بالداخل على احداثيات الافعى الخاصة بنا
//الثلاث نقاط تلك تشير في هذه الحالة الى انشاء نسخة من المتغير snake_pos
//حيث يتم نشر جميع الخصائص الموجودة في snake_pos[0]
داخل الكائن الجديد ya
switch(direction){
case "right":
ya.x++;
if (ya.x > gridsize) ya.x = 1;
break;
case "left":
ya.x--;
if (ya.x < 1) ya.x = gridsize;
break;
case "up":
ya.y--;
if (ya.y < 1) ya.y = gridsize;
break;
case "down":
ya.y++;
if (ya.y > gridsize) ya.y = 1;
break;
}
snake_pos.unshift(ya);
snake_pos.pop();
drawsnake();
// الدالة تقوم بالتالي اولا عندما نقوم بتحديد الاتجاه باستعمال الدالة keys
//سنقوم باستدعاء الدالة الحالية ستتحقق من الاتجاه الذي قمنا بتثبيته ثم تضيف الاحداثيات الجديدة كما قلت لكم مسبقا
//اذا كان الاتجاه left سننقص 1 من x وهكذا
// ثم تاتي الخطوة المهمة بعد انتهاء من switch
//كما ترون قمنا بكتابة snake_pos.unshift(ya); هذه تقوم بادخال الاحداثيات الجديدة داخل ya
//ملاحظة ستدخلها من بداية ثم نقوم باخراج الاحداثيات القديمة snake_pos.pop();
//وننشى الافعى من جديد باستخدام drawsnake()
// قد تتسائلون لماذا اخرجنا الاحداثيات القديمة لو لم نقم بهذا سنتحصل على احداثياتنا الجديدة لكن القديمة ستبقى كما هي وكانها لا تتحرك
// يمكنكم رؤية النتيجة التالية لو لم نقم بهذا
}
لو لم نقم بـ pop نحصل على التالي:
هذه النتيجة من دون حتى أكل الطعام, لذا علينا القيام بـ pop كل مرة نقوم بتحريك الأفعى, الآن هذه الدالة سنقوم باستدعائها في دالة keys بهذا الشكل:
كود:
const board=document.getElementById ('board');
let snake_pos=[{x:10,y:10}];
let direction="down";
food=setfood();
function drawsnake(){
board.innerHTML='';
snake_pos.forEach((x)=>{
const element=creat('div','snake');
pos(element,x);
board.appendChild(element);
});
}
function creat(x,y){
var element=document.createElement(x);
element.className=y;
return element;
}
function pos(element,pos){
element.style.gridColumn=pos.x;
element.style.gridRow=pos.y;
}
function drawfood(){
const element=creat('div','food');
pos(element,food)
board.appendChild(element);
}
function setfood(){
const x=Math.floor(Math.random()*gridsize)+1;
const y=Math.floor(Math.random()*gridsize)+1;
return {x,y};
}
function move_snake(){
const ya={...snake_pos[0]};
switch(direction){
case "right":
ya.x++;
break;
case "left":
ya.x--;
break;
case "up":
ya.y--;
break;
case "down":
ya.y++;
break;
}
snake_pos.unshift(ya);
drawsnake();
drawfood();
}
function keys(event){
switch(event.key){
case'ArrowUp':
direction='up';
break;
case'ArrowRight':
direction="right"
break;
case'ArrowDown':
direction="down";
break;
case'ArrowLeft':
direction="left";
break;
}
move_snake();
}
drawsnake();
drawfood();
هكذا نكون انتهينا من تحريك الأفعى وضمان تحركها في الاتجاه المحدد, ننتقل للخطوة الموالية :

هذا هو المطلوب كل ماعلينا سوى التحقق من مساوات أحداثيات الأفعى مع أحداثيات الطعام ثم ببساطة نقوم بإنشاء الطعام بجديد ولا نقوم بعملية pop هذا كل شيئ .
كود:
function move_snake(){
const ya={...snake_pos[0]};
switch(direction){
case "right":
ya.x++;
break;
case "left":
ya.x--;
break;
case "up":
ya.y--;
break;
case "down":
ya.y++;
break;
}
snake_pos.unshift(ya);
//بداية التعديل
if(snake_pos[0].x===food.x && snake_pos[0].y===food.y){
drawsnake();
food=setfood();
}
else{
snake_pos.pop();
}
//نهاية التعديل
drawsnake();
drawfood();
}
وبهذا نكون أنهينا أصعب الخطوات كل ما يتبقى هو التحقق من اصطدام الأفعى بالجدران وزيادة العداد عند أكل الطعام لذا لنبدأ بالعداد أنشئنا فيما سبق في صفحة html عنصر باسم score والذي ستزداد قيمته كلما أكلت الأفعى الطعام, لذا نقوم بالتالي:
كود:
const scores=document.getElementById('score');
const hieght=document.getElementById('height');// هذا يمثل اعلى نقطة وصلت لها ويعرض عند موت الافعى
لذا ننشئ دالتين باسم updateScore و updateHeightScore
[
كود:
const scores=document.getElementById('score');
const hieght=document.getElementById('height');
function updateScore() {
const currentScore = snake_pos.length - 1;
scores.textContent = currentScore.toString().padStart(3, '0');
}
function updateHeightScore() {
const currentScore = snake_pos.length - 1;
hieght.textContent = currentScore.toString().padStart(3, '0');
}
//الدالتين تقومان بنفس العمل اولا تتحقق من طول الافعى وتقوم بانقاص واحد وهذا لان راس االافعى لايحسب كنقطة
// ثم في الخطوة الثانية الدالة padStart تقوم بالتالي
// على سبيل المثال لو كان طول الافعى الكامل 3 في currentScore=2
//ثم سنحول 2 الى string بعدها الدالة padstart
//تعمل على اضافة حروف أو أرقام إلى بداية سلسلة نصية (string)
حتى تصل إلى طول معين
//string.padStart(targetLength, padString);
// في هذه الحالة في السلسلة النصية "2"
// حددنا الطول ب3 في الدالة اذا نتحصل على التالي
//"002"
سنقوم باستدعاء دالة updatScode في الكود السابق وهذا عند تناول الأفعى الطعام:
كود:
if(snake_pos[0].x===food.x && snake_pos[0].y===food.y){
drawsnake();
updateScore()
food=setfood();
}
والان ننتقل إلى آخر الخطوات:

للتحقق من الاصطدام هناك شيئين يجب التحقق منهما أولًا:
- للتحقق من اصطدام الأفعى بنفسها: أولًا يجب أن يكون لدينا رأس الأفعى بعدها سنتحقق من اصدطامها بإحدى أحداثياتها أم فلا, فلقد رأينا أنه عند أكل الأفعى للطعام لا نقوم بإخراج الأحداثيات السابقة أي يكون لدينا جدول به العديد من أحداثيات الأفعى والتي تخص كل مربع بها لذا ما علينا سوى أن نأتي بأول عنصر من الجدول ثم نمر على بقية العناصر ونتحقق في كل مرة ما إذا كان كل من xوy متساويان.
- التحقق من اصطدام الأفعى بالجدار : ببساطة ما علينا سوى التحقق من أن x او y الخاص برأس الأفعى أكبر أو أقل من عدد أعمدة وأسطر المصفوفة في هذه الحالة مصفوفتنا من شكل 20x20 أي إذا كان x أكبر من 20 أو أقل من واحد يتحقق الاصطدام ونفس الشيء مع y
ملاحظة : عند تحقق هذين الاصطدامين سنستدعي دالة خاصة تقوم باستعراض نهاية ولعبة وإعادة كل شيء من 0 والتي شارحها فيما بعد
لنشرع في كتابة الكود:
لنشرع في كتابة الكود:
كود:
function keys(event){
switch(event.key){
case'ArrowUp':
direction='up';
break;
case'ArrowRight':
direction="right"
break;
case'ArrowDown':
direction="down";
break;
case'ArrowLeft':
direction="left";
break;
}
move_snake();
checkColision();//كما ترون استدعيت هذه الدالة في keys وهذا للتحقق من وجدود اصطدامات او لا في كل مرة يقوم
//المستخدم بادخال الاتجاه
}
document.addEventListener('keydown',keys);
function checkColision(){
head=snake_pos[0];//هذا راس الافعى والذي هو اول عنصر بالجدول
if( head.x<1 || head.x>gridsize || head.y<1 || head.y>gridsize ){
resetgame();
}
else{
for(let i=1;i<=snake_pos.length;i++){
if(head.x===snake_pos[i].x && head.y===snake_pos[i].y){
resetgame();
}
}
}
}

هذه الدالة نستدعيها عند تحقق شروط الاصطدام, فنقوم بإعادة كل شيء لحالته الأصلية والتي تشمل إعادة رأس الأفعى إلى أحداثياته الأصلية والتي هي x:10 وy:10 وإعادة score إلى 0 وإعداد الطعام من جديد وإظهار أيضًا game over
كود:
function resetgame(){
updateHeightScore();// الدالة هذه تستعرض اقصى نقاط لديك عند الاصطدام
snake_pos=[{x:10,y:10}];
food=setfood();
drawsnake();
drawfood();
updateScore();//عند استدعاء هذه سيرجع score="000"
const tag=creat('h2','R');//هنا قمنا بانشاء عنصر html من نوع h2 وباسم
//R هذه الدالة ستقوم بالمطلوب الذي تكلمنا عنه الدرس السابق
tag.innerHTML = 'GAME OVER!';//هذه ستسمح لنا الكتابة داخل العنصر المنشئ H2
board.appendChild(tag);
}
ثم في الاخير يمكنك منادات R
في CSS وتعديلها بالطريقة التي تريد
وبهذا نكون قد وصلنا للنهاية , أتمنى أن تكونوا استفدتم واستمتعتم بلعبة
ستجدون SOURCE CODE في الأسفل لمن يريد تجربتها وشكرًا
دُمتم في ألف خير
ستجدون SOURCE CODE في الأسفل لمن يريد تجربتها وشكرًا
دُمتم في ألف خير
المرفقات
التعديل الأخير بواسطة المشرف: