2014-12-13 4 views
2

2D HTML5 캔버스 자동차 게임을위한 스프라이트로 사용할 이미지 몇 장을 업로드했습니다. 필자는 스프라이트의 좌표를 사용하여 충돌 감지를 시도했지만 원활하게 작동하지 않습니다. 전에 경계 사각형에 대한 언급을 들었습니다. 그리고 내 지식으로 볼 때, 충돌 탐지에 도움이되는 스프라이트 (sprites) 아래의 보이지 않는 직사각형입니다 (잘못된 경우 올바른 수정).스프라이트 HTML5 캔버스 아래 테두리 사각형 만들기

온라인에서 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 사각형이 충돌되는 회전 경우

감지하기 (충돌)과 교차하는 경우 검색 (그러나 충돌하는 부분은 감지하지 못합니다), 분리 축 톰 (Separating Axis Theorm)을 사용할 수 있습니다. 좋은 설명은 여기에 있습니다 :

http://gamedevelopment.tutsplus.com/tutorials/collision-detection-using-the-separating-axis-theorem--gamedev-169는 회전 된 사각형이 충돌되는 경우 검색 및 2 사각형 충돌하는 회전 반등을 적용하려면 사각형

에 반등을 적용 할 몇 가지 복잡한 물리학을 필요로한다. 아마도 가장 쉬운 방법은 Box2dJS와 같은 물리 라이브러리를 사용하는 것입니다. http://box2d-js.sourceforge.net/index2.html

예제 코드와 데모 : : 여기에 충돌 직사각형을 보여주는 Box2dJS의 좋은 데모입니다

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

+0

그래, 내 예제는 모두 경계 사각형과 경계 원을 테스트합니다. 경계 원은 회전 된 경계 사각형보다 훨씬 간단합니다. 내 예제에서 '둥근하지만 빠른 원형 경계 히트 테스트'로 주석 처리 된 경계 원을 사용하는 부분을 참조하십시오. 건배! – markE