我上传了几张图片作为2D HTML5 Canvas汽车游戏的精灵。我一直在尝试使用精灵的坐标进行碰撞检测,但它不能顺利进行。我之前听说过边界矩形,据我所知,它们是精灵下的隐形矩形,有助于碰撞检测(如果我错了,请纠正我)。
我在网上看到过像Element.getBoundingClientRect()
这样的内容。
任何人都可以帮助我在我的精灵下放置一些边界矩形,因为我无能为力,我在网上找不到任何基础教程。
Js代码:Jsbin链接:http://jsbin.com/muzulutaci/2/edit?
var canvas = document.getElementById('background');
var context = canvas.getContext('2d');
//================
//ENTER: USER CAR
//================
//Uploading car sprite
var usercar = new Image();
usercar.src = "http://www.iconshock.com/img_jpg/BETA/communications/jpg/128/car_icon.jpg";
//Setting properties of car
var x = 450;
var y = 730;
var speed = 10;
var angle = -90;
var mod = 0;
function drawUserCar() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.save();
context.translate(x, y);
context.rotate(Math.PI / 180 * angle);
context.drawImage(usercar, -(usercar.width / 2), -(usercar.height / 2));
context.restore();
obstacleCar1();
}
//Interval for animation
var moveInterval = setInterval(function () {
drawUserCar();
}, 30);
//=====================
//ENTER: OBSTACLE CAR 1
//=====================
//Uploading obstacle car
var obstcar = new Image();
obstcar.src = "http://www.iconshock.com/img_jpg/BETA/communications/jpg/128/car_icon.jpg";
//Setting properties of obstacle car
var x1 = 450;
var y1 = 300;
var speed1 = 5;
var angle1 = 90;
var mod1 = 0;
function obstacleCar1() {
x1 += (speed1 * mod1) * Math.cos(Math.PI / 180 * angle1);
y1 += (speed1 * mod1) * Math.sin(Math.PI / 180 * angle1);
context.save();
context.translate(x1, y1);
context.rotate(Math.PI / 180 * angle1);
context.drawImage(obstcar, -(obstcar.width / 1), -(obstcar.height / 1));
context.restore();
}
答案 0 :(得分:3)
旋转矩形之间的碰撞检测在数学上很复杂,有几种风格:
检测2个旋转的矩形是否相交(碰撞)
要检测2个旋转的矩形是否发生碰撞(但未检测到它们碰撞的位置),可以使用“分离轴”。这里有一个很好的解释:http://gamedevelopment.tutsplus.com/tutorials/collision-detection-using-the-separating-axis-theorem--gamedev-169
检测旋转的矩形碰撞的位置并将回弹应用于矩形
当2个旋转的矩形发生碰撞时应用反弹需要一些复杂的物理特性。也许最简单的方法是使用像Box2dJS这样的物理库。这是一个很好的Box2dJS演示,显示了碰撞的矩形:http://box2d-js.sourceforge.net/index2.html
示例代码和演示:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var BB=canvas.getBoundingClientRect();
var offsetX=BB.left;
var offsetY=BB.top;
var isDown=false;
var startX;
var startY;
var PI=Math.PI;
var car1Rect,car2Rect;
var cars=[];
var Closure=(function(){
// ctor
function Closure(x,y,imageObject){
var iw=imageObject.width;
var ih=imageObject.height;
this.img=imageObject;
this.x=x;
this.y=y;
this.w=iw;
this.h=iw;
this.cx=x+iw/2;
this.cy=y+ih/2;
this.radius=Math.sqrt(iw*iw+ih*ih)/2;
this.rotation=0;
this.corners=[];
this.isDragging=false;
this.collisionType=0;
// corner angles
var w2=iw/2;
var h2=ih/2;
this.negHalfWidth=-w2;
this.negHalfHeight=-h2;
this.cornerAngles=[
Math.atan2(-h2,-w2), // top-left
Math.atan2(h2,-w2), // top-right
Math.atan2(h2,w2), // bottom-right
Math.atan2(-h2,w2) // bottom-left
];
this.rotateTo(0);
}
//
Closure.prototype.draw=function(){
this.drawImage();
this.drawBB(true);
this.drawBoundingCircle(true);
};
Closure.prototype.moveBy=function(dx,dy){
this.cx+=dx;
this.cy+=dy;
};
Closure.prototype.rotateTo=function(angle){
this.rotation=angle;
this.setCorners();
};
Closure.prototype.setCorners=function(){
this.corners.length=0;
for(var i=0;i<this.cornerAngles.length;i++){
var a=this.cornerAngles[i]+this.rotation;
var x=this.radius*Math.cos(a);
var y=this.radius*Math.sin(a);
this.corners.push({x:x,y:y});
}
};
Closure.prototype.drawBB=function(withStroke){
var p=this.corners;
var cx=this.cx;
var cy=this.cy;
ctx.beginPath();
ctx.moveTo(cx+p[0].x,cy+p[0].y);
for(var i=1;i<p.length;i++){
ctx.lineTo(cx+p[i].x,cy+p[i].y);
}
ctx.closePath();
if(withStroke){
switch(this.collisionType){
case 0:ctx.strokeStyle='gray';break;
case 1:ctx.strokeStyle='green';break;
case 2:ctx.strokeStyle='red';break;
}
ctx.stroke();
}
};
Closure.prototype.drawBoundingCircle=function(withStroke){
var p=this.corners;
var cx=this.cx;
var cy=this.cy;
ctx.beginPath();
ctx.arc(this.cx,this.cy,this.radius,0,PI*2);
ctx.closePath();
if(withStroke){
switch(this.collisionType){
case 0:ctx.strokeStyle='gray';break;
case 1:ctx.strokeStyle='red';break;
case 2:ctx.strokeStyle='red';break;
}
ctx.stroke();
}
};
Closure.prototype.drawImage=function(){
ctx.globalAlpha=0.50;
ctx.translate(this.cx,this.cy);
ctx.rotate(this.rotation);
ctx.drawImage(this.img,this.negHalfWidth,this.negHalfHeight);
ctx.setTransform(1,0,0,1,0,0);
ctx.globalAlpha=1.00;
};
// Closure.prototype.=function(){};
return(Closure);
})();
function calculateCollisionType(r1,r2){
// rough but fast circular bounds hit-test
var dx=r2.cx-r1.cx;
var dy=r2.cy-r1.cy;
var rr=r1.radius+r2.radius;
if(dx*dx+dy*dy>rr*rr){
r1.collisionType=0; // no collision
r2.collisionType=0; // no collision
return(false);
}
// hit-test the bounding rectangles
if(RectanglesIntersect(r1,r2)){
r1.collisionType=2; // bounding rectangles collide
r2.collisionType=2;
}else{
r1.collisionType=1; // circular bounds collide
r2.collisionType=1;
}
return(true);
}
$car1Angle=$('#car1Angle');
$car2Angle=$('#car2Angle');
$car1Angle.val(0);
$car2Angle.val(0);
var carCount=2;
var car1=new Image();
car1.onload=start;
car1.src="https://dl.dropboxusercontent.com/u/139992952/multple/car1.png";
var car2=new Image();
car2.onload=start;
car2.src="https://dl.dropboxusercontent.com/u/139992952/multple/car2.png";
function start(){
if(--carCount>0){return;}
car1Rect=new Closure(50,100,car1);
cars.push(car1Rect);
car2Rect=new Closure(50,250,car2);
cars.push(car2Rect);
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUpOut(e);});
$("#canvas").mouseout(function(e){handleMouseUpOut(e);});
$car1Angle.change(function(){
car1Rect.rotateTo($(this).val()*PI/180);
draw();
});
$car2Angle.change(function(){
car2Rect.rotateTo($(this).val()*PI/180);
draw();
});
calculateCollisionType(car1Rect,car2Rect);
draw();
$('#testCollision').click(function(){
log(RectanglesIntersect(car1Rect,car2Rect));
});
}
function draw(){
ctx.clearRect(0,0,cw,ch);
car2Rect.draw();
car1Rect.draw();
}
function handleMouseDown(e){
e.preventDefault();
e.stopPropagation();
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
isDown=false;
for(var i=0;i<cars.length;i++){
var c=cars[i];
c.drawBB(false);
if(ctx.isPointInPath(startX,startY)){
c.isDragging=true;
isDown=true;
}
}
}
function handleMouseUpOut(e){
e.preventDefault();
e.stopPropagation();
isDown=false;
for(var i=0;i<cars.length;i++){
cars[i].isDragging=false;
}
}
function handleMouseMove(e){
if(!isDown){return;}
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
var dx=mouseX-startX;
var dy=mouseY-startY;
startX=mouseX;
startY=mouseY;
for(var i=0;i<cars.length;i++){
var c=cars[i];
if(c.isDragging){ c.moveBy(dx,dy); }
}
calculateCollisionType(car1Rect,car2Rect);
draw();
}
///////////////////////////////////////
// Attribution for RectanglesIntersect() & isProjectedAxisCollision()
// https://github.com/jozefchutka/YCanvas/blob/master/YCanvasLibrary/libs/yoz/sk/yoz/math/FastCollisions.as
//
function RectanglesIntersect(r1,r2){
// rotated rectangle hit-test
var cx,cy,c;
//
cx=r1.cx;
cy=r1.cy;
c=r1.corners;
//
var r1p1x=cx+c[0].x;
var r1p2x=cx+c[1].x;
var r1p3x=cx+c[2].x;
var r1p4x=cx+c[3].x;
//
var r1p1y=cy+c[0].y;
var r1p2y=cy+c[1].y;
var r1p3y=cy+c[2].y;
var r1p4y=cy+c[3].y;
//
cx=r2.cx;
cy=r2.cy;
c=r2.corners;
//
var r2p1x=cx+c[0].x;
var r2p2x=cx+c[1].x;
var r2p3x=cx+c[2].x;
var r2p4x=cx+c[3].x;
//
var r2p1y=cy+c[0].y;
var r2p2y=cy+c[1].y;
var r2p3y=cy+c[2].y;
var r2p4y=cy+c[3].y;
//
if(!isProjectedAxisCollision(r1p1x, r1p1y, r1p2x, r1p2y,
r2p1x, r2p1y, r2p2x, r2p2y, r2p3x, r2p3y, r2p4x, r2p4y))
return false;
if(!isProjectedAxisCollision(r1p2x, r1p2y, r1p3x, r1p3y,
r2p1x, r2p1y, r2p2x, r2p2y, r2p3x, r2p3y, r2p4x, r2p4y))
return false;
if(!isProjectedAxisCollision(r2p1x, r2p1y, r2p2x, r2p2y,
r1p1x, r1p1y, r1p2x, r1p2y, r1p3x, r1p3y, r1p4x, r1p4y))
return false;
if(!isProjectedAxisCollision(r2p2x, r2p2y, r2p3x, r2p3y,
r1p1x, r1p1y, r1p2x, r1p2y, r1p3x, r1p3y, r1p4x, r1p4y))
return false;
//
return true;
}
function isProjectedAxisCollision( b1x, b1y, b2x, b2y, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y){
var x1, x2, x3, x4;
var y1, y2, y3, y4;
if(b1x == b2x){
x1 = x2 = x3 = x4 = b1x;
y1 = p1y;
y2 = p2y;
y3 = p3y;
y4 = p4y;
if(b1y > b2y)
{
if((y1 > b1y && y2 > b1y && y3 > b1y && y4 > b1y) ||
(y1 < b2y && y2 < b2y && y3 < b2y && y4 < b2y))
return false;
}
else
{
if((y1 > b2y && y2 > b2y && y3 > b2y && y4 > b2y) ||
(y1 < b1y && y2 < b1y && y3 < b1y && y4 < b1y))
return false;
}
return true;
}
else if(b1y == b2y){
x1 = p1x;
x2 = p2x;
x3 = p3x;
x4 = p4x;
y1 = y2 = y3 = y4 = b1y;
}else{
var a = (b1y - b2y) / (b1x - b2x);
var ia = 1 / a;
var t1 = b2x * a - b2y;
var t2 = 1 / (a + ia);
x1 = (p1y + t1 + p1x * ia) * t2;
x2 = (p2y + t1 + p2x * ia) * t2;
x3 = (p3y + t1 + p3x * ia) * t2;
x4 = (p4y + t1 + p4x * ia) * t2;
y1 = p1y + (p1x - x1) * ia;
y2 = p2y + (p2x - x2) * ia;
y3 = p3y + (p3x - x3) * ia;
y4 = p4y + (p4x - x4) * ia;
}
if(b1x > b2x){
if((x1 > b1x && x2 > b1x && x3 > b1x && x4 > b1x) ||
(x1 < b2x && x2 < b2x && x3 < b2x && x4 < b2x))
return false;
}else{
if((x1 > b2x && x2 > b2x && x3 > b2x && x4 > b2x) ||
(x1 < b1x && x2 < b1x && x3 < b1x && x4 < b1x))
return false;
}
return true;
}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
Red car angle: <input id=car1Angle type=range min=0 max=360 value=0><br>
Gold car angle: <input id=car2Angle type=range min=0 max=360 value=0><br>
<h4>Use sliders above to rotate cars.<br>Drag cars closer.<br>Bounding Circles turn green if they collide.<br>Bounding Rectangles turn green if they collide.</h4>
<br>
<canvas id="canvas" width=400 height=500></canvas>
以上演示最好以全屏模式观看