Html5-Canvas: Viết game Tower Defense – part 1

Tower Defender (tháp phòng thủ) là một thể loại game rất phổ biến và có sức hấp dẫn người chơi bởi có cách chơi đơn giản nhưng lại mang tính chiến thuật và đối kháng của nó. Hơn nữa mỗi game lại có thể khả năng sáng tạo và mở rộng linh hoạt để đem đến những tính năng hấp dẫn người chơi.

Giới thiệu

Trong tut này, tôi sẽ hướng dẫn viết một nền tảng game Tower Defense để bạn có thể phát triển lên thành một sản phẩm mang tính đặc trưng của riêng mình.

“Mục tiêu của trò chơi tháp phòng thủ là cố gắng để ngăn chặn kẻ thù vượt qua bản đồ bằng cách xây dựng các tòa tháp bắn vào họ khi họ đi qua. Kẻ thù và tháp thường có đa dạng về khả năng, chi phí. Khi kẻ địch bị đánh bại, người chơi kiếm được tiền hoặc điểm, được sử dụng để mua hoặc nâng cấp tháp, hoặc nâng cấp các số tiền hoặc các điểm thu được, hoặc thậm chí nâng cấp để giảm thời gian nâng cấp.” (Wikipedia)

Mục đích của tôi là tạo ra một game có những đặc điểm sau:
– Hỗ trợ nhiều con đường, cũng như nhiều điểm bắt đầu và kết thúc.
– Các tower có thể thay đổi về tầm bắn, sát thương, tốc độ,… và có thể nâng cấp.
– Các tower có thể đặt tại vị trí bất kì được phép mà không cần phải đặt chính xác vào từng ô.
– …

Trong tut này, tôi rút kinh nghiệm và sẽ sử dụng lập trình theo hướng event để xác định trạng thái của các đối tượng. Ví dụ như khi enemy đến đích, nó sẽ kích hoạt một event là reachedTarget.

Xem Demo.

Html5-Tower-Defender-1

Tạo Enemy

Trong game này, enemy có thể coi là một đối tượng AI nhưng cũng có thể là đối tượng đơn giản nhất bởi vì nó chỉ có khả năng “cắm đầu chạy” theo một con đường định sẵn. Vì vậy công việc của ta là chỉ cần “dẫn lỗi” để chúng đi lần lượt đến từng điểm trên con đường và đến đích.
Thuật toán rất đơn giản:
– Cho enemy xuất phát tại điểm đầu tiên của con đường.
– Lấy tọa độ điểm tiếp theo và tính toán góc xoay, hướng di chuyển và coi đó là một đích tạm thời.
– Di chuyển cho đến đích tạm thời và lặp lại bước trên cho đến điểm cuối cùng.

Ví dụ tôi có phương thức tính góc xoay và hướng di chuyển từ điểm hiện tại (preX, preY) của enemy đến điểm kế tiếp trên đường (lấy bằng chỉ số currentPoint) với hàm Math.atan2. Bạn có thể đọc về một ví dụ khác giới thiệu cách sử dụng hàm atan2 là Game xe tăng đơn giản.

function calcDirection(preX, preY){

	var dx = roadX[currentPoint] - preX;
	var dy = roadY[currentPoint] - preY;

	var angle = Math.atan2(dy, dx);
	speedX = Math.cos(angle) * speed;
	speedY = Math.sin(angle) * speed;

};

Để kiểm tra enemy chạm đích mới, ta cần tính toán khoảng cách từ tâm enemy đến điểm đó với một sai số EPSILON. Và khi đó, ta sẽ tính toán đích mới dựa vào phương thức calcuDirection() trên. Nếu enemy đến điểm cuối cùng nó sẽ kích hoạt một event là reachedTarget nếu như event này được định nghĩa.

this.update = function(){

	var x = roadX[currentPoint];
	var y = roadY[currentPoint];

	var cx = this.left + HALF_UNIT_SIZE;
	var cy = this.top + HALF_UNIT_SIZE;

	var dx = Math.abs(cx - x);
	var dy = Math.abs(cy - y);

	if(dx < EPSILON && dy < EPSILON)
	{
		if(currentPoint < roadX.length - 1)
		{
			currentPoint++;
			calcDirection(cx, cy);
		}
		// reached the last point
		else if (this.reachedTarget)
			this.reachedTarget(); // raise reachedTarget event
	}else
	{
		this.left += speedX;
		this.top += speedY;
	}
};

Tạo bản đồ

Với bản đồ đơn giản, tôi chỉ tạo một con đường duy nhất đi từ điểm đầu đến đích. Con đường này được chia thành hai mảng roadX, roadY theo tọa độ ngang và dọc. Mỗi phần tử của mảng là 1 điểm trên bản đồ để nối lại thành một con đường hoàn chỉnh. Với số lượng và vị trí các điểm không bị giới hạn, ta có thể tạo ra các con đường uốn khúc hoặc thẳng tắp bằng cách thêm/bớt các điểm.
Trong dữ liệu này, tôi cũng định nghĩa cả số lượng enemy của mỗi lượt trong map.

MAPS.push
	({
		numEnemies		: 10,

		roadX 	: [10,50,50,300,250,90,30,50,160,340,200,WIDTH],
		roadY 	: [10,30,50,70,110,100,200,300,220,140,340,HEIGHT],
	});

Để vẽ bản đồ, tôi tạo một canvas phụ làm buffer và vẽ lên đó. Ta cũng sẽ dùng buffer này để xác định xem một vị trí nào đó có thể được đặt tower hay không (dựa vào màu sắc).

this.buffer = document.createElement("canvas");
this.buffer.width = WIDTH;
this.buffer.height = HEIGHT;
this.context = this.buffer.getContext("2d");

// ...
// draw the road
this.context.strokeStyle = "white";
this.context.lineWidth = UNIT_SIZE*1.2;
this.context.lineJoin = "round";

this.context.beginPath();
this.context.moveTo(this.levelData.roadX[0],this.levelData.roadY[0]);
for(var i = 1;i < this.levelData.roadX.length; i++)
{
	this.context.lineTo(this.levelData.roadX[i],this.levelData.roadY[i]);
}
// ...

Trong Map, khi tạo mới một enemy, ta sẽ đồng thời định nghĩa một function để xử lý event reachedTarget của nó:

Map.prototype.update = function(){

	var enemies = this.enemies;
	if(enemies.length < this.levelData.numEnemies)
	{
		var tick = (new Date()).getTime();
		if(tick - this.lastAddEnemy > this.addEnemyDelay)
		{
			this.lastAddEnemy = tick;
			var e = new Enemy(this.levelData.roadX, this.levelData.roadY);
			e.reachedTarget = function(){
				enemies.shift();
			};
			enemies.push(e);
		}
	}
	for(x in this.enemies)
		enemies[x].update();

}

YinYangIt’s Blog

4 thoughts on “Html5-Canvas: Viết game Tower Defense – part 1

  1. Hi anh!
    Em thấy trong hầu hết game anh viết hay sử dụng kỹ thuật tạo thêm một canvas phụ làm buffer. Anh có thể cho em biết tại sao phải sử dụng kỹ thuật này không, nó có ưu điểm gì so với cách dùng “context.getImageData() và context.putImageData()”.
    Cám ơn anh!!

    Trả lời
  2. Chào Yin Yang,
    Mình đang học lập trình web và tập trung vào Javascript với mong muốn sau này dùng JS để lập trình game trên Canvas bằng JS. Nhưng hiện nay kiến thức về lập trình Game mình vẫn bắt đầu bằng con số 0. Mình muốn hỏi là lập trình game trên Canvas bằng JS thì cần nắm rõ những đối tượng nào của JS và những kiến thức gì lưu ý, tập trung nghiên cứu. Ngoài ra, mình cũng muốn hỏi khái niệm về “Game” trong tư duy lập trình gồm những bước nào? Xây dựng map, tạo monster, heroe, weapon ….các va chạm, tấn công, phòng thủ ….

    Trả lời

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s