HTML5 – Canvas: Kiểm tra va chạm dựa trên pixel

Html5 - Canvas - Pixel Collision DetectionCác đối tượng đồ họa (hoặc hình ảnh) trong game thường được một giới hạn trong một khung bao hình chữ nhật có nền trong suốt (pixel có alpha = 0). Như vậy đối với các đối tượng phức tạp và muốn kiểm tra va chạm chính xác, ta cần kiểm tra các pixel có độ alpha > 0 của hai đối tượng đồ họa có cùng nằm trên một vị trí hay không.

Trước khi đọc tiếp, bạn có thể tìm hiểu trước về đối tượng ImageData và xử lý pixel trong canvas.

Một wrapper của Image

Để tiện xử lý các hình ảnh trong canvas dưới dạng một đối tượng trong game với các chức năng cần thiết, tôi tạo một lớp ImageObj chứa bên trong một đối tượng Image để lưu trữ hình ảnh. Phương thức quan trọng mà ImageObj phải có là draw(), tuy nhiên việc nạp ảnh có thể diễn ra khá lâu vì vậy ta cần một cách thức xử lý trường hợp này.
Chẳng hạn tôi sẽ viết ra một dòng thông báo thay thế cho ảnh với kích thước mặc định trong trường hợp ảnh chưa được tải xong:

function ImageObj(){
	// ...
	this.draw = function(context){
		if(ready){
			context.drawImage(this.img,this.left,this.top);
		}
		else{
			// image has not finished loading
			// draw something useful instead
			context.save();
			context.fillText("Image is not ready",this.left+10,this.top+10);
			context.restore();
		}
		context.strokeRect(this.left,this.top,this.width,this.height);
		context.stroke();
	};
	// ...
}

Chưa đủ, để đối tượng ImageObj có thể tự vẽ lại sau ảnh được nạp xong, đồng thời lấy được đối tượng ImageData (chứa mảng các pixel), ta sẽ viết một số lệnh xử lý trong sự kiện onload của Image.

function ImageObj(){
	// ...
	var self = this;
	this.img.onload = function(){
		self.width = this.width;
		self.height = this.height;
		ready = true; // this image is ready to use

		// draw image after loading
		context.clearRect(self.left,self.top,self.width,self.height);
		self.draw(context);
		// get ImageData from this image
		self.data = context.getImageData(self.left,self.top,self.width,self.height).data;
	};
	this.img.src = url;
}

Do vấn đề bảo mật, ta sẽ không lấy được ImageData của các ảnh không nằm trong host hiện tại (khác host mà script được thực thi).

Xác định vùng giao hai hình chữ nhật

Thay vì lặp qua toàn bộ hai hình ảnh và kiểm tra từng pixel của chúng. Ta sẽ giới hạn lại phần ảnh cần kiểm tra bằng cách xác định vùng giao giữa hai hình ảnh. Phần này cũng giúp ta xác định nhanh hai đối tượng có thể xảy ra va chạm hay không.

function findIntersectionRect(img1,img2){

	var rect = {
		left: Math.max(img1.left,img2.left),
		top: Math.max(img1.top,img2.top),
		right: Math.min(img1.left+img1.width,img2.left+img1.width),
		bottom: Math.min(img1.top+img1.height,img2.top+img2.height)
	};

	if(rect.left>rect.right || rect.top>rect.bottom)
		return  null;
	return rect;
}

Xem Demo.

Html5 - Canvas - Intersection Rectangle

Kiểm tra va chạm

Nếu đã tìm hiểu về đối tượng ImageData, bạn biết rằng các pixel sẽ được lưu trữ trong một mảng một chiều. Mỗi pixel bao gồm bốn phần tử đứng liền nhau trong mảng theo thứ tự là [Red, Green, Blue, Alpha]. Như vậy một ảnh có kích thước 20×30 sẽ có 600 pixel và có 600×4=2400 phần tử trong ImageData.

 function checkPixelCollision(img1,img2){
	if(!img1.data || !img2.data)
		return null;
	var rect = 	findIntersectionRect(img1,img2);
	if(rect)
	{
		// this array will hold all collision points of two images
		var points = [];
		for(var i=rect.left;i<=rect.right;i++){
			for(var j=rect.top;j<=rect.bottom;j++){
				var index1 = ((i-img1.left)+(j-img1.top)*img1.width)*4+3;
				var index2 = ((i-img2.left)+(j-img2.top)*img2.width)*4+3;
				if(img1.data[index1+3]!=0 && img2.data[index2+3]!=0)
				{
					points.push({x:i,y:j});
					// you can exit here instead of continue looping
				}
			}
		}

		return {
			rect: rect,
			points: points
		};
	}
	return null;
}

Để tăng tốc độ kiểm tra va chạm, thay vì lặp qua từng pixel, bạn có thể lặp ngắt quãng n pixel (theo cả 2 chiều ngang và dọc) tùy theo mức độ yêu cầu chính xác của ứng dụng. Như vậy số lần lặp để kiểm tra sẽ giảm đi 2n lần tương ứng. Dưới đây là ảnh minh họa việc thay đổi bước tăng của vòng lặp từ 1 lên 3. Vùng màu đỏ là các pixel có độ alpha > 0 thuộc cả hai ảnh:

Html5 - Canvas - Pixel Collsion - Density

Download Demo.

YinYangIt’s Blog

Advertisements

2 thoughts on “HTML5 – Canvas: Kiểm tra va chạm dựa trên pixel

Trả lờ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