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/3-moving-elements-around/README.md

402 lines
16 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "23f088add24f0f1fa51014a9e27ea280",
"translation_date": "2025-08-27T20:23:38+00:00",
"source_file": "6-space-game/3-moving-elements-around/README.md",
"language_code": "he"
}
-->
# בניית משחק חלל חלק 3: הוספת תנועה
## חידון לפני השיעור
[חידון לפני השיעור](https://ff-quizzes.netlify.app/web/quiz/33)
משחקים לא ממש מהנים עד שיש חייזרים שמתרוצצים על המסך! במשחק הזה נשתמש בשני סוגי תנועות:
- **תנועה באמצעות מקלדת/עכבר**: כאשר המשתמש מתקשר עם המקלדת או העכבר כדי להזיז אובייקט על המסך.
- **תנועה שמונעת על ידי המשחק**: כאשר המשחק מזיז אובייקט במרווחי זמן מסוימים.
אז איך מזיזים דברים על המסך? הכל קשור לקואורדינטות קרטזיות: אנחנו משנים את המיקום (x,y) של האובייקט ואז מציירים מחדש את המסך.
בדרך כלל תצטרכו את השלבים הבאים כדי לבצע *תנועה* על המסך:
1. **קביעת מיקום חדש** לאובייקט; זה נחוץ כדי לתפוס את האובייקט כמשהו שזז.
2. **ניקוי המסך**, יש לנקות את המסך בין הציורים. ניתן לנקות אותו על ידי ציור מלבן שממלאים בצבע רקע.
3. **ציור מחדש של האובייקט** במיקום החדש. כך נוכל להזיז את האובייקט ממקום אחד לאחר.
כך זה יכול להיראות בקוד:
```javascript
//set the hero's location
hero.x += 5;
// clear the rectangle that hosts the hero
ctx.clearRect(0, 0, canvas.width, canvas.height);
// redraw the game background and hero
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);
```
✅ האם אתם יכולים לחשוב על סיבה מדוע ציור מחדש של הגיבור שלכם מספר רב של פעמים בשנייה עלול לגרום לעלויות ביצועים? קראו על [חלופות לדפוס הזה](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas).
## טיפול באירועי מקלדת
מטפלים באירועים על ידי חיבור אירועים ספציפיים לקוד. אירועי מקלדת מופעלים על כל החלון, בעוד שאירועי עכבר כמו `click` יכולים להיות מחוברים ללחיצה על אלמנט מסוים. נשתמש באירועי מקלדת לאורך כל הפרויקט הזה.
כדי לטפל באירוע, עליכם להשתמש בשיטת `addEventListener()` של החלון ולספק לה שני פרמטרים. הפרמטר הראשון הוא שם האירוע, לדוגמה `keyup`. הפרמטר השני הוא הפונקציה שצריכה להיות מופעלת כתוצאה מהתרחשות האירוע.
הנה דוגמה:
```javascript
window.addEventListener('keyup', (evt) => {
// `evt.key` = string representation of the key
if (evt.key === 'ArrowUp') {
// do something
}
})
```
עבור אירועי מקלדת ישנן שתי תכונות באירוע שניתן להשתמש בהן כדי לראות איזו מקש נלחץ:
- `key`, זהו ייצוג מחרוזת של המקש שנלחץ, לדוגמה `ArrowUp`.
- `keyCode`, זהו ייצוג מספרי, לדוגמה `37`, שמתאים ל-`ArrowLeft`.
✅ מניפולציה של אירועי מקלדת שימושית גם מחוץ לפיתוח משחקים. אילו שימושים נוספים אתם יכולים לחשוב עליהם לטכניקה הזו?
### מקשים מיוחדים: אזהרה
ישנם מקשים *מיוחדים* שמשפיעים על החלון. המשמעות היא שאם אתם מאזינים לאירוע `keyup` ומשתמשים במקשים המיוחדים האלה כדי להזיז את הגיבור שלכם, זה גם יבצע גלילה אופקית. מסיבה זו ייתכן שתרצו *לכבות* את ההתנהגות המובנית של הדפדפן בזמן שאתם בונים את המשחק שלכם. תצטרכו קוד כמו זה:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
switch (e.keyCode) {
case 37:
case 39:
case 38:
case 40: // Arrow keys
case 32:
e.preventDefault();
break; // Space
default:
break; // do not block other keys
}
};
window.addEventListener('keydown', onKeyDown);
```
הקוד לעיל יבטיח שמקשי החצים ומקש הרווח יכבו את ההתנהגות *הברירת מחדל* שלהם. מנגנון ה"כיבוי" מתרחש כאשר אנו קוראים ל-`e.preventDefault()`.
## תנועה שמונעת על ידי המשחק
ניתן לגרום לדברים לזוז בעצמם על ידי שימוש בטיימרים כמו הפונקציות `setTimeout()` או `setInterval()` שמעדכנות את מיקום האובייקט בכל טיק, או מרווח זמן. כך זה יכול להיראות:
```javascript
let id = setInterval(() => {
//move the enemy on the y axis
enemy.y += 10;
})
```
## לולאת המשחק
לולאת המשחק היא מושג שבמהותו פונקציה שמופעלת במרווחים קבועים. היא נקראת לולאת המשחק מכיוון שכל מה שצריך להיות גלוי למשתמש מצויר בתוך הלולאה. לולאת המשחק משתמשת בכל אובייקטי המשחק שהם חלק מהמשחק, ומציירת את כולם אלא אם כן מסיבה כלשהי הם כבר לא חלק מהמשחק. לדוגמה, אם אובייקט הוא אויב שנפגע על ידי לייזר ומתפוצץ, הוא כבר לא חלק מלולאת המשחק הנוכחית (תלמדו על כך יותר בשיעורים הבאים).
כך לולאת משחק יכולה להיראות בדרך כלל, בקוד:
```javascript
let gameLoopId = setInterval(() =>
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawHero();
drawEnemies();
drawStaticObjects();
}, 200);
```
הלולאה לעיל מופעלת כל `200` מילישניות כדי לצייר מחדש את הקנבס. יש לכם את היכולת לבחור את המרווח הטוב ביותר שמתאים למשחק שלכם.
## המשך משחק החלל
תיקחו את הקוד הקיים ותמשיכו אותו. או שתתחילו עם הקוד שסיימתם בחלק הראשון או שתשתמשו בקוד שב-[חלק השני - התחלה](../../../../6-space-game/3-moving-elements-around/your-work).
- **הזזת הגיבור**: תוסיפו קוד כדי לוודא שניתן להזיז את הגיבור באמצעות מקשי החצים.
- **הזזת אויבים**: תצטרכו גם להוסיף קוד כדי לוודא שהאויבים זזים מלמעלה למטה בקצב מסוים.
## שלבים מומלצים
אתרו את הקבצים שנוצרו עבורכם בתיקיית `your-work`. היא אמורה להכיל את הדברים הבאים:
```bash
-| assets
-| enemyShip.png
-| player.png
-| index.html
-| app.js
-| package.json
```
תתחילו את הפרויקט שלכם בתיקיית `your_work` על ידי הקלדת:
```bash
cd your-work
npm start
```
הפקודה לעיל תתחיל שרת HTTP בכתובת `http://localhost:5000`. פתחו דפדפן והזינו את הכתובת הזו, כרגע זה אמור להציג את הגיבור ואת כל האויבים; שום דבר עדיין לא זז!
### הוספת קוד
1. **הוסיפו אובייקטים ייעודיים** עבור `hero`, `enemy` ו-`game object`, הם צריכים לכלול תכונות `x` ו-`y`. (זכרו את החלק על [ירושה או קומפוזיציה](../README.md)).
*רמז*: `game object` צריך להיות זה עם `x` ו-`y` והיכולת לצייר את עצמו על הקנבס.
>טיפ: התחילו על ידי הוספת מחלקת GameObject עם הבנאי שלה כפי שמוצג למטה, ואז ציירו אותה על הקנבס:
```javascript
class GameObject {
constructor(x, y) {
this.x = x;
this.y = y;
this.dead = false;
this.type = "";
this.width = 0;
this.height = 0;
this.img = undefined;
}
draw(ctx) {
ctx.drawImage(this.img, this.x, this.y, this.width, this.height);
}
}
```
עכשיו, הרחיבו את GameObject כדי ליצור את Hero ואת Enemy.
```javascript
class Hero extends GameObject {
constructor(x, y) {
...it needs an x, y, type, and speed
}
}
```
```javascript
class Enemy extends GameObject {
constructor(x, y) {
super(x, y);
(this.width = 98), (this.height = 50);
this.type = "Enemy";
let id = setInterval(() => {
if (this.y < canvas.height - this.height) {
this.y += 5;
} else {
console.log('Stopped at', this.y)
clearInterval(id);
}
}, 300)
}
}
```
2. **הוסיפו מאזינים לאירועי מקשים** כדי לטפל בניווט מקשים (הזזת הגיבור למעלה/למטה שמאלה/ימינה).
*זכרו* שזהו מערכת קרטזית, הפינה השמאלית העליונה היא `0,0`. גם זכרו להוסיף קוד לעצירת *התנהגות ברירת מחדל*.
>טיפ: צרו את הפונקציה onKeyDown שלכם וחברו אותה לחלון:
```javascript
let onKeyDown = function (e) {
console.log(e.keyCode);
...add the code from the lesson above to stop default behavior
}
};
window.addEventListener("keydown", onKeyDown);
```
בדקו את קונסולת הדפדפן שלכם בשלב זה, וצפו בלחיצות המקשים שנרשמות.
3. **ממשו** את [תבנית Pub sub](../README.md), זה ישמור על הקוד שלכם נקי ככל שתמשיכו בחלקים הבאים.
כדי לבצע את החלק האחרון, תוכלו:
1. **להוסיף מאזין לאירועים** על החלון:
```javascript
window.addEventListener("keyup", (evt) => {
if (evt.key === "ArrowUp") {
eventEmitter.emit(Messages.KEY_EVENT_UP);
} else if (evt.key === "ArrowDown") {
eventEmitter.emit(Messages.KEY_EVENT_DOWN);
} else if (evt.key === "ArrowLeft") {
eventEmitter.emit(Messages.KEY_EVENT_LEFT);
} else if (evt.key === "ArrowRight") {
eventEmitter.emit(Messages.KEY_EVENT_RIGHT);
}
});
```
1. **ליצור מחלקת EventEmitter** כדי לפרסם ולהירשם להודעות:
```javascript
class EventEmitter {
constructor() {
this.listeners = {};
}
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach((l) => l(message, payload));
}
}
}
```
1. **להוסיף קבועים** ולהגדיר את EventEmitter:
```javascript
const Messages = {
KEY_EVENT_UP: "KEY_EVENT_UP",
KEY_EVENT_DOWN: "KEY_EVENT_DOWN",
KEY_EVENT_LEFT: "KEY_EVENT_LEFT",
KEY_EVENT_RIGHT: "KEY_EVENT_RIGHT",
};
let heroImg,
enemyImg,
laserImg,
canvas, ctx,
gameObjects = [],
hero,
eventEmitter = new EventEmitter();
```
1. **לאתחל את המשחק**
```javascript
function initGame() {
gameObjects = [];
createEnemies();
createHero();
eventEmitter.on(Messages.KEY_EVENT_UP, () => {
hero.y -=5 ;
})
eventEmitter.on(Messages.KEY_EVENT_DOWN, () => {
hero.y += 5;
});
eventEmitter.on(Messages.KEY_EVENT_LEFT, () => {
hero.x -= 5;
});
eventEmitter.on(Messages.KEY_EVENT_RIGHT, () => {
hero.x += 5;
});
}
```
1. **הגדירו את לולאת המשחק**
ערכו מחדש את פונקציית window.onload כדי לאתחל את המשחק ולהגדיר לולאת משחק במרווח טוב. תוסיפו גם קרן לייזר:
```javascript
window.onload = async () => {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
heroImg = await loadTexture("assets/player.png");
enemyImg = await loadTexture("assets/enemyShip.png");
laserImg = await loadTexture("assets/laserRed.png");
initGame();
let gameLoopId = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawGameObjects(ctx);
}, 100)
};
```
5. **הוסיפו קוד** להזזת אויבים במרווח מסוים
ערכו מחדש את הפונקציה `createEnemies()` כדי ליצור את האויבים ולהוסיף אותם למחלקת gameObjects החדשה:
```javascript
function createEnemies() {
const MONSTER_TOTAL = 5;
const MONSTER_WIDTH = MONSTER_TOTAL * 98;
const START_X = (canvas.width - MONSTER_WIDTH) / 2;
const STOP_X = START_X + MONSTER_WIDTH;
for (let x = START_X; x < STOP_X; x += 98) {
for (let y = 0; y < 50 * 5; y += 50) {
const enemy = new Enemy(x, y);
enemy.img = enemyImg;
gameObjects.push(enemy);
}
}
}
```
ותוסיפו פונקציה `createHero()` כדי לבצע תהליך דומה עבור הגיבור.
```javascript
function createHero() {
hero = new Hero(
canvas.width / 2 - 45,
canvas.height - canvas.height / 4
);
hero.img = heroImg;
gameObjects.push(hero);
}
```
ולבסוף, תוסיפו פונקציה `drawGameObjects()` כדי להתחיל את הציור:
```javascript
function drawGameObjects(ctx) {
gameObjects.forEach(go => go.draw(ctx));
}
```
האויבים שלכם אמורים להתחיל להתקדם לעבר חללית הגיבור שלכם!
---
## 🚀 אתגר
כפי שאתם רואים, הקוד שלכם יכול להפוך ל'קוד ספגטי' כאשר אתם מתחילים להוסיף פונקציות, משתנים ומחלקות. איך תוכלו לארגן את הקוד שלכם בצורה טובה יותר כך שיהיה קריא יותר? שרטטו מערכת לארגון הקוד שלכם, גם אם הוא עדיין נמצא בקובץ אחד.
## חידון אחרי השיעור
[חידון אחרי השיעור](https://ff-quizzes.netlify.app/web/quiz/34)
## סקירה ולימוד עצמי
בעוד שאנחנו כותבים את המשחק שלנו ללא שימוש בפריימוורקים, ישנם פריימוורקים רבים מבוססי JavaScript לפיתוח משחקים עם קנבס. הקדישו זמן לקרוא על [הפריימוורקים האלה](https://github.com/collections/javascript-game-engines).
## משימה
[הוסיפו הערות לקוד שלכם](assignment.md)
---
**כתב ויתור**:
מסמך זה תורגם באמצעות שירות תרגום מבוסס בינה מלאכותית [Co-op Translator](https://github.com/Azure/co-op-translator). בעוד שאנו שואפים לדיוק, יש להיות מודעים לכך שתרגומים אוטומטיים עשויים להכיל שגיאות או אי דיוקים. המסמך המקורי בשפתו המקורית צריך להיחשב כמקור סמכותי. עבור מידע קריטי, מומלץ להשתמש בתרגום מקצועי על ידי אדם. איננו נושאים באחריות לאי הבנות או לפרשנויות שגויות הנובעות משימוש בתרגום זה.