You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/he/6-space-game/4-collision-detection/README.md

21 KiB

בניית משחק חלל חלק 4: הוספת לייזר וזיהוי התנגשויות

שאלון לפני השיעור

שאלון לפני השיעור

חשבו על הרגע ב"מלחמת הכוכבים" שבו טורפדו הפרוטון של לוק פגע בפתח הפליטה של כוכב המוות. זיהוי ההתנגשות המדויק הזה שינה את גורל הגלקסיה! במשחקים, זיהוי התנגשויות עובד באותו אופן - הוא קובע מתי אובייקטים מתקשרים ומה קורה לאחר מכן.

בשיעור הזה, תוסיפו כלי נשק לייזר למשחק החלל שלכם ותיישמו זיהוי התנגשויות. בדיוק כמו שמתכנני המשימות של נאס"א מחשבים מסלולי חלליות כדי להימנע מפסולת חלל, תלמדו לזהות מתי אובייקטים במשחק מצטלבים. נשבור את זה לשלבים פשוטים שנבנה אחד על השני.

בסוף השיעור, יהיה לכם מערכת קרב מתפקדת שבה לייזרים משמידים אויבים והתנגשויות מפעילות אירועים במשחק. אותם עקרונות זיהוי התנגשויות משמשים בכל דבר, החל מסימולציות פיזיקה ועד ממשקי אינטרנט אינטראקטיביים.

בצעו מחקר קטן על משחק המחשב הראשון שנכתב אי פעם. מה הייתה הפונקציונליות שלו?

זיהוי התנגשויות

זיהוי התנגשויות עובד כמו חיישני הקרבה של מודול הירח אפולו - הוא בודק כל הזמן מרחקים ומפעיל התראות כאשר אובייקטים מתקרבים מדי. במשחקים, מערכת זו קובעת מתי אובייקטים מתקשרים ומה צריך לקרות לאחר מכן.

הגישה שנשתמש בה מתייחסת לכל אובייקט במשחק כמלבן, בדומה לאופן שבו מערכות בקרת תעופה משתמשות בצורות גיאומטריות פשוטות כדי לעקוב אחר מטוסים. שיטה מלבנית זו עשויה להיראות בסיסית, אך היא יעילה מבחינה חישובית ועובדת היטב ברוב תרחישי המשחק.

ייצוג מלבנים

כל אובייקט במשחק צריך גבולות קואורדינטות, בדומה לאופן שבו רובר Pathfinder של מאדים מיפה את מיקומו על פני השטח של מאדים. כך אנו מגדירים את גבולות הקואורדינטות הללו:

rectFromGameObject() {
  return {
    top: this.y,
    left: this.x,
    bottom: this.y + this.height,
    right: this.x + this.width
  }
}

בואו נפרק את זה:

  • הקצה העליון: זה פשוט המקום שבו האובייקט שלכם מתחיל אנכית (מיקום ה-y שלו)
  • הקצה השמאלי: המקום שבו הוא מתחיל אופקית (מיקום ה-x שלו)
  • הקצה התחתון: הוסיפו את הגובה למיקום ה-y - עכשיו אתם יודעים איפה הוא מסתיים!
  • הקצה הימני: הוסיפו את הרוחב למיקום ה-x - ויש לכם את הגבול המלא

אלגוריתם חיתוך

זיהוי חיתוך מלבנים משתמש בלוגיקה דומה לאופן שבו טלסקופ החלל האבל קובע אם אובייקטים שמימיים חופפים בשדה הראייה שלו. האלגוריתם בודק הפרדה:

function intersectRect(r1, r2) {
  return !(r2.left > r1.right ||
    r2.right < r1.left ||
    r2.top > r1.bottom ||
    r2.bottom < r1.top);
}

מבחן ההפרדה עובד כמו מערכות רדאר:

  • האם מלבן 2 נמצא לגמרי מימין למלבן 1?
  • האם מלבן 2 נמצא לגמרי משמאל למלבן 1?
  • האם מלבן 2 נמצא לגמרי מתחת למלבן 1?
  • האם מלבן 2 נמצא לגמרי מעל מלבן 1?

אם אף אחד מהתנאים הללו אינו נכון, המלבנים חייבים להיות חופפים. גישה זו משקפת כיצד מפעילי רדאר קובעים אם שני מטוסים נמצאים במרחק בטוח.

ניהול מחזורי חיים של אובייקטים

כאשר לייזר פוגע באויב, שני האובייקטים צריכים להיות מוסרים מהמשחק. עם זאת, מחיקת אובייקטים באמצע הלולאה יכולה לגרום לקריסות - שיעור שנלמד בדרך הקשה במערכות מחשב מוקדמות כמו מחשב ההנחיה של אפולו. במקום זאת, אנו משתמשים בגישה של "סימון למחיקה" שמסירה אובייקטים בבטחה בין פריימים.

כך אנו מסמנים משהו להסרה:

// Mark object for removal
enemy.dead = true;

למה הגישה הזו עובדת:

  • אנו מסמנים את האובייקט כ"מת" אך לא מוחקים אותו מיד
  • זה מאפשר לפריים המשחק הנוכחי להסתיים בבטחה
  • אין קריסות מניסיון להשתמש במשהו שכבר נעלם!

לאחר מכן מסננים אובייקטים מסומנים לפני מחזור הרינדור הבא:

gameObjects = gameObjects.filter(go => !go.dead);

מה הסינון הזה עושה:

  • יוצר רשימה חדשה עם רק האובייקטים "החיים"
  • זורק כל דבר שסומן כמת
  • שומר על המשחק שלכם פועל בצורה חלקה
  • מונע הצטברות זיכרון מאובייקטים שנהרסו

יישום מכניקת הלייזר

פרויקטים של לייזר במשחקים עובדים על אותו עיקרון כמו טורפדו פוטון ב"מסע בין כוכבים" - הם אובייקטים נפרדים שנעים בקווים ישרים עד שהם פוגעים במשהו. כל לחיצה על מקש הרווח יוצרת אובייקט לייזר חדש שנע על פני המסך.

כדי לגרום לזה לעבוד, אנחנו צריכים לתאם כמה חלקים שונים:

רכיבים מרכזיים ליישום:

  • יצירת אובייקטי לייזר שמופיעים ממיקום הגיבור
  • טיפול בקלט מהמקלדת כדי להפעיל יצירת לייזר
  • ניהול תנועת הלייזר ומחזור החיים שלו
  • יישום ייצוג חזותי לפרויקטי הלייזר

יישום בקרת קצב ירי

קצב ירי בלתי מוגבל יעמיס על מנוע המשחק ויהפוך את המשחק לקל מדי. מערכות נשק אמיתיות מתמודדות עם מגבלות דומות - אפילו הפייזרים של USS Enterprise היו צריכים זמן לטעינה בין יריות.

ניישם מערכת קירור שמונעת ירי מהיר תוך שמירה על שליטה תגובתית:

class Cooldown {
  constructor(time) {
    this.cool = false;
    setTimeout(() => {
      this.cool = true;
    }, time);
  }
}

class Weapon {
  constructor() {
    this.cooldown = null;
  }
  
  fire() {
    if (!this.cooldown || this.cooldown.cool) {
      // Create laser projectile
      this.cooldown = new Cooldown(500);
    } else {
      // Weapon is still cooling down
    }
  }
}

איך הקירור עובד:

  • כאשר נוצר, הנשק מתחיל "חם" (לא יכול לירות עדיין)
  • לאחר תקופת ההמתנה, הוא הופך ל"קר" (מוכן לירי)
  • לפני הירי, אנו בודקים: "האם הנשק קר?"
  • זה מונע לחיצות חוזרות ונשנות תוך שמירה על שליטה תגובתית

חזרו לשיעור הראשון בסדרת משחקי החלל כדי להזכיר לעצמכם על מערכות קירור.

בניית מערכת זיהוי התנגשויות

תאריכו את קוד משחק החלל הקיים שלכם כדי ליצור מערכת זיהוי התנגשויות. כמו מערכת ההימנעות האוטומטית של תחנת החלל הבינלאומית, המשחק שלכם יעקוב באופן רציף אחר מיקומי אובייקטים ויגיב לצמתים.

מתוך קוד השיעור הקודם שלכם, תוסיפו זיהוי התנגשויות עם כללים ספציפיים שמנהלים את האינטראקציות בין האובייקטים.

💡 טיפ מקצועי: ספרייט הלייזר כבר כלול בתיקיית הנכסים שלכם ומוזכר בקוד שלכם, מוכן ליישום.

כללי התנגשויות ליישום

מכניקת משחק להוסיף:

  1. לייזר פוגע באויב: אובייקט האויב נהרס כאשר פוגע בו פרויקט לייזר
  2. לייזר פוגע בגבול המסך: הלייזר מוסר כאשר מגיע לקצה העליון של המסך
  3. התנגשות בין אויב לגיבור: שני האובייקטים נהרסים כאשר הם מצטלבים
  4. אויב מגיע לתחתית: מצב סיום משחק כאשר אויבים מגיעים לתחתית המסך

הגדרת סביבת הפיתוח שלכם

חדשות טובות - כבר הכנו את רוב התשתית עבורכם! כל הנכסים של המשחק שלכם והמבנה הבסיסי מחכים בתיקיית your-work, מוכנים להוספת תכונות זיהוי התנגשויות מגניבות.

מבנה הפרויקט

-| assets
  -| enemyShip.png
  -| player.png
  -| laserRed.png
-| index.html
-| app.js
-| package.json

הבנת מבנה הקבצים:

  • מכיל את כל תמונות הספרייט הדרושות לאובייקטי המשחק
  • כולל את מסמך ה-HTML הראשי וקובץ ה-JavaScript של האפליקציה
  • מספק תצורת חבילה לשרת פיתוח מקומי

הפעלת שרת הפיתוח

נווטו לתיקיית הפרויקט שלכם והפעילו את השרת המקומי:

cd your-work
npm start

רצף הפקודות הזה:

  • משנה את הספרייה לתיקיית הפרויקט שלכם
  • מפעיל שרת HTTP מקומי בכתובת http://localhost:5000
  • מגיש את קבצי המשחק שלכם לבדיקה ופיתוח
  • מאפשר פיתוח חי עם טעינה אוטומטית

פתחו את הדפדפן שלכם ונווטו לכתובת http://localhost:5000 כדי לראות את מצב המשחק הנוכחי שלכם עם הגיבור והאויבים מוצגים על המסך.

יישום שלב אחר שלב

כמו הגישה השיטתית שבה השתמשה נאס"א לתכנות חללית וויאג'ר, ניישם את זיהוי ההתנגשויות בצורה שיטתית, ונבנה כל רכיב שלב אחר שלב.

1. הוספת גבולות חיתוך מלבניים

ראשית, נלמד את אובייקטי המשחק שלנו כיצד לתאר את גבולותיהם. הוסיפו את השיטה הזו למחלקת GameObject:

rectFromGameObject() {
    return {
      top: this.y,
      left: this.x,
      bottom: this.y + this.height,
      right: this.x + this.width,
    };
  }

השיטה הזו משיגה:

  • יוצרת אובייקט מלבני עם גבולות קואורדינטות מדויקות
  • מחשבת את הקצוות התחתונים והימניים באמצעות מיקום בתוספת ממדים
  • מחזירה אובייקט מוכן לאלגוריתמי זיהוי התנגשויות
  • מספקת ממשק סטנדרטי לכל אובייקטי המשחק

2. יישום זיהוי חיתוך

עכשיו ניצור את הבלש שלנו להתנגשויות - פונקציה שיכולה לומר מתי שני מלבנים חופפים:

function intersectRect(r1, r2) {
  return !(
    r2.left > r1.right ||
    r2.right < r1.left ||
    r2.top > r1.bottom ||
    r2.bottom < r1.top
  );
}

האלגוריתם הזה עובד על ידי:

  • בודק ארבעה תנאי הפרדה בין מלבנים
  • מחזיר false אם כל תנאי הפרדה נכון
  • מציין התנגשות כאשר אין הפרדה
  • משתמש בלוגיקת שלילה לבדיקת חיתוך יעילה

3. יישום מערכת ירי לייזר

כאן הדברים מתחילים להיות מרגשים! נקים את מערכת ירי הלייזר.

קבועי הודעות

ראשית, נגדיר כמה סוגי הודעות כך שחלקים שונים של המשחק שלנו יוכלו לתקשר זה עם זה:

KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",

הקבועים הללו מספקים:

  • מסטנדרטים שמות אירועים בכל האפליקציה
  • מאפשרים תקשורת עקבית בין מערכות המשחק
  • מונעים שגיאות כתיב ברישום מטפלי אירועים
טיפול בקלט מהמקלדת

הוסיפו זיהוי מקש רווח למאזין אירועי המקלדת שלכם:

} else if(evt.keyCode === 32) {
  eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}

מטפל הקלט הזה:

  • מזהה לחיצות על מקש הרווח באמצעות keyCode 32
  • משדר הודעת אירוע סטנדרטית
  • מאפשר לוגיקת ירי מנותקת
הגדרת מאזין אירועים

רשמו התנהגות ירי בפונקציית initGame() שלכם:

eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
 if (hero.canFire()) {
   hero.fire();
 }
});

מאזין האירועים הזה:

  • מגיב לאירועי מקש רווח
  • בודק את מצב קירור הירי
  • מפעיל יצירת לייזר כאשר מותר

הוסיפו טיפול בהתנגשויות לאינטראקציות לייזר-אויב:

eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
  first.dead = true;
  second.dead = true;
});

מטפל ההתנגשויות הזה:

  • מקבל נתוני אירוע התנגשות עם שני האובייקטים
  • מסמן את שני האובייקטים להסרה
  • מבטיח ניקוי נכון לאחר התנגשות

4. יצירת מחלקת לייזר

יישמו פרויקט לייזר שנע כלפי מעלה ומנהל את מחזור החיים שלו:

class Laser extends GameObject {
  constructor(x, y) {
    super(x, y);
    this.width = 9;
    this.height = 33;
    this.type = 'Laser';
    this.img = laserImg;
    
    let id = setInterval(() => {
      if (this.y > 0) {
        this.y -= 15;
      } else {
        this.dead = true;
        clearInterval(id);
      }
    }, 100);
  }
}

יישום המחלקה הזו:

  • מרחיב את GameObject כדי לרשת פונקציונליות בסיסית
  • מגדיר ממדים מתאימים לספרייט הלייזר
  • יוצר תנועה אוטומטית כלפי מעלה באמצעות setInterval()
  • מטפל בהרס עצמי כאשר מגיע לקצה העליון של המסך
  • מנהל את תזמון האנימציה והניקוי שלו

5. יישום מערכת זיהוי התנגשויות

צרו פונקציית זיהוי התנגשויות מקיפה:

function updateGameObjects() {
  const enemies = gameObjects.filter(go => go.type === 'Enemy');
  const lasers = gameObjects.filter(go => go.type === "Laser");
  
  // Test laser-enemy collisions
  lasers.forEach((laser) => {
    enemies.forEach((enemy) => {
      if (intersectRect(laser.rectFromGameObject(), enemy.rectFromGameObject())) {
        eventEmitter.emit(Messages.COLLISION_ENEMY_LASER, {
          first: laser,
          second: enemy,
        });
      }
    });
  });

  // Remove destroyed objects
  gameObjects = gameObjects.filter(go => !go.dead);
}

מערכת ההתנגשויות הזו:

  • מסננת אובייקטי משחק לפי סוג לבדיקת יעילות
  • בודקת כל לייזר מול כל אויב לצמתים
  • משדרת אירועי התנגשות כאשר צמתים מזוהים
  • מנקה אובייקטים שנהרסו לאחר עיבוד התנגשויות

⚠️ חשוב: הוסיפו updateGameObjects() ללולאת המשחק הראשית שלכם ב-window.onload כדי לאפשר זיהוי התנגשויות.

6. הוספת מערכת קירור למחלקת הגיבור

שפרו את מחלקת הגיבור עם מכניקת ירי והגבלת קצב:

class Hero extends GameObject {
  constructor(x, y) {
    super(x, y);
    this.width = 99;
    this.height = 75;
    this.type = "Hero";
    this.speed = { x: 0, y: 0 };
    this.cooldown = 0;
  }
  
  fire() {
    gameObjects.push(new Laser(this.x + 45, this.y - 10));
    this.cooldown = 500;

    let id = setInterval(() => {
      if (this.cooldown > 0) {
        this.cooldown -= 100;
      } else {
        clearInterval(id);
      }
    }, 200);
  }
  
  canFire() {
    return this.cooldown === 0;
  }
}

הבנת מחלקת הגיבור המשופרת:

  • מאתחלת טיימר קירור באפס (מוכן לירי)
  • יוצרת אובייקטי לייזר שממוקמים מעל ספינת הגיבור
  • מגדירה תקופת קירור למניעת ירי מהיר
  • מקטינה את טיימר הקירור באמצעות עדכונים מבוססי אינטרוול
  • מספקת בדיקת מצב ירי דרך שיטת canFire()

בדיקת היישום שלכם

למשחק החלל שלכם עכשיו יש מערכת זיהוי התנגשויות מלאה ומכניקת קרב. 🚀 בדקו את היכולות החדשות הללו:

  • נווטו עם מקשי החצים כדי לוודא את שליטת התנועה
  • ירו לייזרים עם מקש הרווח - שימו לב איך הקירור מונע לחיצות חוזרות ונשנות
  • צפו בהתנגשויות כאשר לייזרים פוגעים באויבים, מה שמפעיל הסרה
  • אמתו את הניקוי כאשר אובייקטים שנהרסו נעלמים מהמשחק

הצלחתם ליישם מערכת זיהוי התנגשויות באמצעות אותם עקרונות מתמטיים שמנחים ניווט חלליות ורובוטיקה.

אתגר סוכן GitHub Copilot 🚀

השתמשו במצב סוכן כדי להשלים את האתגר הבא:

תיאור: שפרו את מערכת זיהוי ההתנגשויות על ידי יישום חיזוקים שמופיעים באופן אקראי ומספקים יכולות זמניות כאשר נאספים על ידי ספינת הגיבור.

הנחיה: צרו מחלקת PowerUp שמרחיבה את GameObject ויישמו זיהוי התנגשויות בין הגיבור לחיזוקים. הוסיפו לפחות שני סוגי חיזוקים: אחד שמגדיל את קצב הירי (מפחית את הקירור) ואחד שיוצר מגן זמני. כללו לוגיקת הופעה שיוצרת חיזוקים במרווחים ובמיקומים אקראיים.


🚀 אתגר

הוסיפו פיצוץ! הסתכלו על נכסי המשחק במאגר אמנות החלל ונסו להוסיף פיצוץ כאשר הלייזר פוגע בחייזר.

שאלון לאחר השיעור

שאלון לאחר השיעור

סקירה ולימוד עצמי

נסו לשחק עם המרווחים במשחק שלכם עד כה. מה קורה כשאתם משנים אותם? קראו עוד על אירועי תזמון ב-JavaScript.

משימה

חקירת התנגשויות


הצהרת אחריות:
מסמך זה תורגם באמצעות שירות תרגום AI Co-op Translator. למרות שאנו שואפים לדיוק, יש לקחת בחשבון שתרגומים אוטומטיים עשויים להכיל שגיאות או אי דיוקים. המסמך המקורי בשפתו המקורית צריך להיחשב כמקור סמכותי. עבור מידע קריטי, מומלץ להשתמש בתרגום מקצועי אנושי. איננו אחראים לאי הבנות או פירושים שגויים הנובעים משימוש בתרגום זה.