# בניית משחק חלל חלק 3: הוספת תנועה חשבו על המשחקים האהובים עליכם – מה שהופך אותם למרתקים זה לא רק הגרפיקה היפה, אלא גם האופן שבו הכל זז ומגיב לפעולות שלכם. כרגע, משחק החלל שלכם הוא כמו ציור יפה, אבל אנחנו עומדים להוסיף תנועה שתביא אותו לחיים. כשמהנדסי נאס"א תכנתו את מחשב ההנחיה למשימות אפולו, הם עמדו בפני אתגר דומה: איך לגרום לחללית להגיב לפקודות הטייס תוך שמירה אוטומטית על תיקוני מסלול? העקרונות שנלמד היום מהדהדים את אותם רעיונות – ניהול תנועה הנשלטת על ידי השחקן לצד התנהגויות מערכת אוטומטיות. בשיעור הזה תלמדו איך לגרום לחלליות להחליק על פני המסך, להגיב לפקודות השחקן וליצור דפוסי תנועה חלקים. נפרק הכל למושגים שניתן להבין בקלות ושבונים אחד על השני באופן טבעי. בסוף השיעור, השחקנים יוכלו להטיס את ספינת הגיבור שלהם על פני המסך בזמן שספינות אויב יסיירו מעל. חשוב מכך, תבינו את העקרונות המרכזיים שמניעים מערכות תנועה במשחקים. ## שאלון לפני השיעור [שאלון לפני השיעור](https://ff-quizzes.netlify.app/web/quiz/33) ## הבנת תנועת משחקים משחקים מתעוררים לחיים כשהדברים מתחילים לזוז, ויש שתי דרכים בסיסיות שבהן זה קורה: - **תנועה הנשלטת על ידי השחקן**: כשאתם לוחצים על מקש או מקליקים עם העכבר, משהו זז. זהו הקשר הישיר ביניכם לבין עולם המשחק. - **תנועה אוטומטית**: כשהמשחק עצמו מחליט להזיז דברים – כמו ספינות אויב שצריכות לסייר על המסך בין אם אתם עושים משהו או לא. להזיז אובייקטים על מסך מחשב זה פשוט יותר ממה שאתם חושבים. זוכרים את הקואורדינטות x ו-y משיעורי מתמטיקה? זה בדיוק מה שאנחנו עובדים איתו כאן. כשגלילאו עקב אחרי ירחי צדק בשנת 1610, הוא למעשה עשה את אותו הדבר – שרטט מיקומים לאורך זמן כדי להבין דפוסי תנועה. להזיז דברים על המסך זה כמו ליצור אנימציה בספרון דפדוף – צריך לעקוב אחרי שלושה שלבים פשוטים: 1. **עדכון המיקום** – לשנות איפה האובייקט צריך להיות (אולי להזיז אותו 5 פיקסלים ימינה) 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); ``` **מה הקוד הזה עושה:** - **מעדכן** את קואורדינטת ה-x של הגיבור ב-5 פיקסלים כדי להזיז אותו אופקית - **מנקה** את כל שטח הקנבס כדי להסיר את הפריים הקודם - **ממלא** את הקנבס בצבע רקע שחור - **מצייר מחדש** את תמונת הגיבור במיקום החדש שלה ✅ האם אתם יכולים לחשוב על סיבה מדוע ציור מחדש של הגיבור מספר פריימים בשנייה עשוי לגרום לעלויות ביצועים? קראו על [חלופות לדפוס הזה](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas). ## טיפול באירועי מקלדת כאן אנחנו מחברים את קלט השחקן לפעולת המשחק. כשמישהו לוחץ על מקש רווח כדי לירות לייזר או מקיש על מקש חץ כדי להתחמק מאסטרואיד, המשחק שלכם צריך לזהות ולהגיב לקלט הזה. אירועי מקלדת מתרחשים ברמת החלון, כלומר כל חלון הדפדפן שלכם מקשיב ללחיצות המקלדת האלה. לחיצות עכבר, לעומת זאת, יכולות להיות קשורות לאלמנטים ספציפיים (כמו לחיצה על כפתור). עבור משחק החלל שלנו, נתמקד בשליטה באמצעות מקלדת מכיוון שזה מה שנותן לשחקנים את התחושה הקלאסית של משחקי ארקייד. זה מזכיר לי איך מפעילי טלגרף במאה ה-19 היו צריכים לתרגם קלט קוד מורס להודעות משמעותיות – אנחנו עושים משהו דומה, מתרגמים לחיצות מקשים לפקודות משחק. כדי לטפל באירוע, צריך להשתמש בשיטת `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` ✅ מניפולציה של אירועי מקשים שימושית גם מחוץ לפיתוח משחקים. אילו שימושים נוספים אתם יכולים לחשוב עליהם עבור הטכניקה הזו? ### מקשים מיוחדים: שימו לב! לחלק מהמפתחות יש התנהגויות מובנות בדפדפן שיכולות להפריע למשחק שלכם. מקשי החצים גוללים את הדף ומקש הרווח מקפיץ למטה – התנהגויות שאתם לא רוצים כשמישהו מנסה להטיס את החללית שלו. אנחנו יכולים למנוע את ההתנהגויות המובנות האלה ולתת למשחק שלנו לטפל בקלט במקום זאת. זה דומה לאופן שבו מתכנתים מוקדמים היו צריכים לעקוף הפרעות מערכת כדי ליצור התנהגויות מותאמות אישית – אנחנו פשוט עושים את זה ברמת הדפדפן. הנה איך: ```javascript const 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()` כדי לעצור את ההתנהגות המובנית של הדפדפן ## תנועה יזומה על ידי המשחק עכשיו נדבר על אובייקטים שזזים ללא קלט מהשחקן. חשבו על ספינות אויב שמפליגות על המסך, כדורים שעפים בקווים ישרים, או עננים שמרחפים ברקע. התנועה האוטונומית הזו גורמת לעולם המשחק שלכם להרגיש חי גם כשאף אחד לא נוגע בשליטה. אנחנו משתמשים בטיימרים מובנים של JavaScript כדי לעדכן מיקומים במרווחים קבועים. הרעיון הזה דומה לאופן שבו שעוני מטוטלת עובדים – מנגנון קבוע שמפעיל פעולות מתוזמנות באופן עקבי. הנה כמה פשוט זה יכול להיות: ```javascript const id = setInterval(() => { // Move the enemy on the y axis enemy.y += 10; }, 100); ``` **מה הקוד הזה עושה:** - **יוצר** טיימר שרץ כל 100 מילישניות - **מעדכן** את קואורדינטת ה-y של האויב ב-10 פיקסלים בכל פעם - **שומר** את מזהה המרווח כדי שנוכל לעצור אותו מאוחר יותר אם צריך - **מזיז** את האויב כלפי מטה על המסך באופן אוטומטי ## לולאת המשחק הנה הרעיון שמחבר הכל יחד – לולאת המשחק. אם המשחק שלכם היה סרט, לולאת המשחק הייתה המקרן, שמראה פריים אחרי פריים כל כך מהר שהכל נראה כאילו זז בצורה חלקה. לכל משחק יש אחת מהלולאות האלה שרצה מאחורי הקלעים. זו פונקציה שמעדכנת את כל אובייקטי המשחק, מציירת מחדש את המסך, וחוזרת על התהליך הזה ברציפות. זה עוקב אחרי הגיבור שלכם, כל האויבים, כל הלייזרים שעפים – כל מצב המשחק. הרעיון הזה מזכיר לי איך אנימטורים מוקדמים כמו וולט דיסני היו צריכים לצייר מחדש דמויות פריים אחרי פריים כדי ליצור את אשליית התנועה. אנחנו עושים את אותו הדבר, רק עם קוד במקום עפרונות. הנה איך לולאת משחק יכולה להיראות בדרך כלל, בקוד: ```javascript const 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(); } gameLoop(); }, 200); ``` **הבנת מבנה לולאת המשחק:** - **מנקה** את כל הקנבס כדי להסיר את הפריים הקודם - **ממלא** את הרקע בצבע אחיד - **מצייר** את כל אובייקטי המשחק במיקומם הנוכחי - **חוזר** על התהליך הזה כל 200 מילישניות כדי ליצור אנימציה חלקה - **מנהל** את קצב הפריימים על ידי שליטה בתזמון המרווחים ## המשך משחק החלל עכשיו נוסיף תנועה לסצנה הסטטית שבניתם קודם. אנחנו הולכים להפוך אותה מצילום מסך לחוויה אינטראקטיבית. נעבוד על זה שלב אחר שלב כדי להבטיח שכל חלק יבנה על הקודם. קחו את הקוד מהמקום שבו הפסקנו בשיעור הקודם (או התחילו עם הקוד בתיקיית [חלק II - התחלה](../../../../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` - **מגישה** את קבצי המשחק שלכם כדי שתוכלו לבדוק אותם בדפדפן הפקודה הזו תתחיל שרת HTTP בכתובת `http://localhost:5000`. פתחו דפדפן והכניסו את הכתובת הזו, כרגע זה אמור להציג את הגיבור ואת כל האויבים; שום דבר עדיין לא זז! ### הוספת קוד 1. **הוסיפו אובייקטים ייעודיים** עבור `hero`, `enemy` ו-`game object`, הם צריכים לכלול תכונות `x` ו-`y`. (זכרו את החלק על [ירושה או קומפוזיציה](../README.md)). *רמז*: התחילו בהוספת מחלקת `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); } } ``` **הבנת מחלקת הבסיס הזו:** - **מגדירה** תכונות משותפות שכל אובייקטי המשחק חולקים (מיקום, גודל, תמונה) - **כוללת** דגל `dead` למעקב אחרי האם האובייקט צריך להימחק - **מספקת** שיטת `draw()` שמציירת את האובייקט על הקנבס - **מגדירה** ערכי ברירת מחדל לכל התכונות שהמחלקות הנגזרות יכולות להחליף עכשיו, הרחיבו את `GameObject` כדי ליצור את `Hero` ו-`Enemy`: ```javascript class Hero extends GameObject { constructor(x, y) { super(x, y); this.width = 98; this.height = 75; this.type = "Hero"; this.speed = 5; } } ``` ```javascript class Enemy extends GameObject { constructor(x, y) { super(x, y); this.width = 98; this.height = 50; this.type = "Enemy"; const id = setInterval(() => { if (this.y < canvas.height - this.height) { this.y += 5; } else { console.log('Stopped at', this.y); clearInterval(id); } }, 300); } } ``` **מושגים מרכזיים במחלקות האלה:** - **יורש** מ-`GameObject` באמצעות מילת המפתח `extends` - **קורא** לבנאי ההורה עם `super(x, y)` - **מגדיר** מידות ותכונות ספציפיות לכל סוג אובייקט - **מיישם** תנועה אוטומטית עבור אויבים באמצעות `setInterval()` 2. **הוסיפו מטפלי אירועי מקשים** כדי לטפל בניווט מקשים (הזזת הגיבור למעלה/למטה שמאלה/ימינה) *זכרו* שזה מערכת קרטזית, פינה שמאלית-עליונה היא `0,0`. גם זכרו להוסיף קוד לעצירת *התנהגות ברירת מחדל* > **רמז**: צרו את פונקציית `onKeyDown` שלכם וצרפו אותה לחלון: ```javascript const onKeyDown = function (e) { console.log(e.keyCode); // Add the code from the lesson above to stop default behavior 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); ``` **מה מטפל האירועים הזה עושה:** - **מקשיב** לאירועי keydown על כל החלון - **רושם** את קוד המקש כדי לעזור לכם לבדוק אילו מקשים נלחצים - **מונע** את התנהגות ברירת המחדל של הדפדפן עבור מקשי חצים ומקש רווח - **מאפשר** למקשים אחרים לפעול כרגיל בדקו את קונסולת הדפדפן שלכם בשלב זה, וצפו בלחיצות המקלדת שנרשמות. 3. **יישמו** את [תבנית Pub sub](../README.md), זה ישמור על הקוד שלכם נקי ככל שתמשיכו בחלקים הבאים. תבנית Publish-Subscribe עוזרת לארגן את הקוד שלכם על ידי הפרדת זיהוי אירועים מטיפול באירועים. זה הופך את הקוד שלכם ליותר מודולרי וקל לתחזוקה. כדי לבצע את החלק האחרון, תוכלו: 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); } }); ``` **מה מערכת האירועים הזו עושה:** - **מזהה** קלט מקלדת וממירה אותו לאירועי משחק מותאמים אישית - **מפרידה** בין זיהוי קלט ללוגיקת המשחק - **מקלה** על שינוי שליטה מאוחר יותר מבלי להשפיע על קוד המשחק - **מאפשרת** למערכות רבות להגיב לאותו קלט 2. **צרו מחלקת EventEmitter** כדי לפרסם ולהירשם להודעות: ```javascript class EventEmitter { constructor() { this.listeners = {}; } on(message, listener) { if (!this.listeners[message]) { this.listeners[message] = []; } this.listeners[message].push(listener); } 3. **הוסיפו קבועים** והגדירו את ה-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(); ``` **הבנת ההגדרה:** - **מגדירה** קבועי הודעות כדי להימנע משגיאות ולהקל על שינוי קוד - **מכריזה** על משתנים עבור תמונות, הקשר הקנבס ומצב המשחק - **יוצרת** EventEmitter גלובלי עבור מערכת pub-sub - **מאתחלת** מערך שמחזיק את כל אובייקטי המשחק 4. **אתחלו את המשחק** ```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; }); 4. **הגדירו את לולאת המשחק** בצעו שינוי בפונקציית `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(); const gameLoopId = setInterval(() => { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = "black"; ctx.fillRect(0, 0, canvas.width, canvas.height); drawGameObjects(ctx); }, 100); }; ``` **הבנת הגדרת המשחק:** - **ממתין** לטעינת הדף לחלוטין לפני התחלה - **מקבל** את אלמנט הקנבס והקשר הרינדור הדו-ממדי שלו - **טוען** את כל נכסי התמונה באופן אסינכרוני באמצעות `await` - **מתחיל** את לולאת המשחק שרצה במרווחים של 100 מילישניות (10 FPS) - **מנקה** ומצייר מחדש את כל המסך בכל פריים 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)); } ``` **הבנת פונקציית הציור:** - **עובר** על כל אובייקטי המשחק במערך - **מפעיל** את המתודה `draw()` על כל אובייקט - **מעביר** את הקונטקסט של הקנבס כדי שהאובייקטים יוכלו לצייר את עצמם האויבים שלך צריכים להתחיל להתקדם לעבר חללית הגיבור שלך! } } ``` and add a `createHero()` function to do a similar process for the hero. ```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)); } ``` האויבים שלך צריכים להתחיל להתקדם לעבר חללית הגיבור שלך! --- ## אתגר סוכן GitHub Copilot 🚀 הנה אתגר שישפר את הפוליש של המשחק שלך: הוספת גבולות ושליטה חלקה. כרגע, הגיבור שלך יכול לעוף מחוץ למסך, והתנועה עשויה להרגיש לא חלקה. **המשימה שלך:** הפוך את החללית שלך למציאותית יותר על ידי יישום גבולות מסך ותנועה חלקה. זה דומה לאופן שבו מערכות בקרת הטיסה של נאס"א מונעות מחלליות לחרוג מפרמטרי פעולה בטוחים. **מה לבנות:** צור מערכת ששומרת את חללית הגיבור שלך על המסך, ותגרום לשליטה להרגיש חלקה. כאשר שחקנים מחזיקים מקש חץ, החללית צריכה להחליק ברציפות במקום לנוע בצעדים נפרדים. שקול להוסיף משוב חזותי כאשר החללית מגיעה לגבולות המסך – אולי אפקט עדין שמציין את קצה אזור המשחק. למד עוד על [מצב סוכן](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) כאן. ## 🚀 אתגר ארגון קוד הופך לחשוב יותר ויותר ככל שהפרויקטים גדלים. אולי שמתם לב שהקובץ שלכם מתחיל להיות עמוס בפונקציות, משתנים וקלאסים מעורבבים יחד. זה מזכיר לי איך המהנדסים שארגנו את קוד משימת אפולו היו צריכים ליצור מערכות ברורות וניתנות לתחזוקה שצוותים מרובים יוכלו לעבוד עליהן בו זמנית. **המשימה שלך:** חשוב כמו ארכיטקט תוכנה. איך היית מארגן את הקוד שלך כך שישה חודשים מעכשיו, אתה (או חבר צוות) תוכל להבין מה קורה? גם אם הכל נשאר בקובץ אחד כרגע, אתה יכול ליצור ארגון טוב יותר: - **קיבוץ פונקציות קשורות** יחד עם כותרות הערות ברורות - **הפרדת תחומי אחריות** - שמור את לוגיקת המשחק בנפרד מהציור - **שימוש בשמות עקביים** למשתנים ולפונקציות - **יצירת מודולים** או מרחבי שמות לארגון היבטים שונים של המשחק שלך - **הוספת תיעוד** שמסביר את המטרה של כל חלק מרכזי **שאלות למחשבה:** - אילו חלקים בקוד שלך הכי קשה להבין כשאתה חוזר אליהם? - איך תוכל לארגן את הקוד שלך כך שיהיה קל יותר למישהו אחר לתרום? - מה יקרה אם תרצה להוסיף תכונות חדשות כמו חיזוקים או סוגי אויבים שונים? ## מבחן לאחר ההרצאה [מבחן לאחר ההרצאה](https://ff-quizzes.netlify.app/web/quiz/34) ## סקירה ולימוד עצמי בנינו הכל מאפס, שזה נהדר ללמידה, אבל הנה סוד קטן – יש כמה מסגרות JavaScript מדהימות שיכולות להתמודד עם הרבה מהעבודה הקשה עבורך. ברגע שתרגיש בנוח עם היסודות שכיסינו, כדאי [לחקור מה זמין](https://github.com/collections/javascript-game-engines). חשוב על מסגרות כמו שיש לך ארגז כלים מצויד היטב במקום להכין כל כלי בעצמך. הן יכולות לפתור רבות מאותן אתגרי ארגון קוד שדיברנו עליהם, בנוסף להציע תכונות שייקח שבועות לבנות בעצמך. **דברים שכדאי לחקור:** - איך מנועי משחק מארגנים קוד – תתפלא מהדפוסים החכמים שהם משתמשים בהם - טריקים לשיפור ביצועים כדי לגרום למשחקי קנבס לרוץ בצורה חלקה - תכונות מודרניות של JavaScript שיכולות להפוך את הקוד שלך לנקי וניתן לתחזוקה - גישות שונות לניהול אובייקטי משחק והקשרים ביניהם ## משימה [הוסף הערות לקוד שלך](assignment.md) --- **כתב ויתור**: מסמך זה תורגם באמצעות שירות תרגום AI [Co-op Translator](https://github.com/Azure/co-op-translator). למרות שאנו שואפים לדיוק, יש להיות מודעים לכך שתרגומים אוטומטיים עשויים להכיל שגיאות או אי דיוקים. המסמך המקורי בשפתו המקורית צריך להיחשב כמקור סמכותי. עבור מידע קריטי, מומלץ להשתמש בתרגום מקצועי אנושי. איננו אחראים לאי הבנות או לפרשנויות שגויות הנובעות משימוש בתרגום זה.