مضى على الشبكة و يوم من العطاء.

تصميم لعبة الافعى باستخدام vanilla js و Html (الجزء 2)


السمعة:

السلام عليكم ورحمة الله تعالى وبركاته
بسم الله الرحمن الرحيم

اليوم سنكمل لعبة الأفعى إن شاء الله, لقد توقفنا في كيفية إنشاء الطعام والأفعى الواجهة وغيرها ,اليوم إن شاء الله سنرى كيفية تحريك الأفعى وزيادة طولها لحظة أكلها للطعام وغيرها.

🔹 تحريك الافعى
لتحريك الافعى نحتاج لدالتين أول دالة تُستدعى تلقائيًا بمجرد الضغط على التحريك من قبل المستخدم وثاني دالة تدرس تحرك الأفعى, بمعنى آخر إذا قمنا على سبيل المثال بالضغط على ستقوم الدالة الأولى بمناداة الثانية والتي ستقوم هذه الأخيرة بتحريك الأفعى, لنرى كيفية فعل هذا:

أولًا في جافا سكريبت هناك دالة تسمى بـ 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 نحصل على التالي:

Capture.webp

هذه النتيجة من دون حتى أكل الطعام, لذا علينا القيام بـ 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();

هكذا نكون انتهينا من تحريك الأفعى وضمان تحركها في الاتجاه المحدد, ننتقل للخطوة الموالية :

fa.webp

🔹 إنشاء الطعام
الخطوة هذه سهلة وبسيطة, فيما سبق تكلما عن pop وماذا يحصل إذا لم نقم بكتابتها, ألم يتبادر في أذهانكم أنه عند إنشاء الطعام لا نقوم بإخراج الأحداثيات السابقة ؟

images.webp

هذا هو المطلوب كل ماعلينا سوى
التحقق من مساوات أحداثيات الأفعى مع أحداثيات الطعام ثم ببساطة نقوم بإنشاء الطعام بجديد ولا نقوم بعملية 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();

}

والان ننتقل إلى آخر الخطوات:

fa.webp

🔹التحقق من الاصطدامات :

للتحقق من الاصطدام هناك شيئين يجب التحقق منهما أولًا:

  • للتحقق من اصطدام الأفعى بنفسها: أولًا يجب أن يكون لدينا رأس الأفعى بعدها سنتحقق من اصدطامها بإحدى أحداثياتها أم فلا, فلقد رأينا أنه عند أكل الأفعى للطعام لا نقوم بإخراج الأحداثيات السابقة أي يكون لدينا جدول به العديد من أحداثيات الأفعى والتي تخص كل مربع بها لذا ما علينا سوى أن نأتي بأول عنصر من الجدول ثم نمر على بقية العناصر ونتحقق في كل مرة ما إذا كان كل من 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();
        }
    }
}

}

fa.webp


🔹 شرح دالة 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 في الأسفل لمن يريد تجربتها وشكرًا


دُمتم في ألف خير
 

المرفقات

التعديل الأخير بواسطة المشرف:
السلام عليكم ورحمة الله تعالى وبركاته
بسم الله الرحمن الرحيم


اليوم انشاء الله سنكمل لعبة الافعى

لقد توقفنا في كيفية انشاء الطعام والافعى الواجهة وغيرها ,اليوم انشاء الله سنرى كيفية تحريك الافعى وزيادة طولها لحظة اكلها للطعام وغيرها انشاء الله

تحريك الافعى :
لتحريك الافعى نحتاج لدالتين اول دالة تستدعى تلقائيا بمجرد الضغط على التحريك من قبل المستخدم وثانيا دالة اخرى تدرس تحرك الافعى بمعنى اخر اذا قمنا على سبيل المثال بالضغط على ستقوم الدالة الاولى بمناداة الثانية والتي ستقوم هذه الاخيرة بتحريك الافعى ولنرى كيفية فعل هذا
اولا في جافا سكريبت هناك دالة تسمى ب 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 نتحصل على التالي


مشاهدة المرفق 12538
هذه النتيجة من دون حتى اكل الطعام لذا علينا القيام ب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 وماذا يحصل اذا لم نقم بكتابتها الم يتبادر في اذهانكم انه عند انشاء الطعام لانقوم باخراج الاحداثيات السابقة
هذا هو المطلوب كل ماعلينا سوى التحقق من مساوات احداثيات الافعى مع احداثيات الطعام ثم ببساطة نقوم بانشاء الطعام بجديد ولانقوم بعملية 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();
        }
    }
}

}

شرح دالة 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 في الاسفل لمن يريد تجربتها وشكرا
دمتم في الف خير
ما شاء الله لا قوة الا بالله
بارك الله فيك اخي وجزاك الله كل خير على هذا الطرح الرائع
ننتظر جديدك دائماً
 
ما شاء الله وكالعادة مع المواضيع الرائعة 🔥
بارك الله فيك
 
السلام عليكم ورحمة الله تعالى وبركاته
بسم الله الرحمن الرحيم


اليوم انشاء الله سنكمل لعبة الافعى

لقد توقفنا في كيفية انشاء الطعام والافعى الواجهة وغيرها ,اليوم انشاء الله سنرى كيفية تحريك الافعى وزيادة طولها لحظة اكلها للطعام وغيرها انشاء الله

تحريك الافعى :
لتحريك الافعى نحتاج لدالتين اول دالة تستدعى تلقائيا بمجرد الضغط على التحريك من قبل المستخدم وثانيا دالة اخرى تدرس تحرك الافعى بمعنى اخر اذا قمنا على سبيل المثال بالضغط على ستقوم الدالة الاولى بمناداة الثانية والتي ستقوم هذه الاخيرة بتحريك الافعى ولنرى كيفية فعل هذا
اولا في جافا سكريبت هناك دالة تسمى ب 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 نتحصل على التالي


مشاهدة المرفق 12538
هذه النتيجة من دون حتى اكل الطعام لذا علينا القيام ب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 وماذا يحصل اذا لم نقم بكتابتها الم يتبادر في اذهانكم انه عند انشاء الطعام لانقوم باخراج الاحداثيات السابقة
هذا هو المطلوب كل ماعلينا سوى التحقق من مساوات احداثيات الافعى مع احداثيات الطعام ثم ببساطة نقوم بانشاء الطعام بجديد ولانقوم بعملية 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();
        }
    }
}

}

شرح دالة 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 في الاسفل لمن يريد تجربتها وشكرا
دمتم في الف خير
على الرغم من إني ما أحب شغل الفرونت بشكل عام إلا إني كنت بانتظار هذا الجزء 😅
بارك الله فيك أختنا مينا ونفع بك
 
عندي تحفظ من اللغات التي ليس لديها entry point 😅 لكن في الأول و الأخير هدا حال الscripting langs
لكن بعيدا عن هدا و داك موضوع يستحق الاشادة تعلمت منه كثيرا بداية بالجزء الأول و أهم حاجة أن الكود مبسط بطريقة احترافية مشكور جدا
 
أضفت بعض التعديلات على الكود لكي تقوم الأفعى بالتحرك لوحدها بدل الضغط المتكرر على الأسهم
اضفت دالة باسم keep_moving على الشكل التالي

JavaScript:
function keep_moving() {
    move_snake();
    drawsnake();
    drawfood();
}
ة اضافة setinterval على حسب سرعة الافعى
setInterval(keep_moving, 100);

 
أضفت بعض التعديلات على الكود لكي تقوم الأفعى بالتحرك لوحدها بدل الضغط المتكرر على الأسهم
اضفت دالة باسم keep_moving على الشكل التالي

JavaScript:
function keep_moving() {
    move_snake();
    drawsnake();
    drawfood();
}
ة اضافة setinterval على حسب سرعة الافعى
setInterval(keep_moving, 100);

مشاهدة المرفق 12565
نعم فكرة رائعة بارك الله فيك
لقد فكرت في تطبيق خوارزمية A* الذي تجد اقصر طريق بين نقطتين لكن لم ابد العمل عليها بعد ايضا ستقوم بنفس العمل لكن بسرعة اكبر لكن دون الضغظ على اي ازرار
 
السلام عليكم ورحمة الله تعالى وبركاته
بسم الله الرحمن الرحيم

اليوم سنكمل لعبة الأفعى إن شاء الله, لقد توقفنا في كيفية إنشاء الطعام والأفعى الواجهة وغيرها ,اليوم إن شاء الله سنرى كيفية تحريك الأفعى وزيادة طولها لحظة أكلها للطعام وغيرها.

🔹 تحريك الافعى
لتحريك الافعى نحتاج لدالتين أول دالة تُستدعى تلقائيًا بمجرد الضغط على التحريك من قبل المستخدم وثاني دالة تدرس تحرك الأفعى, بمعنى آخر إذا قمنا على سبيل المثال بالضغط على ستقوم الدالة الأولى بمناداة الثانية والتي ستقوم هذه الأخيرة بتحريك الأفعى, لنرى كيفية فعل هذا:

أولًا في جافا سكريبت هناك دالة تسمى بـ 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();

هكذا نكون انتهينا من تحريك الأفعى وضمان تحركها في الاتجاه المحدد, ننتقل للخطوة الموالية :

مشاهدة المرفق 12566
🔹 إنشاء الطعام
الخطوة هذه سهلة وبسيطة, فيما سبق تكلما عن pop وماذا يحصل إذا لم نقم بكتابتها, ألم يتبادر في أذهانكم أنه عند إنشاء الطعام لا نقوم بإخراج الأحداثيات السابقة ؟


هذا هو المطلوب كل ماعلينا سوى
التحقق من مساوات أحداثيات الأفعى مع أحداثيات الطعام ثم ببساطة نقوم بإنشاء الطعام بجديد ولا نقوم بعملية 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();

}

والان ننتقل إلى آخر الخطوات:

مشاهدة المرفق 12566
🔹التحقق من الاصطدامات :

للتحقق من الاصطدام هناك شيئين يجب التحقق منهما أولًا:

  • للتحقق من اصطدام الأفعى بنفسها: أولًا يجب أن يكون لدينا رأس الأفعى بعدها سنتحقق من اصدطامها بإحدى أحداثياتها أم فلا, فلقد رأينا أنه عند أكل الأفعى للطعام لا نقوم بإخراج الأحداثيات السابقة أي يكون لدينا جدول به العديد من أحداثيات الأفعى والتي تخص كل مربع بها لذا ما علينا سوى أن نأتي بأول عنصر من الجدول ثم نمر على بقية العناصر ونتحقق في كل مرة ما إذا كان كل من 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();
        }
    }
}

}

مشاهدة المرفق 12566

🔹 شرح دالة 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 في الأسفل لمن يريد تجربتها وشكرًا


دُمتم في ألف خير
ما شاء الله , الله يعطيك العافية ويجزيك الخير
 

آخر المشاركات

فانوس

رمضان
عودة
أعلى