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/4-collision-detection/README.md

426 lines
30 KiB

<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "4b1d441cfd31924084956000c0fee5a5",
"translation_date": "2025-10-23T21:20:14+00:00",
"source_file": "6-space-game/4-collision-detection/README.md",
"language_code": "th"
}
-->
# สร้างเกมอวกาศ ตอนที่ 4: เพิ่มเลเซอร์และตรวจจับการชนกัน
## แบบทดสอบก่อนเรียน
[แบบทดสอบก่อนเรียน](https://ff-quizzes.netlify.app/web/quiz/35)
ลองนึกถึงช่วงเวลาที่ลุคใช้ตอร์ปิโดโปรตอนยิงเข้าช่องไอเสียของดาวมรณะใน Star Wars การตรวจจับการชนกันที่แม่นยำนี้เปลี่ยนชะตากรรมของกาแล็กซี่! ในเกม การตรวจจับการชนกันทำงานในลักษณะเดียวกัน - มันจะกำหนดว่าเมื่อใดที่วัตถุมีปฏิสัมพันธ์และจะเกิดอะไรขึ้นต่อไป
ในบทเรียนนี้ คุณจะเพิ่มอาวุธเลเซอร์ในเกมอวกาศของคุณและนำการตรวจจับการชนกันมาใช้ เช่นเดียวกับที่นักวางแผนภารกิจของ NASA คำนวณวิถีของยานอวกาศเพื่อหลีกเลี่ยงเศษซาก คุณจะได้เรียนรู้วิธีตรวจจับเมื่อวัตถุในเกมตัดกัน เราจะทำให้มันง่ายขึ้นด้วยการแบ่งเป็นขั้นตอนที่จัดการได้
เมื่อจบบทเรียนนี้ คุณจะมีระบบการต่อสู้ที่สมบูรณ์ซึ่งเลเซอร์สามารถทำลายศัตรูได้ และการชนกันจะกระตุ้นเหตุการณ์ในเกม หลักการตรวจจับการชนกันเหล่านี้ถูกใช้ในทุกอย่างตั้งแต่การจำลองทางฟิสิกส์ไปจนถึงอินเทอร์เฟซเว็บแบบโต้ตอบ
✅ ลองค้นคว้าเกี่ยวกับเกมคอมพิวเตอร์เกมแรกที่เคยเขียนขึ้น มันมีฟังก์ชันอะไรบ้าง?
## การตรวจจับการชนกัน
การตรวจจับการชนกันทำงานเหมือนเซ็นเซอร์ตรวจจับระยะใกล้บนโมดูลดวงจันทร์ของ Apollo - มันจะตรวจสอบระยะห่างอย่างต่อเนื่องและส่งสัญญาณเตือนเมื่อวัตถุเข้าใกล้กันมากเกินไป ในเกม ระบบนี้จะกำหนดว่าเมื่อใดที่วัตถุมีปฏิสัมพันธ์และควรเกิดอะไรขึ้นต่อไป
วิธีที่เราจะใช้คือการมองว่าวัตถุในเกมทุกชิ้นเป็นรูปสี่เหลี่ยมผืนผ้า คล้ายกับที่ระบบควบคุมการจราจรทางอากาศใช้รูปทรงเรขาคณิตที่เรียบง่ายเพื่อติดตามเครื่องบิน วิธีการแบบสี่เหลี่ยมนี้อาจดูพื้นฐาน แต่มีประสิทธิภาพในการคำนวณและทำงานได้ดีสำหรับสถานการณ์ในเกมส่วนใหญ่
### การแทนค่ารูปสี่เหลี่ยมผืนผ้า
วัตถุในเกมทุกชิ้นต้องมีขอบเขตพิกัด คล้ายกับที่รถสำรวจ Mars Pathfinder กำหนดตำแหน่งของมันบนพื้นผิวดาวอังคาร นี่คือวิธีที่เรากำหนดพิกัดขอบเขตเหล่านี้:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width
}
}
```
**มาดูรายละเอียดกัน:**
- **ขอบบน**: คือจุดเริ่มต้นในแนวตั้งของวัตถุ (ตำแหน่ง y)
- **ขอบซ้าย**: คือจุดเริ่มต้นในแนวนอนของวัตถุ (ตำแหน่ง x)
- **ขอบล่าง**: เพิ่มความสูงเข้าไปในตำแหน่ง y - ตอนนี้คุณรู้แล้วว่ามันสิ้นสุดที่ไหน!
- **ขอบขวา**: เพิ่มความกว้างเข้าไปในตำแหน่ง x - และคุณได้ขอบเขตทั้งหมดแล้ว
### อัลกอริทึมการตัดกัน
การตรวจจับการตัดกันของรูปสี่เหลี่ยมผืนผ้าใช้ตรรกะคล้ายกับที่กล้องโทรทรรศน์อวกาศฮับเบิลใช้ในการกำหนดว่าวัตถุท้องฟ้าทับซ้อนกันในมุมมองของมันหรือไม่ อัลกอริทึมจะตรวจสอบการแยก:
```javascript
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
```
**การทดสอบการแยกทำงานเหมือนระบบเรดาร์:**
- รูปสี่เหลี่ยมผืนผ้า 2 อยู่ทางขวาสุดของรูปสี่เหลี่ยมผืนผ้า 1 หรือไม่?
- รูปสี่เหลี่ยมผืนผ้า 2 อยู่ทางซ้ายสุดของรูปสี่เหลี่ยมผืนผ้า 1 หรือไม่?
- รูปสี่เหลี่ยมผืนผ้า 2 อยู่ด้านล่างสุดของรูปสี่เหลี่ยมผืนผ้า 1 หรือไม่?
- รูปสี่เหลี่ยมผืนผ้า 2 อยู่ด้านบนสุดของรูปสี่เหลี่ยมผืนผ้า 1 หรือไม่?
หากไม่มีเงื่อนไขเหล่านี้เป็นจริง รูปสี่เหลี่ยมผืนผ้าจะต้องทับซ้อนกัน วิธีนี้คล้ายกับที่ผู้ควบคุมเรดาร์กำหนดว่าเครื่องบินสองลำอยู่ในระยะปลอดภัยหรือไม่
## การจัดการวงจรชีวิตของวัตถุ
เมื่อเลเซอร์ชนศัตรู วัตถุทั้งสองต้องถูกลบออกจากเกม อย่างไรก็ตาม การลบวัตถุระหว่างลูปอาจทำให้เกิดการล่ม - บทเรียนที่ได้เรียนรู้จากระบบคอมพิวเตอร์ยุคแรก ๆ เช่น Apollo Guidance Computer ดังนั้นเราจึงใช้วิธี "ทำเครื่องหมายเพื่อลบ" ซึ่งจะลบวัตถุอย่างปลอดภัยระหว่างเฟรม
นี่คือวิธีที่เราทำเครื่องหมายบางสิ่งเพื่อการลบ:
```javascript
// Mark object for removal
enemy.dead = true;
```
**ทำไมวิธีนี้ถึงได้ผล:**
- เราทำเครื่องหมายวัตถุว่า "ตาย" แต่ยังไม่ลบมันทันที
- สิ่งนี้ช่วยให้เฟรมเกมปัจจุบันเสร็จสิ้นอย่างปลอดภัย
- ไม่มีการล่มจากการพยายามใช้สิ่งที่ถูกลบไปแล้ว!
จากนั้นกรองวัตถุที่ถูกทำเครื่องหมายออกก่อนรอบการแสดงผลครั้งต่อไป:
```javascript
gameObjects = gameObjects.filter(go => !go.dead);
```
**สิ่งที่การกรองนี้ทำ:**
- สร้างรายการใหม่ที่มีเฉพาะวัตถุที่ "ยังมีชีวิตอยู่"
- ทิ้งสิ่งที่ถูกทำเครื่องหมายว่าตายไปแล้ว
- ทำให้เกมของคุณทำงานได้อย่างราบรื่น
- ป้องกันการสะสมของวัตถุที่ถูกทำลายซึ่งทำให้หน่วยความจำบวม
## การนำกลไกเลเซอร์มาใช้
โปรเจกไทล์เลเซอร์ในเกมทำงานบนหลักการเดียวกับตอร์ปิโดโฟตอนใน Star Trek - มันเป็นวัตถุที่แยกออกมาและเคลื่อนที่เป็นเส้นตรงจนกว่าจะชนบางสิ่ง การกดแป้นเว้นวรรคแต่ละครั้งจะสร้างวัตถุเลเซอร์ใหม่ที่เคลื่อนที่ข้ามหน้าจอ
เพื่อให้สิ่งนี้ทำงาน เราต้องประสานชิ้นส่วนต่าง ๆ:
**ส่วนสำคัญที่ต้องนำมาใช้:**
- **สร้าง** วัตถุเลเซอร์ที่เกิดจากตำแหน่งของฮีโร่
- **จัดการ** การป้อนข้อมูลจากแป้นพิมพ์เพื่อกระตุ้นการสร้างเลเซอร์
- **จัดการ** การเคลื่อนที่และวงจรชีวิตของเลเซอร์
- **นำเสนอ** การแสดงผลภาพสำหรับโปรเจกไทล์เลเซอร์
## การควบคุมอัตราการยิง
อัตราการยิงที่ไม่จำกัดจะทำให้เครื่องยนต์เกมทำงานหนักเกินไปและทำให้การเล่นเกมง่ายเกินไป ระบบอาวุธจริงก็เผชิญกับข้อจำกัดที่คล้ายกัน - แม้แต่ปืนเฟเซอร์ของ USS Enterprise ก็ต้องใช้เวลาชาร์จระหว่างการยิง
เราจะนำระบบคูลดาวน์มาใช้เพื่อป้องกันการยิงแบบรัว ๆ ในขณะที่ยังคงควบคุมได้อย่างตอบสนอง:
```javascript
class Cooldown {
constructor(time) {
this.cool = false;
setTimeout(() => {
this.cool = true;
}, time);
}
}
class Weapon {
constructor() {
this.cooldown = null;
}
fire() {
if (!this.cooldown || this.cooldown.cool) {
// Create laser projectile
this.cooldown = new Cooldown(500);
} else {
// Weapon is still cooling down
}
}
}
```
**วิธีการทำงานของคูลดาวน์:**
- เมื่อสร้างขึ้น อาวุธจะเริ่มต้นเป็น "ร้อน" (ยังยิงไม่ได้)
- หลังจากช่วงเวลาที่กำหนด มันจะกลายเป็น "เย็น" (พร้อมยิง)
- ก่อนยิง เราจะตรวจสอบว่า "อาวุธเย็นหรือยัง?"
- สิ่งนี้ป้องกันการคลิกแบบรัว ๆ ในขณะที่ยังคงควบคุมได้อย่างตอบสนอง
✅ อ้างอิงบทเรียนที่ 1 ในซีรีส์เกมอวกาศเพื่อเตือนตัวเองเกี่ยวกับคูลดาวน์
## การสร้างระบบตรวจจับการชนกัน
คุณจะขยายโค้ดเกมอวกาศที่มีอยู่ของคุณเพื่อสร้างระบบตรวจจับการชนกัน เช่นเดียวกับระบบหลีกเลี่ยงการชนกันอัตโนมัติของสถานีอวกาศนานาชาติ เกมของคุณจะตรวจสอบตำแหน่งของวัตถุอย่างต่อเนื่องและตอบสนองต่อการตัดกัน
เริ่มต้นจากโค้ดบทเรียนก่อนหน้านี้ของคุณ คุณจะเพิ่มการตรวจจับการชนกันพร้อมกฎเฉพาะที่ควบคุมการมีปฏิสัมพันธ์ของวัตถุ
> 💡 **เคล็ดลับ**: สไปรต์เลเซอร์มีอยู่แล้วในโฟลเดอร์ทรัพย์สินของคุณและอ้างอิงในโค้ดของคุณ พร้อมสำหรับการนำไปใช้
### กฎการชนกันที่ต้องนำมาใช้
**กลไกเกมที่ต้องเพิ่ม:**
1. **เลเซอร์ชนศัตรู**: วัตถุศัตรูจะถูกทำลายเมื่อถูกโปรเจกไทล์เลเซอร์
2. **เลเซอร์ชนขอบหน้าจอ**: เลเซอร์จะถูกลบเมื่อถึงขอบบนของหน้าจอ
3. **ศัตรูชนฮีโร่**: วัตถุทั้งสองจะถูกทำลายเมื่อชนกัน
4. **ศัตรูถึงด้านล่าง**: เงื่อนไขเกมโอเวอร์เมื่อศัตรูถึงด้านล่างของหน้าจอ
## การตั้งค่าสภาพแวดล้อมการพัฒนา
ข่าวดี - เราได้ตั้งค่าพื้นฐานส่วนใหญ่ให้คุณแล้ว! ทรัพยากรเกมและโครงสร้างพื้นฐานทั้งหมดของคุณรออยู่ในโฟลเดอร์ `your-work` พร้อมสำหรับคุณที่จะเพิ่มฟีเจอร์การชนกันที่เจ๋ง ๆ
### โครงสร้างโปรเจกต์
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
**ทำความเข้าใจโครงสร้างไฟล์:**
- **มี** ภาพสไปรต์ทั้งหมดที่จำเป็นสำหรับวัตถุในเกม
- **รวม** เอกสาร HTML หลักและไฟล์แอปพลิเคชัน JavaScript
- **จัดเตรียม** การกำหนดค่าของแพ็กเกจสำหรับเซิร์ฟเวอร์พัฒนาท้องถิ่น
### การเริ่มเซิร์ฟเวอร์พัฒนา
ไปที่โฟลเดอร์โปรเจกต์ของคุณและเริ่มเซิร์ฟเวอร์ท้องถิ่น:
```bash
cd your-work
npm start
```
**ลำดับคำสั่งนี้:**
- **เปลี่ยน**ไดเรกทอรีไปยังโฟลเดอร์โปรเจกต์ที่คุณทำงานอยู่
- **เริ่มต้น**เซิร์ฟเวอร์ HTTP ท้องถิ่นที่ `http://localhost:5000`
- **ให้บริการ**ไฟล์เกมของคุณสำหรับการทดสอบและพัฒนา
- **เปิดใช้งาน**การพัฒนาแบบสดพร้อมการรีโหลดอัตโนมัติ
เปิดเบราว์เซอร์ของคุณและไปที่ `http://localhost:5000` เพื่อดูสถานะเกมปัจจุบันของคุณพร้อมฮีโร่และศัตรูที่แสดงบนหน้าจอ
### การนำไปใช้ทีละขั้นตอน
เช่นเดียวกับวิธีการที่ NASA ใช้ในการเขียนโปรแกรมยานอวกาศ Voyager เราจะนำการตรวจจับการชนกันมาใช้อย่างเป็นระบบ โดยสร้างแต่ละส่วนทีละขั้นตอน
#### 1. เพิ่มขอบเขตการชนกันของรูปสี่เหลี่ยมผืนผ้า
ก่อนอื่น มาสอนวัตถุในเกมของเราให้บอกขอบเขตของมันกัน เพิ่มเมธอดนี้ในคลาส `GameObject` ของคุณ:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width,
};
}
```
**เมธอดนี้ทำให้สำเร็จ:**
- **สร้าง**วัตถุรูปสี่เหลี่ยมพร้อมพิกัดขอบเขตที่แม่นยำ
- **คำนวณ**ขอบล่างและขอบขวาโดยใช้ตำแหน่งบวกกับขนาด
- **ส่งคืน**วัตถุที่พร้อมสำหรับอัลกอริทึมการตรวจจับการชนกัน
- **จัดเตรียม**อินเทอร์เฟซมาตรฐานสำหรับวัตถุในเกมทั้งหมด
#### 2. นำการตรวจจับการตัดกันมาใช้
ตอนนี้มาสร้างนักสืบการชนกันของเรา - ฟังก์ชันที่สามารถบอกได้ว่าเมื่อใดที่รูปสี่เหลี่ยมสองรูปทับซ้อนกัน:
```javascript
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
```
**อัลกอริทึมนี้ทำงานโดย:**
- **ทดสอบ**เงื่อนไขการแยกสี่ข้อระหว่างรูปสี่เหลี่ยม
- **ส่งคืน** `false` หากเงื่อนไขการแยกใด ๆ เป็นจริง
- **ระบุ**การชนกันเมื่อไม่มีการแยก
- **ใช้**ตรรกะการปฏิเสธเพื่อการทดสอบการตัดกันที่มีประสิทธิภาพ
#### 3. นำระบบการยิงเลเซอร์มาใช้
นี่คือจุดที่น่าตื่นเต้น! มาตั้งค่าระบบการยิงเลเซอร์กัน
##### ค่าคงที่ข้อความ
ก่อนอื่น มากำหนดประเภทข้อความบางประเภทเพื่อให้ส่วนต่าง ๆ ของเกมของเราสามารถสื่อสารกันได้:
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
**ค่าคงที่เหล่านี้ให้:**
- **มาตรฐาน**ชื่อเหตุการณ์ทั่วทั้งแอปพลิเคชัน
- **เปิดใช้งาน**การสื่อสารที่สอดคล้องกันระหว่างระบบเกม
- **ป้องกัน**ข้อผิดพลาดในการพิมพ์ในการลงทะเบียนตัวจัดการเหตุการณ์
##### การจัดการการป้อนข้อมูลจากแป้นพิมพ์
เพิ่มการตรวจจับแป้นเว้นวรรคในตัวฟังเหตุการณ์แป้นพิมพ์ของคุณ:
```javascript
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
```
**ตัวจัดการการป้อนข้อมูลนี้:**
- **ตรวจจับ**การกดแป้นเว้นวรรคโดยใช้ keyCode 32
- **ส่ง**ข้อความเหตุการณ์มาตรฐาน
- **เปิดใช้งาน**ตรรกะการยิงที่แยกออกจากกัน
##### การตั้งค่าตัวฟังเหตุการณ์
ลงทะเบียนพฤติกรรมการยิงในฟังก์ชัน `initGame()` ของคุณ:
```javascript
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
if (hero.canFire()) {
hero.fire();
}
});
```
**ตัวฟังเหตุการณ์นี้:**
- **ตอบสนอง**ต่อเหตุการณ์แป้นเว้นวรรค
- **ตรวจสอบ**สถานะคูลดาวน์การยิง
- **กระตุ้น**การสร้างเลเซอร์เมื่อได้รับอนุญาต
เพิ่มการจัดการการชนกันสำหรับการโต้ตอบระหว่างเลเซอร์และศัตรู:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
});
```
**ตัวจัดการการชนกันนี้:**
- **รับ**ข้อมูลเหตุการณ์การชนกันพร้อมวัตถุทั้งสอง
- **ทำเครื่องหมาย**วัตถุทั้งสองเพื่อการลบ
- **รับรอง**การทำความสะอาดที่เหมาะสมหลังการชนกัน
#### 4. สร้างคลาส Laser
นำโปรเจกไทล์เลเซอร์มาใช้ที่เคลื่อนที่ขึ้นด้านบนและจัดการวงจรชีวิตของมันเอง:
```javascript
class Laser extends GameObject {
constructor(x, y) {
super(x, y);
this.width = 9;
this.height = 33;
this.type = 'Laser';
this.img = laserImg;
let id = setInterval(() => {
if (this.y > 0) {
this.y -= 15;
} else {
this.dead = true;
clearInterval(id);
}
}, 100);
}
}
```
**การนำคลาสนี้ไปใช้:**
- **ขยาย** GameObject เพื่อรับฟังก์ชันพื้นฐาน
- **ตั้งค่า**ขนาดที่เหมาะสมสำหรับสไปรต์เลเซอร์
- **สร้าง**การเคลื่อนที่ขึ้นด้านบนโดยอัตโนมัติด้วย `setInterval()`
- **จัดการ**การทำลายตัวเองเมื่อถึงด้านบนของหน้าจอ
- **จัดการ**เวลาแอนิเมชันและการทำความสะอาดของตัวเอง
#### 5. นำระบบตรวจจับการชนกันมาใช้
สร้างฟังก์ชันตรวจจับการชนกันที่ครอบคลุม:
```javascript
function updateGameObjects() {
const enemies = gameObjects.filter(go => go.type === 'Enemy');
const lasers = gameObjects.filter(go => go.type === "Laser");
// Test laser-enemy collisions
lasers.forEach((laser) => {
enemies.forEach((enemy) => {
if (intersectRect(laser.rectFromGameObject(), enemy.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_LASER, {
first: laser,
second: enemy,
});
}
});
});
// Remove destroyed objects
gameObjects = gameObjects.filter(go => !go.dead);
}
```
**ระบบการชนกันนี้:**
- **กรอง**วัตถุในเกมตามประเภทเพื่อการทดสอบที่มีประสิทธิภาพ
- **ทดสอบ**เลเซอร์ทุกอันกับศัตรูทุกตัวเพื่อการตัดกัน
- **ส่ง**เหตุการณ์การชนกันเมื่อพบการตัดกัน
- **ทำความสะอาด**วัตถุที่ถูกทำลายหลังการประมวลผลการชนกัน
> ⚠️ **สำคัญ**: เพิ่ม `updateGameObjects()` ในลูปเกมหลักของคุณใน `window.onload` เพื่อเปิดใช้งานการตรวจจับการชนกัน
#### 6. เพิ่มระบบคูลดาวน์ในคลาส Hero
ปรับปรุงคลาส Hero ด้วยกลไกการยิงและการจำกัดอัตรา:
```javascript
class Hero extends GameObject {
constructor(x, y) {
super(x, y);
this.width = 99;
this.height = 75;
this.type = "Hero";
this.speed = { x: 0, y: 0 };
this.cooldown = 0;
}
fire() {
gameObjects.push(new Laser(this.x + 45, this.y - 10));
this.cooldown = 500;
let id = setInterval(() => {
if (this.cooldown > 0) {
this.cooldown -= 100;
} else {
clearInterval(id);
}
}, 200);
}
canFire() {
return this.cooldown === 0;
}
}
```
**ทำความเข้าใจคลาส Hero ที่ปรับปรุงแล้ว:**
- **เริ่มต้น**ตัวจับเวลาคูลดาวน์ที่ศูนย์ (พร้อมยิง)
- **สร้าง**วัตถุเลเซอร์ที่วางตำแหน่งเหนือยานฮีโร่
- **ตั้งค่า**ช่วงเวลาคูลดาวน์เพื่อป้องกันการยิงแบบรัว
- **ลดลง**ตัวจับเวลาคูลดาวน์โดยใช้การอ
---
**ข้อจำกัดความรับผิดชอบ**:
เอกสารนี้ได้รับการแปลโดยใช้บริการแปลภาษา AI [Co-op Translator](https://github.com/Azure/co-op-translator) แม้ว่าเราจะพยายามให้การแปลมีความถูกต้อง แต่โปรดทราบว่าการแปลโดยอัตโนมัติอาจมีข้อผิดพลาดหรือความไม่ถูกต้อง เอกสารต้นฉบับในภาษาดั้งเดิมควรถือเป็นแหล่งข้อมูลที่เชื่อถือได้ สำหรับข้อมูลที่สำคัญ ขอแนะนำให้ใช้บริการแปลภาษามืออาชีพ เราไม่รับผิดชอบต่อความเข้าใจผิดหรือการตีความผิดที่เกิดจากการใช้การแปลนี้