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/th/6-space-game/2-drawing-to-canvas/README.md

30 KiB

สร้างเกมอวกาศ ตอนที่ 2: วาดฮีโร่และมอนสเตอร์ลงบน Canvas

Canvas API เป็นหนึ่งในฟีเจอร์ที่ทรงพลังที่สุดของการพัฒนาเว็บสำหรับการสร้างกราฟิกแบบไดนามิกและโต้ตอบได้ในเบราว์เซอร์ของคุณ ในบทเรียนนี้ เราจะเปลี่ยน <canvas> HTML ที่ว่างเปล่าให้กลายเป็นโลกของเกมที่เต็มไปด้วยฮีโร่และมอนสเตอร์ ลองนึกถึง canvas ว่าเป็นกระดานศิลปะดิจิทัลที่โค้ดกลายเป็นภาพ

เราจะต่อยอดจากสิ่งที่คุณเรียนรู้ในบทเรียนก่อนหน้า และตอนนี้เราจะเจาะลึกในด้านภาพ คุณจะได้เรียนรู้วิธีโหลดและแสดงสไปรท์ของเกม การจัดตำแหน่งองค์ประกอบอย่างแม่นยำ และสร้างพื้นฐานภาพสำหรับเกมอวกาศของคุณ นี่คือสะพานเชื่อมระหว่างหน้าเว็บแบบคงที่และประสบการณ์แบบไดนามิกและโต้ตอบได้

เมื่อจบบทเรียนนี้ คุณจะมีฉากเกมที่สมบูรณ์พร้อมกับยานฮีโร่ที่วางตำแหน่งอย่างถูกต้องและการจัดรูปแบบศัตรูที่พร้อมสำหรับการต่อสู้ คุณจะเข้าใจวิธีที่เกมสมัยใหม่แสดงกราฟิกในเบราว์เซอร์ และได้รับทักษะในการสร้างประสบการณ์ภาพแบบโต้ตอบของคุณเอง มาสำรวจกราฟิกของ canvas และทำให้เกมอวกาศของคุณมีชีวิตชีวากันเถอะ!

แบบทดสอบก่อนเรียน

แบบทดสอบก่อนเรียน

Canvas คืออะไร

แล้ว <canvas> คืออะไร? มันคือโซลูชันของ HTML5 สำหรับการสร้างกราฟิกและแอนิเมชันแบบไดนามิกในเบราว์เซอร์เว็บ ไม่เหมือนกับภาพหรือวิดีโอทั่วไปที่เป็นแบบคงที่ Canvas ให้คุณควบคุมระดับพิกเซลในทุกสิ่งที่ปรากฏบนหน้าจอ ซึ่งทำให้มันเหมาะสำหรับเกม การแสดงผลข้อมูล และศิลปะเชิงโต้ตอบ ลองนึกถึงมันว่าเป็นพื้นผิวการวาดที่สามารถเขียนโปรแกรมได้ โดยที่ JavaScript เป็นแปรงวาดของคุณ

โดยปกติแล้ว องค์ประกอบ canvas จะดูเหมือนสี่เหลี่ยมโปร่งใสที่ว่างเปล่าบนหน้าเว็บของคุณ แต่ศักยภาพที่แท้จริงของมันจะปรากฏเมื่อคุณใช้ JavaScript เพื่อวาดรูปร่าง โหลดภาพ สร้างแอนิเมชัน และทำให้สิ่งต่าง ๆ ตอบสนองต่อการโต้ตอบของผู้ใช้ มันคล้ายกับวิธีที่ผู้บุกเบิกกราฟิกคอมพิวเตอร์ในยุคแรก ๆ ที่ Bell Labs ในปี 1960 ต้องเขียนโปรแกรมพิกเซลทุกจุดเพื่อสร้างแอนิเมชันดิจิทัลครั้งแรก

อ่าน เพิ่มเติมเกี่ยวกับ Canvas API บน MDN

นี่คือตัวอย่างการประกาศ canvas โดยทั่วไป ซึ่งเป็นส่วนหนึ่งของ body ของหน้าเว็บ:

<canvas id="myCanvas" width="200" height="100"></canvas>

สิ่งที่โค้ดนี้ทำ:

  • ตั้งค่า attribute id เพื่อให้คุณสามารถอ้างอิงถึงองค์ประกอบ canvas นี้ใน JavaScript
  • กำหนด width เป็นพิกเซลเพื่อควบคุมขนาดแนวนอนของ canvas
  • กำหนด height เป็นพิกเซลเพื่อกำหนดขนาดแนวตั้งของ canvas

การวาดรูปทรงเรขาคณิตง่าย ๆ

ตอนนี้คุณรู้แล้วว่า canvas คืออะไร ลองมาสำรวจวิธีการวาดบนมันกัน! Canvas ใช้ระบบพิกัดที่อาจจะคุ้นเคยจากชั้นเรียนคณิตศาสตร์ แต่มีจุดพลิกผันสำคัญที่เฉพาะเจาะจงสำหรับกราฟิกคอมพิวเตอร์

Canvas ใช้ระบบพิกัด Cartesian ที่มีแกน x (แนวนอน) และแกน y (แนวตั้ง) เพื่อกำหนดตำแหน่งของทุกสิ่งที่คุณวาด แต่มีความแตกต่างสำคัญ: ไม่เหมือนกับระบบพิกัดในชั้นเรียนคณิตศาสตร์ จุดเริ่มต้น (0,0) จะอยู่ที่มุมบนซ้าย โดยค่าของ x จะเพิ่มขึ้นเมื่อคุณเลื่อนไปทางขวา และค่าของ y จะเพิ่มขึ้นเมื่อคุณเลื่อนลง วิธีนี้มีต้นกำเนิดมาจากจอคอมพิวเตอร์ในยุคแรก ๆ ที่ลำแสงอิเล็กตรอนสแกนจากบนลงล่าง ทำให้มุมบนซ้ายเป็นจุดเริ่มต้นที่เป็นธรรมชาติ

กริดของ canvas

ภาพจาก MDN

ในการวาดบน canvas คุณจะทำตามกระบวนการสามขั้นตอนเดียวกันที่เป็นพื้นฐานของกราฟิก canvas ทั้งหมด เมื่อคุณทำสิ่งนี้สองสามครั้ง มันจะกลายเป็นธรรมชาติ:

  1. อ้างอิงถึงองค์ประกอบ Canvas ของคุณจาก DOM (เหมือนกับองค์ประกอบ HTML อื่น ๆ)
  2. รับบริบทการเรนเดอร์ 2D นี่คือเครื่องมือที่มีวิธีการวาดทั้งหมด
  3. เริ่มวาด! ใช้วิธีการในตัวของบริบทเพื่อสร้างกราฟิกของคุณ

นี่คือลักษณะของโค้ด:

// 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

คุณสามารถวาดสิ่งต่าง ๆ ได้หลากหลายด้วย Canvas API เช่น:

  • รูปร่างเรขาคณิต เราได้แสดงวิธีการวาดสี่เหลี่ยมแล้ว แต่ยังมีอีกมากมายที่คุณสามารถวาดได้
  • ข้อความ คุณสามารถวาดข้อความด้วยฟอนต์และสีใดก็ได้ที่คุณต้องการ
  • ภาพ คุณสามารถวาดภาพจากไฟล์ภาพ เช่น .jpg หรือ .png เป็นต้น

ลองทำดู! คุณรู้วิธีวาดสี่เหลี่ยมแล้ว คุณสามารถวาดวงกลมลงบนหน้าได้หรือไม่? ลองดูภาพวาด Canvas ที่น่าสนใจบน CodePen นี่คือตัวอย่างที่ น่าประทับใจ

โหลดและวาดภาพ

การวาดรูปร่างพื้นฐานมีประโยชน์สำหรับการเริ่มต้น แต่เกมส่วนใหญ่ต้องการภาพจริง! สไปรท์ พื้นหลัง และพื้นผิวคือสิ่งที่ทำให้เกมมีความน่าสนใจทางภาพ การโหลดและแสดงภาพบน canvas ทำงานแตกต่างจากการวาดรูปร่างเรขาคณิต แต่ก็ง่ายเมื่อคุณเข้าใจกระบวนการ

เราจำเป็นต้องสร้างวัตถุ Image โหลดไฟล์ภาพของเรา (สิ่งนี้เกิดขึ้นแบบอะซิงโครนัส หมายถึง "ในพื้นหลัง") และวาดมันลงบน canvas เมื่อมันพร้อม วิธีนี้ช่วยให้ภาพของคุณแสดงผลได้อย่างถูกต้องโดยไม่ทำให้แอปพลิเคชันของคุณหยุดชะงักขณะโหลด

การโหลดภาพพื้นฐาน

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 ทำให้โค้ดของคุณมีการจัดระเบียบมากขึ้นและจัดการข้อผิดพลาดได้อย่างดี:

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 นั้นค่อนข้างตรงไปตรงมา:

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 เราได้เตรียมภาพสองภาพให้คุณ:

  • ยานฮีโร่

    ยานฮีโร่

  • มอนสเตอร์ 5*5

    ยานมอนสเตอร์

ขั้นตอนที่แนะนำในการเริ่มต้นพัฒนา

ค้นหาไฟล์เริ่มต้นที่ถูกสร้างไว้ให้คุณในโฟลเดอร์ your-work โครงสร้างโปรเจกต์ของคุณควรมี:

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 ตั้งค่าในคอมพิวเตอร์ของคุณ นี่คือวิธีการติดตั้ง

เริ่มเซิร์ฟเวอร์พัฒนาของคุณโดยไปที่โฟลเดอร์ your-work:

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 และตั้งค่าลูปซ้อนกัน คุณจะต้องทำคณิตศาสตร์สำหรับการเว้นระยะและการวางตำแหน่ง แต่ไม่ต้องกังวล ฉันจะแสดงให้คุณเห็นว่าทำอย่างไร!

ก่อนอื่น กำหนดค่าคงที่สำหรับการจัดรูปแบบศัตรูที่เหมาะสม:

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 ที่สวยงาม)
  • เรา กำหนดระยะห่างระหว่างศัตรูเพื่อไม่ให้ดูแออัด
  • เรา คำนวณความกว้างของรูปแบบทั้งหมด
  • เรา หาจุดเริ่มต้นและจุดสิ้นสุดเพื่อให้รูปแบบดูสมดุล

จากนั้น สร้างลูปซ้อนกันเพื่อวาดรูปแบบศัตรู:

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

วิธีแก้ปัญหา

กรุณาลองแก้ปัญหาด้วยตัวเองก่อน แต่ถ้าคุณติดขัด ลองดู วิธีแก้ปัญหา


ความท้าทาย GitHub Copilot Agent 🚀

ใช้โหมด Agent เพื่อทำความท้าทายต่อไปนี้ให้สำเร็จ:

คำอธิบาย: เพิ่มเอฟเฟกต์ภาพและองค์ประกอบแบบโต้ตอบใน canvas เกมอวกาศของคุณโดยใช้เทคนิค Canvas API ที่คุณได้เรียนรู้

คำสั่ง: สร้างไฟล์ใหม่ชื่อ enhanced-canvas.html พร้อม canvas ที่แสดงดาวที่เคลื่อนไหวในพื้นหลัง แถบสุขภาพที่เต้นเป็นจังหวะสำหรับยานฮีโร่ และยานศัตรูที่เคลื่อนที่ลงอย่างช้า ๆ รวมโค้ด JavaScript ที่วาดดาวที่กระพริบโดยใช้ตำแหน่งและความทึบแบบสุ่ม ใช้แถบสุขภาพที่เปลี่ยนสีตามระดับสุขภาพ (เขียว > เหลือง > แดง) และทำให้ยานศัตรูเคลื่อนที่ลงหน้าจอด้วยความเร็วต่างกัน

เรียนรู้เพิ่มเติมเกี่ยวกับ [โหมด Agent](https://code


ข้อจำกัดความรับผิดชอบ:
เอกสารนี้ได้รับการแปลโดยใช้บริการแปลภาษา AI Co-op Translator แม้ว่าเราจะพยายามให้การแปลมีความถูกต้อง แต่โปรดทราบว่าการแปลอัตโนมัติอาจมีข้อผิดพลาดหรือความไม่ถูกต้อง เอกสารต้นฉบับในภาษาดั้งเดิมควรถือเป็นแหล่งข้อมูลที่เชื่อถือได้ สำหรับข้อมูลที่สำคัญ ขอแนะนำให้ใช้บริการแปลภาษามืออาชีพ เราจะไม่รับผิดชอบต่อความเข้าใจผิดหรือการตีความผิดที่เกิดจากการใช้การแปลนี้