2014-12-13 180 views
2

我已經上傳了幾張圖片來充當2D HTML5 Canvas汽車遊戲的精靈。我一直在嘗試使用精靈的座標來進行碰撞檢測,但不能順利運行。我聽說過之前提到了邊界矩形,並且據我所知,它們是在精靈之下的無形矩形,有助於碰撞檢測(如果我錯了,請糾正我)。Sprites下的邊界矩形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(); 

      } 

回答

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:&nbsp<input id=car1Angle type=range min=0 max=360 value=0><br> 
 
Gold car angle:&nbsp<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>

以上在全屏模式下最佳效果的演示

+0

有沒有比使用邊界矩形更簡單的方法來做碰撞檢測? – SasukeRinnegan 2014-12-13 21:18:04

+0

是的,我的例子命中 - 測試邊界矩形和邊界。邊界圓比旋轉的邊界矩形簡單得多。請參閱我的示例中使用邊界圓的部分,該邊界用'粗糙而快速的圓形邊界命中測試'評論。乾杯! – markE 2014-12-13 21:22:07