Thông thường các đối tượng trong game sẽ được xác định va chạm bằng cách đưa chúng về một dạng hình học cơ bản như hình chữ nhật, hình tròn. Bài viết này sẽ giúp bạn cách tính toán để kiểm tra va chạm giữa hai loại hình học này.
Giữa hai hình chữ nhật
Phương pháp: kiểm tra từng đỉnh của hình này có nằm bên trong hình kia không.
Rect.prototype.collideWidthRect = function(rect) { return this.contains(rect.left,rect.top) || this.contains(rect.right,rect.top) || this.contains(rect.right,rect.bottom) || this.contains(rect.left,rect.bottom); }
Cách trên không phải cách nhanh nhất, vì vậy bạn có thể dùng cách sau, đơn giản và hiệu quả hơn:
Rect.prototype.collideWidthRect = function(rect) { return !(this.left > rect.right || this.right < rect.left || this.top > rect.bottom || this.bottom < rect.top); }
Giữa hai hình tròn
Phương pháp: Bởi vì mọi điểm nằm trên đường tròn cách đều tâm, nên việc kiểm tra va chạm giữa hai hình tròn sẽ được xác định dựa vào khoảng cách tâm giữa chúng.
Để xác định khoảng cách giữa hai điểm, ta dựa vào định lý Pythagoras (Pythagorean theorem) . Ta coi khoảng cách giữa hai điểm là đường chéo của một tam giác vuông. Vậy độ lớn của đường chéo này là:
c² = a² + b² => c = sqrt(a² + b²)Circle.prototype.collideWithCircle = function(circle){ var dx = this.cx - circle.cx; var dy = this.cy - circle.cy; return Math.sqrt(dx*dx + dy*dy) <= this.radius+circle.radius; }Trong minh họa dưới đây, hai hình tròn có màu mặc định là xanh lá, khi va chạm nhau, chúng sẽ chuyển sang màu đỏ.
Giữa hình tròn và hình chữ nhật
Phương pháp: Gọi C là tâm và R là bán kính hình tròn. Ta sẽ tìm cách xác định điểm A là điểm gần nhất thuộc hình chữ nhật đến tâm C. Sau đó so sánh độ lớn của CA với R.
Khoảng cách giữa tâm C hình tròn và điểm A của hình chữ nhật được minh họa như hình dưới đây. Khi tâm hình tròn nằm bên trong hình chữ nhật, thì điểm C và A sẽ trùng nhau.Gọi rect là hình chữ nhật cần xác định va chạm. Ta có thuật toán để xác định điểm A như sau:
– B1: Gán A bằng với C.
– B2: Nếu C.X < rect.Left, đặt A.X = rect.Left. Ngược lại nếu C.X > rect.Right, đặt A.X = rect.Right.
– B3: Nếu C.Y < rect.Top, đặt A.Y = rect.Top. Ngược lại nếu C.Y > rect.Bottom, đặt A.Y = rect.Bottom.Khi đã có điểm A, ta lại dùng công thức Pythagoras để so sánh với bán kính của hình tròn.
Circle.prototype.collideWithRect = function(rect){ var px = this.cx; var py = this.cy; if(px < rect.left) px = rect.left; else if(px > rect.right) px = rect.right; if(py < rect.top) py = rect.top; else if(py > rect.bottom) py = rect.bottom; var dx = this.cx - px; var dy = this.cy - py; return (dx*dx + dy*dy) <= this.radius*this.radius; }YinYang’s Blog
Mình rất thích các bài viết của bạn, rất cơ bản và dễ hiểu. Và mình đã học được kha khá kiến thức từ bạn. Thanks bạn
Thank you!
Rất hay, phát huy nhé
minh moi hoc html 5 nen cung chua biet nhieu . ban co the giai thich cho minh Rect.prototype.collideWidthRect co y nghia la j khong? thank ban!!!
Đây là cách để tạo một phương thức instance cho một “class” trong javascript. Tức là khi bạn tạo một đối tượng Rect, bạn có gọi phương thức collideWidthRect của đối tượng này.
bài viết của bạn rất tuyệt.Thank bạn nhiều . Minh xin được mượn ý tưởng và gợi ý bạn làm bài của mình nhen 🙂
bạn có làm cho xna được không? mình rất cần những cái này nhưng mà code băng xna
Tất nhiên là có thể nhưng không biết bạn hỏi làm gì?
Cho em hỏi va chạm giữa 2 đường tròn có thể tính khoảng cách giữa 2 tâm rồi sau đó so sánh với tổng bán kính 2 đường tròn nếu tổng bán kính nhỏ hơn hoặc bằng khoảng cách thì va chạm, cách này được ko anh?
Đó là cách mình thực hiện trong bài đó. Việc tính khoảng cách giữa 2 tâm như trên sử dụng công thức Pythagoras.
quá tuyệt bài viết của anh giúp em rất nhiều
Cám ơn bạn đã hướng dẫn bài này bằng hình ảnh và code rất dễ hiểu.Nhưng cho mình xin hỏi.Mình có thắc mắc sau: như trong trường hợp va chạm thứ 3(Tròn vs HCN),ở 4 phía Left(rect.X – rect.W/2),Right(rect.X + rect.W/2),Top(rect.Y+rect.H/2),Bottom(rect.Y-rect.H/2)
– Sau khi gán A = C ;
– if(C.X <= Left ){ A.X = Left; }
else(C.X <= Right ) { A.X = Right; }
Vậy có cần xét thêm trường hợp :
else if(Left <= C.X <= Right){?}
và các hướng còn lại tương tự.
Ý mình là nếu không xét thêm trường hợp tiếp điểm nằm khoảng giữa thì nó có tự hiểu giữ nguyên giá trị của A.X = C.X và hàm này có được thực thi đúng công thức hay không?Mình là người khá kỹ tính mà code chưa vững lắm nên mình mới thắc mắc.Rất mong bạn chỉ dẫn mình thêm.Chân thành cám ơn bạn.
Mình bổ sung câu hỏi thêm :
Vì nếu như trường hợp mình nêu trên là tiếp điểm nằm giữa,có thể nó sẽ ko chạy vào if()else() –> dx = this.cx – px // =0 ? (ban đầu đã gán px = cx) .
Mình hiểu rồi..xin lỗi và cám ơn bài viết của bạn rất nhiều..mình quên là khúc cuối vẫn có trường hợp xét <= r*r 🙂 hihi..nếu có choáng chỗ cmt cho mọi người thì bạn có thể tháo cmt của mình xuống :)..chúc bạn nhiều sức khỏe 🙂