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/vi/6-space-game/4-collision-detection/README.md

311 lines
13 KiB

<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "a6ce295ff03bb49df7a3e17e6e7100a0",
"translation_date": "2025-08-29T08:52:35+00:00",
"source_file": "6-space-game/4-collision-detection/README.md",
"language_code": "vi"
}
-->
# Xây dựng trò chơi không gian Phần 4: Thêm Laser và Phát hiện Va chạm
## Câu hỏi trước bài học
[Câu hỏi trước bài học](https://ff-quizzes.netlify.app/web/quiz/35)
Trong bài học này, bạn sẽ học cách bắn laser bằng JavaScript! Chúng ta sẽ thêm hai yếu tố vào trò chơi:
- **Laser**: laser được bắn từ tàu của nhân vật chính và di chuyển theo chiều dọc lên trên.
- **Phát hiện va chạm**, như một phần của việc triển khai khả năng *bắn*, chúng ta cũng sẽ thêm một số quy tắc trò chơi thú vị:
- **Laser bắn trúng kẻ thù**: Kẻ thù sẽ bị tiêu diệt nếu bị laser bắn trúng.
- **Laser chạm vào đỉnh màn hình**: Laser sẽ bị phá hủy nếu chạm vào phần trên cùng của màn hình.
- **Kẻ thù và nhân vật chính va chạm**: Kẻ thù và nhân vật chính sẽ bị phá hủy nếu va chạm với nhau.
- **Kẻ thù chạm đáy màn hình**: Kẻ thù và nhân vật chính sẽ bị phá hủy nếu kẻ thù chạm vào đáy màn hình.
Tóm lại, bạn -- *nhân vật chính* -- cần bắn hạ tất cả kẻ thù bằng laser trước khi chúng kịp di chuyển xuống đáy màn hình.
✅ Hãy tìm hiểu một chút về trò chơi máy tính đầu tiên từng được viết. Chức năng của nó là gì?
Hãy cùng trở thành anh hùng nào!
## Phát hiện va chạm
Làm thế nào để phát hiện va chạm? Chúng ta cần nghĩ về các đối tượng trong trò chơi như những hình chữ nhật di chuyển. Tại sao lại như vậy? Bởi vì hình ảnh được sử dụng để vẽ một đối tượng trò chơi là một hình chữ nhật: nó có `x`, `y`, `width``height`.
Nếu hai hình chữ nhật, ví dụ như nhân vật chính và kẻ thù *giao nhau*, thì bạn có một va chạm. Điều gì xảy ra tiếp theo sẽ phụ thuộc vào các quy tắc của trò chơi. Để triển khai phát hiện va chạm, bạn cần:
1. Một cách để lấy đại diện hình chữ nhật của một đối tượng trò chơi, như sau:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width
}
}
```
2. Một hàm so sánh, hàm này có thể trông như sau:
```javascript
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
```
## Làm thế nào để phá hủy các đối tượng
Để phá hy các đối tượng trong trò chơi, bn cn cho trò chơi biết rng đối tượng đó không nên được v li trong vòng lp trò chơi kích hot theo mt khong thi gian nht định. Mt cách để làm điu này là đánh du mt đối tượng trò chơi là *đã chết* khi có điu gì đó xy ra, như sau:
```javascript
// collision happened
enemy.dead = true
```
Sau đó, bn có th x lý các đối tượng *đã chết* trước khi v li màn hình, như sau:
```javascript
gameObjects = gameObject.filter(go => !go.dead);
```
## Làm thế nào để bắn laser
Bn laser có nghĩa là phn hi mt s kin phím và to ra mt đối tượng di chuyn theo mt hướng nht định. Vì vy, chúng ta cn thc hin các bước sau:
1. **Tạo một đối tượng laser**: t đỉnh tàu ca nhân vt chính, đối tượng này s bt đầu di chuyn lên trên v phía đỉnh màn hình ngay khi được to.
2. **Gắn mã vào một sự kiện phím**: chúng ta cn chn mt phím trên bàn phím để đại din cho hành động bn laser ca người chơi.
3. **Tạo một đối tượng trò chơi trông giống như laser** khi phím được nhn.
## Thời gian hồi chiêu của laser
Laser cn được bn mi khi bn nhn mt phím, ví d như phím *space*. Để ngăn trò chơi to ra quá nhiu laser trong mt khong thi gian ngn, chúng ta cn khc phc điu này. Cách khc phc là trin khai mt cái gi là *thời gian hồi chiêu*, mt b đếm thi gian đảm bo rng laser ch có th được bn sau mt khong thi gian nht định. Bn có th trin khai điu này như sau:
```javascript
class Cooldown {
constructor(time) {
this.cool = false;
setTimeout(() => {
this.cool = true;
}, time)
}
}
class Weapon {
constructor {
}
fire() {
if (!this.cooldown || this.cooldown.cool) {
// produce a laser
this.cooldown = new Cooldown(500);
} else {
// do nothing - it hasn't cooled down yet.
}
}
}
```
Tham kho bài hc 1 trong lot bài trò chơi không gian để nhc li v *thời gian hồi chiêu*.
## Những gì cần xây dựng
Bn s s dng mã hin có (mà bn nên đã dn dp và tái cu trúc) t bài hc trước và m rng nó. Hoc bt đầu vi mã t phn II hoc s dng mã ti [Phần III - khởi đầu](../../../../../../../../../your-work).
> mẹo: laser mà bạn sẽ làm việc đã có sẵn trong thư mục tài sản và được tham chiếu bởi mã của bạn.
- **Thêm phát hin va chm**, khi laser va chm vi mt đối tượng, các quy tc sau s được áp dng:
1. **Laser bắn trúng kẻ thù**: k thù s chết nếu b laser bn trúng.
2. **Laser chạm vào đỉnh màn hình**: Laser s b phá hy nếu chm vào phn trên cùng ca màn hình.
3. **Kẻ thù và nhân vật chính va chạm**: k thù và nhân vt chính s b phá hy nếu va chm vi nhau.
4. **Kẻ thù chạm đáy màn hình**: K thù và nhân vt chính s b phá hy nếu k thù chm vào đáy màn hình.
## Các bước được khuyến nghị
Tìm các tp đã được to sn cho bn trong thư mc con `your-work`. Nó s cha các tp sau:
```bash
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
```
Bn bt đầu d án trong thư mc `your_work` bng cách gõ:
```bash
cd your-work
npm start
```
Lnh trên s khi động mt HTTP Server ti địa ch `http://localhost:5000`. M trình duyt và nhp địa ch đó, hin ti nó s hin th nhân vt chính và tt c k thù, nhưng chưa có gì di chuyn - vn chưa :).
### Thêm mã
1. **Thiết lập đại diện hình chữ nhật cho đối tượng trò chơi để xử lý va chạm**. Đon mã dưới đây cho phép bn ly đại din hình ch nht ca mt `GameObject`. Chnh sa lp GameObject để m rng nó:
```javascript
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width,
};
}
```
2. **Thêm mã kiểm tra va chạm**. Đây s là mt hàm mi kim tra xem hai hình ch nht có giao nhau không:
```javascript
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
```
3. **Thêm khả năng bắn laser**
1. **Thêm thông báo sự kiện phím**. Phím *space* s to ra mt laser ngay phía trên tàu ca nhân vt chính. Thêm ba hng s vào đối tượng Messages:
```javascript
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
```
1. **Xử lý phím space**. Chnh sa hàm `window.addEventListener` keyup để x lý phím space:
```javascript
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
```
1. **Thêm listeners**. Chnh sa hàm `initGame()` để đảm bo rng nhân vt chính có th bn khi phím space được nhn:
```javascript
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
if (hero.canFire()) {
hero.fire();
}
```
và thêm một hàm `eventEmitter.on()` mới để đảm bảo hành vi khi kẻ thù va chạm với laser:
```javascript
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
})
```
1. **Di chuyển đối tượng**, đảm bảo laser di chuyển dần lên đỉnh màn hình. Bạn sẽ tạo một lớp Laser mới mở rộng từ `GameObject`, như bạn đã làm trước đây:
```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)
}
}
```
1. **Xử lý va chạm**, triển khai các quy tắc va chạm cho laser. Thêm một hàm `updateGameObjects()` để kiểm tra các đối tượng va chạm:
```javascript
function updateGameObjects() {
const enemies = gameObjects.filter(go => go.type === 'Enemy');
const lasers = gameObjects.filter((go) => go.type === "Laser");
// laser hit something
lasers.forEach((l) => {
enemies.forEach((m) => {
if (intersectRect(l.rectFromGameObject(), m.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_LASER, {
first: l,
second: m,
});
}
});
});
gameObjects = gameObjects.filter(go => !go.dead);
}
```
Đảm bảo thêm `updateGameObjects()` vào vòng lặp trò chơi trong `window.onload`.
4. **Triển khai thời gian hồi chiêu** cho laser, để nó chỉ có thể được bắn sau một khoảng thời gian nhất định.
Cuối cùng, chỉnh sửa lớp Hero để nó có thể xử lý thời gian hồi chiêu:
```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;
}
}
```
Đến thời điểm này, trò chơi của bạn đã có một số chức năng! Bạn có thể di chuyển bằng các phím mũi tên, bắn laser bằng phím space, và kẻ thù sẽ biến mất khi bạn bắn trúng chúng. Làm tốt lắm!
---
## 🚀 Thử thách
Thêm hiệu ứng nổ! Xem các tài sản trò chơi trong [kho Space Art](../../../../6-space-game/solution/spaceArt/readme.txt) và thử thêm hiệu ứng nổ khi laser bắn trúng người ngoài hành tinh.
## Câu hỏi sau bài học
[Câu hỏi sau bài học](https://ff-quizzes.netlify.app/web/quiz/36)
## Ôn tập & Tự học
Thử nghiệm với các khoảng thời gian trong trò chơi của bạn cho đến nay. Điều gì xảy ra khi bạn thay đổi chúng? Đọc thêm về [các sự kiện thời gian trong JavaScript](https://www.freecodecamp.org/news/javascript-timing-events-settimeout-and-setinterval/).
## Bài tập
[Khám phá va chạm](assignment.md)
---
**Tuyên bố miễn trừ trách nhiệm**:
Tài liệu này đã được dịch bằng dịch vụ dịch thuật AI [Co-op Translator](https://github.com/Azure/co-op-translator). Mặc dù chúng tôi cố gắng đảm bảo độ chính xác, xin lưu ý rằng các bản dịch tự động có thể chứa lỗi hoặc không chính xác. Tài liệu gốc bằng ngôn ngữ bản địa nên được coi là nguồn tham khảo chính thức. Đối với các thông tin quan trọng, chúng tôi khuyến nghị sử dụng dịch vụ dịch thuật chuyên nghiệp từ con người. Chúng tôi không chịu trách nhiệm cho bất kỳ sự hiểu lầm hoặc diễn giải sai nào phát sinh từ việc sử dụng bản dịch này.