# สร้างเกมอวกาศ ตอนที่ 2: วาดฮีโร่และมอนสเตอร์ลงบน Canvas Canvas API เป็นหนึ่งในฟีเจอร์ที่ทรงพลังที่สุดของการพัฒนาเว็บสำหรับการสร้างกราฟิกแบบไดนามิกและโต้ตอบได้ในเบราว์เซอร์ของคุณ ในบทเรียนนี้ เราจะเปลี่ยน `` HTML ที่ว่างเปล่าให้กลายเป็นโลกของเกมที่เต็มไปด้วยฮีโร่และมอนสเตอร์ ลองนึกถึง canvas ว่าเป็นกระดานศิลปะดิจิทัลที่โค้ดกลายเป็นภาพ เราจะต่อยอดจากสิ่งที่คุณเรียนรู้ในบทเรียนก่อนหน้า และตอนนี้เราจะเจาะลึกในด้านภาพ คุณจะได้เรียนรู้วิธีโหลดและแสดงสไปรท์ของเกม การจัดตำแหน่งองค์ประกอบอย่างแม่นยำ และสร้างพื้นฐานภาพสำหรับเกมอวกาศของคุณ นี่คือสะพานเชื่อมระหว่างหน้าเว็บแบบคงที่และประสบการณ์แบบไดนามิกและโต้ตอบได้ เมื่อจบบทเรียนนี้ คุณจะมีฉากเกมที่สมบูรณ์พร้อมกับยานฮีโร่ที่วางตำแหน่งอย่างถูกต้องและการจัดรูปแบบศัตรูที่พร้อมสำหรับการต่อสู้ คุณจะเข้าใจวิธีที่เกมสมัยใหม่แสดงกราฟิกในเบราว์เซอร์ และได้รับทักษะในการสร้างประสบการณ์ภาพแบบโต้ตอบของคุณเอง มาสำรวจกราฟิกของ canvas และทำให้เกมอวกาศของคุณมีชีวิตชีวากันเถอะ! ## แบบทดสอบก่อนเรียน [แบบทดสอบก่อนเรียน](https://ff-quizzes.netlify.app/web/quiz/31) ## Canvas คืออะไร แล้ว `` คืออะไร? มันคือโซลูชันของ HTML5 สำหรับการสร้างกราฟิกและแอนิเมชันแบบไดนามิกในเบราว์เซอร์เว็บ ไม่เหมือนกับภาพหรือวิดีโอทั่วไปที่เป็นแบบคงที่ Canvas ให้คุณควบคุมระดับพิกเซลในทุกสิ่งที่ปรากฏบนหน้าจอ ซึ่งทำให้มันเหมาะสำหรับเกม การแสดงผลข้อมูล และศิลปะเชิงโต้ตอบ ลองนึกถึงมันว่าเป็นพื้นผิวการวาดที่สามารถเขียนโปรแกรมได้ โดยที่ JavaScript เป็นแปรงวาดของคุณ โดยปกติแล้ว องค์ประกอบ canvas จะดูเหมือนสี่เหลี่ยมโปร่งใสที่ว่างเปล่าบนหน้าเว็บของคุณ แต่ศักยภาพที่แท้จริงของมันจะปรากฏเมื่อคุณใช้ JavaScript เพื่อวาดรูปร่าง โหลดภาพ สร้างแอนิเมชัน และทำให้สิ่งต่าง ๆ ตอบสนองต่อการโต้ตอบของผู้ใช้ มันคล้ายกับวิธีที่ผู้บุกเบิกกราฟิกคอมพิวเตอร์ในยุคแรก ๆ ที่ Bell Labs ในปี 1960 ต้องเขียนโปรแกรมพิกเซลทุกจุดเพื่อสร้างแอนิเมชันดิจิทัลครั้งแรก ✅ อ่าน [เพิ่มเติมเกี่ยวกับ Canvas API](https://developer.mozilla.org/docs/Web/API/Canvas_API) บน MDN นี่คือตัวอย่างการประกาศ canvas โดยทั่วไป ซึ่งเป็นส่วนหนึ่งของ body ของหน้าเว็บ: ```html ``` **สิ่งที่โค้ดนี้ทำ:** - **ตั้งค่า** attribute `id` เพื่อให้คุณสามารถอ้างอิงถึงองค์ประกอบ canvas นี้ใน JavaScript - **กำหนด** `width` เป็นพิกเซลเพื่อควบคุมขนาดแนวนอนของ canvas - **กำหนด** `height` เป็นพิกเซลเพื่อกำหนดขนาดแนวตั้งของ canvas ## การวาดรูปทรงเรขาคณิตง่าย ๆ ตอนนี้คุณรู้แล้วว่า canvas คืออะไร ลองมาสำรวจวิธีการวาดบนมันกัน! Canvas ใช้ระบบพิกัดที่อาจจะคุ้นเคยจากชั้นเรียนคณิตศาสตร์ แต่มีจุดพลิกผันสำคัญที่เฉพาะเจาะจงสำหรับกราฟิกคอมพิวเตอร์ Canvas ใช้ระบบพิกัด Cartesian ที่มีแกน x (แนวนอน) และแกน y (แนวตั้ง) เพื่อกำหนดตำแหน่งของทุกสิ่งที่คุณวาด แต่มีความแตกต่างสำคัญ: ไม่เหมือนกับระบบพิกัดในชั้นเรียนคณิตศาสตร์ จุดเริ่มต้น `(0,0)` จะอยู่ที่มุมบนซ้าย โดยค่าของ x จะเพิ่มขึ้นเมื่อคุณเลื่อนไปทางขวา และค่าของ y จะเพิ่มขึ้นเมื่อคุณเลื่อนลง วิธีนี้มีต้นกำเนิดมาจากจอคอมพิวเตอร์ในยุคแรก ๆ ที่ลำแสงอิเล็กตรอนสแกนจากบนลงล่าง ทำให้มุมบนซ้ายเป็นจุดเริ่มต้นที่เป็นธรรมชาติ ![กริดของ canvas](../../../../translated_images/canvas_grid.5f209da785ded492a01ece440e3032afe51efa500cc2308e5ea4252487ceaf0b.th.png) > ภาพจาก [MDN](https://developer.mozilla.org/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes) ในการวาดบน canvas คุณจะทำตามกระบวนการสามขั้นตอนเดียวกันที่เป็นพื้นฐานของกราฟิก canvas ทั้งหมด เมื่อคุณทำสิ่งนี้สองสามครั้ง มันจะกลายเป็นธรรมชาติ: 1. **อ้างอิง**ถึงองค์ประกอบ Canvas ของคุณจาก DOM (เหมือนกับองค์ประกอบ HTML อื่น ๆ) 2. **รับบริบทการเรนเดอร์ 2D** – นี่คือเครื่องมือที่มีวิธีการวาดทั้งหมด 3. **เริ่มวาด!** ใช้วิธีการในตัวของบริบทเพื่อสร้างกราฟิกของคุณ นี่คือลักษณะของโค้ด: ```javascript // Step 1: Get the canvas element const canvas = document.getElementById("myCanvas"); // Step 2: Get the 2D rendering context const ctx = canvas.getContext("2d"); // Step 3: Set fill color and draw a rectangle ctx.fillStyle = 'red'; ctx.fillRect(0, 0, 200, 200); // x, y, width, height ``` **มาดูทีละขั้นตอน:** - เรา **ดึง**องค์ประกอบ canvas โดยใช้ ID และเก็บไว้ในตัวแปร - เรา **รับ**บริบทการเรนเดอร์ 2D – นี่คือชุดเครื่องมือที่เต็มไปด้วยวิธีการวาด - เรา **บอก** canvas ว่าเราต้องการเติมสีแดงโดยใช้ property `fillStyle` - เรา **วาด**สี่เหลี่ยมที่เริ่มต้นที่มุมบนซ้าย (0,0) ที่มีความกว้างและสูง 200 พิกเซล ✅ Canvas API ส่วนใหญ่เน้นที่รูปร่าง 2D แต่คุณสามารถวาดองค์ประกอบ 3D ลงในเว็บไซต์ได้; สำหรับสิ่งนี้ คุณอาจใช้ [WebGL API](https://developer.mozilla.org/docs/Web/API/WebGL_API) คุณสามารถวาดสิ่งต่าง ๆ ได้หลากหลายด้วย Canvas API เช่น: - **รูปร่างเรขาคณิต** เราได้แสดงวิธีการวาดสี่เหลี่ยมแล้ว แต่ยังมีอีกมากมายที่คุณสามารถวาดได้ - **ข้อความ** คุณสามารถวาดข้อความด้วยฟอนต์และสีใดก็ได้ที่คุณต้องการ - **ภาพ** คุณสามารถวาดภาพจากไฟล์ภาพ เช่น .jpg หรือ .png เป็นต้น ✅ ลองทำดู! คุณรู้วิธีวาดสี่เหลี่ยมแล้ว คุณสามารถวาดวงกลมลงบนหน้าได้หรือไม่? ลองดูภาพวาด Canvas ที่น่าสนใจบน CodePen นี่คือตัวอย่างที่ [น่าประทับใจ](https://codepen.io/dissimulate/pen/KrAwx) ## โหลดและวาดภาพ การวาดรูปร่างพื้นฐานมีประโยชน์สำหรับการเริ่มต้น แต่เกมส่วนใหญ่ต้องการภาพจริง! สไปรท์ พื้นหลัง และพื้นผิวคือสิ่งที่ทำให้เกมมีความน่าสนใจทางภาพ การโหลดและแสดงภาพบน canvas ทำงานแตกต่างจากการวาดรูปร่างเรขาคณิต แต่ก็ง่ายเมื่อคุณเข้าใจกระบวนการ เราจำเป็นต้องสร้างวัตถุ `Image` โหลดไฟล์ภาพของเรา (สิ่งนี้เกิดขึ้นแบบอะซิงโครนัส หมายถึง "ในพื้นหลัง") และวาดมันลงบน canvas เมื่อมันพร้อม วิธีนี้ช่วยให้ภาพของคุณแสดงผลได้อย่างถูกต้องโดยไม่ทำให้แอปพลิเคชันของคุณหยุดชะงักขณะโหลด ### การโหลดภาพพื้นฐาน ```javascript const img = new Image(); img.src = 'path/to/my/image.png'; img.onload = () => { // Image loaded and ready to be used console.log('Image loaded successfully!'); }; ``` **สิ่งที่เกิดขึ้นในโค้ดนี้:** - เรา **สร้าง**วัตถุ Image ใหม่เพื่อเก็บสไปรท์หรือพื้นผิวของเรา - เรา **บอก**ว่าต้องโหลดไฟล์ภาพใดโดยตั้งค่าเส้นทาง source - เรา **ฟัง**เหตุการณ์ load เพื่อให้เรารู้ว่าเมื่อใดภาพพร้อมใช้งาน ### วิธีที่ดีกว่าในการโหลดภาพ นี่คือวิธีที่มีประสิทธิภาพมากขึ้นในการจัดการการโหลดภาพที่นักพัฒนามืออาชีพใช้กันทั่วไป เราจะห่อการโหลดภาพในฟังก์ชันที่ใช้ Promise – วิธีนี้ซึ่งได้รับความนิยมเมื่อ JavaScript Promises กลายเป็นมาตรฐานใน ES6 ทำให้โค้ดของคุณมีการจัดระเบียบมากขึ้นและจัดการข้อผิดพลาดได้อย่างดี: ```javascript function loadAsset(path) { return new Promise((resolve, reject) => { const img = new Image(); img.src = path; img.onload = () => { resolve(img); }; img.onerror = () => { reject(new Error(`Failed to load image: ${path}`)); }; }); } // Modern usage with async/await async function initializeGame() { try { const heroImg = await loadAsset('hero.png'); const monsterImg = await loadAsset('monster.png'); // Images are now ready to use } catch (error) { console.error('Failed to load game assets:', error); } } ``` **สิ่งที่เราทำในที่นี้:** - **ห่อ**ตรรกะการโหลดภาพทั้งหมดใน Promise เพื่อให้เราจัดการได้ดีขึ้น - **เพิ่ม**การจัดการข้อผิดพลาดที่บอกเราจริง ๆ เมื่อมีบางอย่างผิดพลาด - **ใช้**ไวยากรณ์ async/await ที่ทันสมัยเพราะมันอ่านง่ายมาก - **รวม**บล็อก try/catch เพื่อจัดการปัญหาการโหลดอย่างสุภาพ เมื่อภาพของคุณโหลดเสร็จ การวาดมันลงบน canvas นั้นค่อนข้างตรงไปตรงมา: ```javascript async function renderGameScreen() { try { // Load game assets const heroImg = await loadAsset('hero.png'); const monsterImg = await loadAsset('monster.png'); // Get canvas and context const canvas = document.getElementById("myCanvas"); const ctx = canvas.getContext("2d"); // Draw images to specific positions ctx.drawImage(heroImg, canvas.width / 2, canvas.height / 2); ctx.drawImage(monsterImg, 0, 0); } catch (error) { console.error('Failed to render game screen:', error); } } ``` **มาดูทีละขั้นตอน:** - เรา **โหลด**ภาพฮีโร่และมอนสเตอร์ในพื้นหลังโดยใช้ await - เรา **ดึง**องค์ประกอบ canvas และรับบริบทการเรนเดอร์ 2D ที่เราต้องการ - เรา **จัดตำแหน่ง**ภาพฮีโร่ตรงกลางโดยใช้คณิตศาสตร์พิกัดอย่างรวดเร็ว - เรา **วาง**ภาพมอนสเตอร์ที่มุมบนซ้ายเพื่อเริ่มการจัดรูปแบบศัตรู - เรา **จับ**ข้อผิดพลาดใด ๆ ที่อาจเกิดขึ้นระหว่างการโหลดหรือการเรนเดอร์ ## ถึงเวลาสร้างเกมของคุณแล้ว ตอนนี้เราจะรวมทุกอย่างเข้าด้วยกันเพื่อสร้างพื้นฐานภาพของเกมอวกาศของคุณ คุณมีความเข้าใจที่มั่นคงเกี่ยวกับพื้นฐานของ canvas และเทคนิคการโหลดภาพ ดังนั้นส่วนที่ลงมือทำนี้จะนำคุณผ่านการสร้างหน้าจอเกมที่สมบูรณ์พร้อมสไปรท์ที่วางตำแหน่งอย่างเหมาะสม ### สิ่งที่ต้องสร้าง คุณจะสร้างหน้าเว็บที่มีองค์ประกอบ Canvas ซึ่งจะแสดงหน้าจอสีดำขนาด `1024*768` เราได้เตรียมภาพสองภาพให้คุณ: - ยานฮีโร่ ![ยานฮีโร่](../../../../translated_images/player.dd24c1afa8c71e9b82b2958946d4bad13308681392d4b5ddcc61a0e818ef8088.th.png) - มอนสเตอร์ 5*5 ![ยานมอนสเตอร์](../../../../translated_images/enemyShip.5df2a822c16650c2fb3c06652e8ec8120cdb9122a6de46b9a1a56d54db22657f.th.png) ### ขั้นตอนที่แนะนำในการเริ่มต้นพัฒนา ค้นหาไฟล์เริ่มต้นที่ถูกสร้างไว้ให้คุณในโฟลเดอร์ `your-work` โครงสร้างโปรเจกต์ของคุณควรมี: ```bash your-work/ ├── assets/ │ ├── enemyShip.png │ └── player.png ├── index.html ├── app.js └── package.json ``` **สิ่งที่คุณกำลังทำงานด้วย:** - **สไปรท์เกม** อยู่ในโฟลเดอร์ `assets/` เพื่อให้ทุกอย่างเป็นระเบียบ - **ไฟล์ HTML หลักของคุณ** ตั้งค่าองค์ประกอบ canvas และเตรียมทุกอย่างให้พร้อม - **ไฟล์ JavaScript** ที่คุณจะเขียนโค้ดการเรนเดอร์เกมทั้งหมด - **ไฟล์ package.json** ที่ตั้งค่าเซิร์ฟเวอร์พัฒนาเพื่อให้คุณทดสอบได้ในเครื่อง เปิดโฟลเดอร์นี้ใน Visual Studio Code เพื่อเริ่มพัฒนา คุณจะต้องมีสภาพแวดล้อมการพัฒนาในเครื่องที่มี Visual Studio Code, NPM และ Node.js ติดตั้งอยู่ หากคุณยังไม่มี `npm` ตั้งค่าในคอมพิวเตอร์ของคุณ [นี่คือวิธีการติดตั้ง](https://www.npmjs.com/get-npm) เริ่มเซิร์ฟเวอร์พัฒนาของคุณโดยไปที่โฟลเดอร์ `your-work`: ```bash cd your-work npm start ``` **คำสั่งนี้ทำสิ่งที่น่าสนใจ:** - **เริ่มต้น**เซิร์ฟเวอร์ในเครื่องที่ `http://localhost:5000` เพื่อให้คุณทดสอบเกมของคุณ - **ให้บริการ**ไฟล์ทั้งหมดของคุณอย่างถูกต้องเพื่อให้เบราว์เซอร์โหลดได้อย่างถูกต้อง - **เฝ้าดู**ไฟล์ของคุณสำหรับการเปลี่ยนแปลงเพื่อให้คุณพัฒนาได้อย่างราบรื่น - **ให้คุณ**สภาพแวดล้อมการพัฒนาระดับมืออาชีพเพื่อทดสอบทุกอย่าง > 💡 **หมายเหตุ**: เบราว์เซอร์ของคุณจะแสดงหน้าเปล่าในตอนแรก – นั่นเป็นเรื่องปกติ! เมื่อคุณเพิ่มโค้ด ให้รีเฟรชเบราว์เซอร์เพื่อดูการเปลี่ยนแปลงของคุณ วิธีการพัฒนาแบบวนซ้ำนี้คล้ายกับวิธีที่ NASA สร้างคอมพิวเตอร์นำทาง Apollo – ทดสอบแต่ละส่วนก่อนที่จะรวมเข้ากับระบบใหญ่ ### เพิ่มโค้ด เพิ่มโค้ดที่จำเป็นลงใน `your-work/app.js` เพื่อทำงานต่อไปนี้ให้เสร็จ: 1. **วาด canvas พร้อมพื้นหลังสีดำ** > 💡 **วิธีการ**: ค้นหา TODO ใน `/app.js` และเพิ่มเพียงสองบรรทัด ตั้งค่า `ctx.fillStyle` เป็นสีดำ จากนั้นใช้ `ctx.fillRect()` เริ่มต้นที่ (0,0) พร้อมขนาดของ canvas ของคุณ ง่ายมาก! 2. **โหลดพื้นผิวเกม** > 💡 **วิธีการ**: ใช้ `await loadAsset()` เพื่อโหลดภาพผู้เล่นและศัตรูของคุณ เก็บไว้ในตัวแปรเพื่อให้คุณสามารถใช้มันในภายหลัง จำไว้ – มันจะไม่ปรากฏจนกว่าคุณจะวาดมันจริง ๆ! 3. **วาดยานฮีโร่ในตำแหน่งกลาง-ล่าง** > 💡 **วิธีการ**: ใช้ `ctx.drawImage()` เพื่อวางตำแหน่งฮีโร่ของคุณ สำหรับพิกัด x ลองใช้ `canvas.width / 2 - 45` เพื่อวางตรงกลาง และสำหรับพิกัด y ใช้ `canvas.height - canvas.height / 4` เพื่อวางในพื้นที่ด้านล่าง 4. **วาดรูปแบบมอนสเตอร์ 5×5** > 💡 **วิธีการ**: ค้นหาฟังก์ชัน `createEnemies` และตั้งค่าลูปซ้อนกัน คุณจะต้องทำคณิตศาสตร์สำหรับการเว้นระยะและการวางตำแหน่ง แต่ไม่ต้องกังวล – ฉันจะแสดงให้คุณเห็นว่าทำอย่างไร! ก่อนอื่น กำหนดค่าคงที่สำหรับการจัดรูปแบบศัตรูที่เหมาะสม: ```javascript const ENEMY_TOTAL = 5; const ENEMY_SPACING = 98; const FORMATION_WIDTH = ENEMY_TOTAL * ENEMY_SPACING; const START_X = (canvas.width - FORMATION_WIDTH) / 2; const STOP_X = START_X + FORMATION_WIDTH; ``` **มาดูว่าค่าคงที่เหล่านี้ทำอะไร:** - เรา **ตั้งค่า**ศัตรู 5 ตัวต่อแถวและคอลัมน์ (กริด 5×5 ที่สวยงาม) - เรา **กำหนด**ระยะห่างระหว่างศัตรูเพื่อไม่ให้ดูแออัด - เรา **คำนวณ**ความกว้างของรูปแบบทั้งหมด - เรา **หาจุดเริ่มต้นและจุดสิ้นสุด**เพื่อให้รูปแบบดูสมดุล จากนั้น สร้างลูปซ้อนกันเพื่อวาดรูปแบบศัตรู: ```javascript for (let x = START_X; x < STOP_X; x += ENEMY_SPACING) { for (let y = 0; y < 50 * 5; y += 50) { ctx.drawImage(enemyImg, x, y); } } ``` **นี่คือสิ่งที่ลูปซ้อนกันนี้ทำ:** - ลูปด้านนอก **เคลื่อนที่**จากซ้ายไปขวาในรูปแบบของเรา - ลูปด้านใน **เคลื่อนที่**จากบนลงล่างเพื่อสร้างแถวที่เรียบร้อย - เรา **วาด**สไปรท์ศัตรูแต่ละตัวที่พิกัด x,y ที่เราคำนวณไว้ - ทุกอย่าง **เว้นระยะห่างอย่างสม่ำเสมอ**เพื่อให้ดูเป็นมืออาชีพและเป็นระเบียบ ## ผลลัพธ์ ผลลัพธ์ที่เสร็จสมบูรณ์ควรมีลักษณะดังนี้: ![หน้าจอสีดำพร้อมฮีโร่และมอนสเตอร์ 5*5](../../../../translated_images/partI-solution.36c53b48c9ffae2a5e15496b23b604ba5393433e4bf91608a7a0a020eb7a2691.th.png) ## วิธีแก้ปัญหา กรุณาลองแก้ปัญหาด้วยตัวเองก่อน แต่ถ้าคุณติดขัด ลองดู [วิธีแก้ปัญหา](../../../../6-space-game/2-drawing-to-canvas/solution/app.js) --- ## ความท้าทาย GitHub Copilot Agent 🚀 ใช้โหมด Agent เพื่อทำความท้าทายต่อไปนี้ให้สำเร็จ: **คำอธิบาย:** เพิ่มเอฟเฟกต์ภาพและองค์ประกอบแบบโต้ตอบใน canvas เกมอวกาศของคุณโดยใช้เทคนิค Canvas API ที่คุณได้เรียนรู้ **คำสั่ง:** สร้างไฟล์ใหม่ชื่อ `enhanced-canvas.html` พร้อม canvas ที่แสดงดาวที่เคลื่อนไหวในพื้นหลัง แถบสุขภาพที่เต้นเป็นจังหวะสำหรับยานฮีโร่ และยานศัตรูที่เคลื่อนที่ลงอย่างช้า ๆ รวมโค้ด JavaScript ที่วาดดาวที่กระพริบโดยใช้ตำแหน่งและความทึบแบบสุ่ม ใช้แถบสุขภาพที่เปลี่ยนสีตามระดับสุขภาพ (เขียว > เหลือง > แดง) และทำให้ยานศัตรูเคลื่อนที่ลงหน้าจอด้วยความเร็วต่างกัน เรียนรู้เพิ่มเติมเกี่ยวกับ [โหมด Agent](https://code --- **ข้อจำกัดความรับผิดชอบ**: เอกสารนี้ได้รับการแปลโดยใช้บริการแปลภาษา AI [Co-op Translator](https://github.com/Azure/co-op-translator) แม้ว่าเราจะพยายามให้การแปลมีความถูกต้อง แต่โปรดทราบว่าการแปลอัตโนมัติอาจมีข้อผิดพลาดหรือความไม่ถูกต้อง เอกสารต้นฉบับในภาษาดั้งเดิมควรถือเป็นแหล่งข้อมูลที่เชื่อถือได้ สำหรับข้อมูลที่สำคัญ ขอแนะนำให้ใช้บริการแปลภาษามืออาชีพ เราจะไม่รับผิดชอบต่อความเข้าใจผิดหรือการตีความผิดที่เกิดจากการใช้การแปลนี้