# สร้างเกมอวกาศ ตอนที่ 3: เพิ่มการเคลื่อนไหว ลองนึกถึงเกมโปรดของคุณ – สิ่งที่ทำให้เกมน่าดึงดูดไม่ใช่แค่กราฟิกที่สวยงาม แต่เป็นวิธีที่ทุกอย่างเคลื่อนไหวและตอบสนองต่อการกระทำของคุณ ตอนนี้เกมอวกาศของคุณเหมือนภาพวาดที่สวยงาม แต่เรากำลังจะเพิ่มการเคลื่อนไหวเพื่อทำให้มันมีชีวิตชีวา เมื่อวิศวกรของ NASA เขียนโปรแกรมคอมพิวเตอร์นำทางสำหรับภารกิจ Apollo พวกเขาเผชิญกับความท้าทายที่คล้ายกัน: จะทำให้ยานอวกาศตอบสนองต่อการควบคุมของนักบินในขณะที่ยังคงรักษาการปรับเส้นทางอัตโนมัติได้อย่างไร หลักการที่เราจะเรียนรู้ในวันนี้สะท้อนแนวคิดเดียวกัน – การจัดการการเคลื่อนไหวที่ควบคุมโดยผู้เล่นควบคู่ไปกับพฤติกรรมของระบบอัตโนมัติ ในบทเรียนนี้ คุณจะได้เรียนรู้วิธีทำให้ยานอวกาศลอยไปบนหน้าจอ ตอบสนองต่อคำสั่งของผู้เล่น และสร้างรูปแบบการเคลื่อนไหวที่ราบรื่น เราจะอธิบายทุกอย่างให้เข้าใจง่ายและเป็นขั้นตอน เมื่อจบบทเรียนนี้ ผู้เล่นจะสามารถบังคับยานฮีโร่ของพวกเขาไปรอบ ๆ หน้าจอ ในขณะที่ยานศัตรูลาดตระเวนอยู่ด้านบน และที่สำคัญที่สุด คุณจะเข้าใจหลักการสำคัญที่ขับเคลื่อนระบบการเคลื่อนไหวในเกม ## แบบทดสอบก่อนเรียน [แบบทดสอบก่อนเรียน](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) ## จัดการเหตุการณ์คีย์บอร์ด นี่คือจุดที่เราจะเชื่อมโยงการป้อนข้อมูลของผู้เล่นกับการกระทำในเกม เมื่อมีคนกด spacebar เพื่อยิงเลเซอร์หรือกดปุ่มลูกศรเพื่อหลบดาวเคราะห์น้อย เกมของคุณต้องตรวจจับและตอบสนองต่อการป้อนข้อมูลนั้น เหตุการณ์คีย์บอร์ดเกิดขึ้นในระดับหน้าต่าง หมายความว่าหน้าต่างเบราว์เซอร์ทั้งหมดของคุณกำลังฟังการกดปุ่มเหล่านั้น ในทางกลับกัน การคลิกเมาส์สามารถผูกกับองค์ประกอบเฉพาะ (เช่น การคลิกปุ่ม) สำหรับเกมอวกาศของเรา เราจะเน้นที่การควบคุมด้วยคีย์บอร์ดเพราะนั่นคือสิ่งที่ให้ความรู้สึกเหมือนเกมอาร์เคดคลาสสิก สิ่งนี้ทำให้ฉันนึกถึงวิธีที่ผู้ปฏิบัติการโทรเลขในศตวรรษที่ 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` ✅ การจัดการเหตุการณ์คีย์มีประโยชน์นอกเหนือจากการพัฒนาเกม คุณคิดว่าเทคนิคนี้สามารถนำไปใช้ในด้านอื่น ๆ ได้อย่างไร? ### ปุ่มพิเศษ: ข้อควรระวัง! บางปุ่มมีพฤติกรรมในตัวเบราว์เซอร์ที่อาจรบกวนเกมของคุณ ปุ่มลูกศรเลื่อนหน้า และ spacebar กระโดดลง – พฤติกรรมที่คุณไม่ต้องการเมื่อมีคนพยายามควบคุมยานอวกาศของพวกเขา เราสามารถป้องกันพฤติกรรมเริ่มต้นเหล่านี้และให้เกมของเราจัดการการป้อนข้อมูลแทน สิ่งนี้คล้ายกับวิธีที่โปรแกรมเมอร์คอมพิวเตอร์ยุคแรกต้องเขียนทับการขัดจังหวะของระบบเพื่อสร้างพฤติกรรมที่กำหนดเอง – เราแค่ทำในระดับเบราว์เซอร์ นี่คือวิธี: ```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); ``` **การทำความเข้าใจโค้ดป้องกันนี้:** - **ตรวจสอบ** รหัสปุ่มเฉพาะที่อาจทำให้เกิดพฤติกรรมเบราว์เซอร์ที่ไม่ต้องการ - **ป้องกัน** การกระทำเริ่มต้นของเบราว์เซอร์สำหรับปุ่มลูกศรและ spacebar - **อนุญาต** ให้ปุ่มอื่นทำงานตามปกติ - **ใช้** `e.preventDefault()` เพื่อหยุดพฤติกรรมในตัวของเบราว์เซอร์ ## การเคลื่อนไหวที่เกิดจากเกม ตอนนี้เรามาพูดถึงวัตถุที่เคลื่อนไหวโดยไม่มีการป้อนข้อมูลจากผู้เล่น ลองนึกถึงยานศัตรูที่ล่องลอยบนหน้าจอ กระสุนที่บินเป็นเส้นตรง หรือเมฆที่ลอยอยู่ในพื้นหลัง การเคลื่อนไหวอัตโนมัตินี้ทำให้โลกของเกมของคุณรู้สึกมีชีวิตชีวาแม้ไม่มีใครแตะต้องการควบคุม เราใช้ตัวจับเวลาที่มีอยู่ใน JavaScript เพื่ออัปเดตตำแหน่งในช่วงเวลาปกติ แนวคิดนี้คล้ายกับวิธีการทำงานของนาฬิกาลูกตุ้ม – กลไกปกติที่กระตุ้นการกระทำที่สม่ำเสมอและตรงเวลา นี่คือวิธีที่ง่าย: ```javascript const id = setInterval(() => { // Move the enemy on the y axis enemy.y += 10; }, 100); ``` **สิ่งที่โค้ดการเคลื่อนไหวนี้ทำ:** - **สร้าง** ตัวจับเวลาที่ทำงานทุก ๆ 100 มิลลิวินาที - **อัปเดต** พิกัด y ของศัตรูโดย 10 พิกเซลทุกครั้ง - **เก็บ** ID ของช่วงเวลาเพื่อหยุดมันในภายหลังหากจำเป็น - **เคลื่อนย้าย** ศัตรูลงบนหน้าจอโดยอัตโนมัติ ## วงลูปของเกม นี่คือแนวคิดที่ผูกทุกอย่างเข้าด้วยกัน – วงลูปของเกม หากเกมของคุณเป็นภาพยนตร์ วงลูปของเกมจะเป็นเครื่องฉายภาพยนตร์ที่แสดงเฟรมต่อเฟรมอย่างรวดเร็วจนทุกอย่างดูเหมือนเคลื่อนไหวอย่างราบรื่น ทุกเกมมีวงลูปเหล่านี้ทำงานอยู่เบื้องหลัง เป็นฟังก์ชันที่อัปเดตวัตถุเกมทั้งหมด วาดหน้าจอใหม่ และทำซ้ำกระบวนการนี้อย่างต่อเนื่อง สิ่งนี้ช่วยติดตามฮีโร่ของคุณ ศัตรูทั้งหมด เลเซอร์ที่บินไปรอบ ๆ – สถานะของเกมทั้งหมด แนวคิดนี้ทำให้ฉันนึกถึงวิธีที่นักวาดภาพยนตร์ยุคแรก ๆ เช่น Walt Disney ต้องวาดตัวละครใหม่เฟรมต่อเฟรมเพื่อสร้างภาพลวงตาของการเคลื่อนไหว เรากำลังทำสิ่งเดียวกัน เพียงแค่ใช้โค้ดแทนดินสอ นี่คือสิ่งที่วงลูปของเกมมักจะมีลักษณะเป็นโค้ด: ```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 มิลลิวินาทีเพื่อสร้างภาพเคลื่อนไหวที่ราบรื่น - **จัดการ** อัตราเฟรมโดยการควบคุมช่วงเวลาเวลา ## การพัฒนาเกมอวกาศต่อไป ตอนนี้เราจะเพิ่มการเคลื่อนไหวให้กับฉากนิ่งที่คุณสร้างไว้ก่อนหน้านี้ เรากำลังจะเปลี่ยนมันจากภาพหน้าจอเป็นประสบการณ์แบบโต้ตอบ เราจะทำงานผ่านขั้นตอนนี้ทีละขั้นตอนเพื่อให้แน่ใจว่าแต่ละส่วนสร้างขึ้นจากส่วนก่อนหน้า ดึงโค้ดจากที่ที่เราหยุดในบทเรียนก่อนหน้า (หรือเริ่มต้นด้วยโค้ดในโฟลเดอร์ [Part II- starter](../../../../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 Server ที่อยู่ `http://localhost:5000` - **ให้บริการ** ไฟล์เกมของคุณเพื่อทดสอบในเบราว์เซอร์ คำสั่งด้านบนจะเริ่ม HTTP Server ที่อยู่ `http://localhost:5000` เปิดเบราว์เซอร์และใส่ที่อยู่นั้น ตอนนี้มันควรจะแสดงฮีโร่และศัตรูทั้งหมด; แต่ยังไม่มีอะไรเคลื่อนไหว – ยัง! ### เพิ่มโค้ด 1. **เพิ่มออบเจ็กต์เฉพาะ** สำหรับ `hero` และ `enemy` และ `game object` ซึ่งควรมีคุณสมบัติ `x` และ `y` (จำส่วนเกี่ยวกับ [Inheritance หรือ composition](../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); } } ``` **การทำความเข้าใจคลาสพื้นฐานนี้:** - **กำหนด** คุณสมบัติทั่วไปที่วัตถุเกมทั้งหมดมีร่วมกัน (ตำแหน่ง ขนาด ภาพ) - **รวมถึง** ธง `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 ในหน้าต่างทั้งหมด - **บันทึก** รหัสคีย์เพื่อช่วยคุณดีบักว่าปุ่มใดถูกกด - **ป้องกัน** พฤติกรรมเริ่มต้นของเบราว์เซอร์สำหรับปุ่มลูกศรและ spacebar - **อนุญาต** ให้ปุ่มอื่นทำงานตามปกติ ตรวจสอบคอนโซลเบราว์เซอร์ของคุณ ณ จุดนี้ และดูการกดปุ่มที่ถูกบันทึกไว้ 3. **นำไปใช้** [Pub sub pattern](../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); } }); ``` **สิ่งที่ระบบเหตุการณ์นี้ทำ:** - **ตรวจจับ** การป้อนข้อมูลคีย์บอร์ดและแปลงเป็นเหตุการณ์เกมที่กำหนดเอง - **แยก** การตรวจจับการป้อนข้อมูลออกจากตรรกะของเกม - **ทำให้** การเปลี่ยนแปลงการควบคุมในภายหลังง่ายขึ้นโดยไม่กระทบต่อโค้ดเกม - **อนุญาต** ให้ระบบหลายระบบ - **สร้าง** ตารางของศัตรูโดยใช้ลูปซ้อนกัน - **กำหนด** ภาพศัตรูให้กับวัตถุศัตรูแต่ละตัว - **เพิ่ม** ศัตรูแต่ละตัวลงในอาร์เรย์วัตถุเกมแบบ global และเพิ่มฟังก์ชัน `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()` บนวัตถุแต่ละตัว - **ส่งผ่าน** context ของ canvas เพื่อให้วัตถุสามารถแสดงผลตัวเองได้ ศัตรูของคุณควรเริ่มเคลื่อนที่เข้าหายานอวกาศฮีโร่ของคุณ! } } ``` 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 Agent Challenge 🚀 นี่คือความท้าทายที่จะช่วยปรับปรุงความสมบูรณ์ของเกมของคุณ: การเพิ่มขอบเขตและการควบคุมที่ลื่นไหล ปัจจุบันฮีโร่ของคุณสามารถบินออกนอกหน้าจอได้ และการเคลื่อนไหวอาจดูไม่ราบรื่น **ภารกิจของคุณ:** ทำให้ยานอวกาศของคุณรู้สึกสมจริงมากขึ้นโดยการเพิ่มขอบเขตหน้าจอและการควบคุมที่ลื่นไหล นี่คล้ายกับระบบควบคุมการบินของ NASA ที่ป้องกันไม่ให้ยานอวกาศเกินพารามิเตอร์การปฏิบัติการที่ปลอดภัย **สิ่งที่ต้องสร้าง:** สร้างระบบที่ทำให้ยานอวกาศฮีโร่ของคุณอยู่ในหน้าจอ และทำให้การควบคุมรู้สึกลื่นไหล เมื่อผู้เล่นกดปุ่มลูกศรค้างไว้ ยานควรลื่นไหลต่อเนื่องแทนที่จะเคลื่อนที่เป็นขั้นๆ ลองพิจารณาเพิ่มการตอบสนองภาพเมื่อยานถึงขอบหน้าจอ – อาจเป็นเอฟเฟกต์เล็กๆ เพื่อบ่งบอกถึงขอบพื้นที่เล่น เรียนรู้เพิ่มเติมเกี่ยวกับ [agent mode](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) ที่นี่ ## 🚀 Challenge การจัดระเบียบโค้ดมีความสำคัญมากขึ้นเมื่อโปรเจกต์เติบโต คุณอาจสังเกตเห็นว่าไฟล์ของคุณเริ่มเต็มไปด้วยฟังก์ชัน ตัวแปร และคลาสที่ปะปนกัน นี่ทำให้นึกถึงวิศวกรที่จัดระเบียบโค้ดของภารกิจ Apollo ที่ต้องสร้างระบบที่ชัดเจนและดูแลรักษาได้ ซึ่งทีมหลายทีมสามารถทำงานร่วมกันได้ **ภารกิจของคุณ:** คิดแบบสถาปนิกซอฟต์แวร์ คุณจะจัดระเบียบโค้ดของคุณอย่างไรเพื่อให้หกเดือนจากนี้ คุณ (หรือเพื่อนร่วมทีม) สามารถเข้าใจสิ่งที่เกิดขึ้นได้? แม้ว่าทุกอย่างจะยังอยู่ในไฟล์เดียวในตอนนี้ คุณสามารถสร้างการจัดระเบียบที่ดีขึ้นได้: - **จัดกลุ่มฟังก์ชันที่เกี่ยวข้อง** พร้อมหัวข้อคอมเมนต์ที่ชัดเจน - **แยกความรับผิดชอบ** - แยกตรรกะของเกมออกจากการแสดงผล - **ใช้ชื่อที่สอดคล้องกัน** สำหรับตัวแปรและฟังก์ชัน - **สร้างโมดูล** หรือ namespace เพื่อจัดระเบียบส่วนต่างๆ ของเกม - **เพิ่มเอกสารประกอบ** ที่อธิบายวัตถุประสงค์ของแต่ละส่วนสำคัญ **คำถามสะท้อน:** - ส่วนใดของโค้ดของคุณที่เข้าใจยากที่สุดเมื่อคุณกลับมาดู? - คุณจะจัดระเบียบโค้ดอย่างไรเพื่อให้ง่ายขึ้นสำหรับคนอื่นที่จะมีส่วนร่วม? - จะเกิดอะไรขึ้นถ้าคุณต้องการเพิ่มฟีเจอร์ใหม่ เช่น power-ups หรือประเภทศัตรูที่แตกต่างกัน? ## Post-Lecture Quiz [Post-lecture quiz](https://ff-quizzes.netlify.app/web/quiz/34) ## Review & Self Study เรากำลังสร้างทุกอย่างตั้งแต่เริ่มต้น ซึ่งยอดเยี่ยมสำหรับการเรียนรู้ แต่มีความลับเล็กๆ น้อยๆ – มีเฟรมเวิร์ก JavaScript ที่น่าทึ่งบางตัวที่สามารถจัดการงานหนักให้คุณได้ เมื่อคุณรู้สึกสบายใจกับพื้นฐานที่เราได้ครอบคลุมแล้ว คุ้มค่าที่จะ [สำรวจสิ่งที่มีอยู่](https://github.com/collections/javascript-game-engines) คิดถึงเฟรมเวิร์กเหมือนมีชุดเครื่องมือที่ครบครันแทนที่จะสร้างเครื่องมือทุกชิ้นด้วยตัวเอง พวกมันสามารถแก้ปัญหาความท้าทายในการจัดระเบียบโค้ดที่เราพูดถึงได้มากมาย และยังมีฟีเจอร์ที่อาจใช้เวลาหลายสัปดาห์ในการสร้างเอง **สิ่งที่ควรสำรวจ:** - วิธีที่ game engines จัดระเบียบโค้ด – คุณจะทึ่งกับรูปแบบที่ชาญฉลาดที่พวกเขาใช้ - เทคนิคการเพิ่มประสิทธิภาพเพื่อทำให้เกม canvas ทำงานได้อย่างราบรื่น - ฟีเจอร์ JavaScript สมัยใหม่ที่สามารถทำให้โค้ดของคุณสะอาดและดูแลรักษาได้ง่ายขึ้น - วิธีการต่างๆ ในการจัดการวัตถุเกมและความสัมพันธ์ของพวกมัน ## Assignment [Comment your code](assignment.md) --- **ข้อจำกัดความรับผิดชอบ**: เอกสารนี้ได้รับการแปลโดยใช้บริการแปลภาษา AI [Co-op Translator](https://github.com/Azure/co-op-translator) แม้ว่าเราจะพยายามให้การแปลมีความถูกต้อง แต่โปรดทราบว่าการแปลโดยอัตโนมัติอาจมีข้อผิดพลาดหรือความไม่ถูกต้อง เอกสารต้นฉบับในภาษาดั้งเดิมควรถือเป็นแหล่งข้อมูลที่เชื่อถือได้ สำหรับข้อมูลที่สำคัญ ขอแนะนำให้ใช้บริการแปลภาษามืออาชีพ เราไม่รับผิดชอบต่อความเข้าใจผิดหรือการตีความผิดที่เกิดจากการใช้การแปลนี้