Javascript 如何对两幅部分透明的图像进行像素级的碰撞检测

Javascript 如何对两幅部分透明的图像进行像素级的碰撞检测,javascript,jquery,canvas,Javascript,Jquery,Canvas,所以基本上我需要做的是,当屏幕上的两个字符接触,有人按下按钮时,他们的健康就会被剥夺。我唯一不知道怎么做的就是如何检测他们何时触摸 $(document).ready(function(){ var canvas = document.createElement("canvas"); var context = canvas.getContext("2d"); canvas.width = 1000; canvas.height = 600; document.body.appendChil

所以基本上我需要做的是,当屏幕上的两个字符接触,有人按下按钮时,他们的健康就会被剥夺。我唯一不知道怎么做的就是如何检测他们何时触摸

$(document).ready(function(){


var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
canvas.width = 1000;
canvas.height = 600;
document.body.appendChild(canvas);

var kGroundHeight = 500;

/*var upKey = 38;
var downKey = 40;
var leftKey = 37;
var rightKey = 39;
*/


var render = function() {
  gravity();
  gravity1();



  context.clearRect(0, 0, canvas.width, canvas.height);

    context.fillRect(0,kGroundHeight,canvas.width,10);

  context.drawImage(kirby, kirbyObject.x, kirbyObject.y);

    context.drawImage(link, linkObject.x, linkObject.y);

};




var main = function() {

  render();
  window.requestAnimationFrame(main);
};

main();
});




var linkReady = false;
var link = new Image();
link.onLoad = function() {

  linkReady = true;
};


linkObject = {};
link.src= "https://vignette1.wikia.nocookie.net/zelda/images/1/18/Link_(Sprite)_The_Legend_of_Zelda.png/revision/latest?cb=20130117162823";

linkObject.x = 200;
linkObject.y = 200;






var keys = {};




$(document).keydown(function(e) {
    console.log(e);

   move1(e.keyCode);

    if (keys[87] && keys[65]) {
       linkObject.y -=50;
       linkObject.x -=50;


    }else if(keys[87] && keys[68]){

      linkObject.y -=50;
      linkObject.x +=50;


    }else if(keys[83] && keys[68]){

      linkObject.y +=50;
      linkObject.x +=50;


    }else if(keys[83] && keys[65]){

      linkObject.y +=50;
      linkObject.x -=50;
    }

    keys[e.keyCode] = true;
}).keyup(function(e) {

   keys[e.keyCode] = false;



});

var upKey1 = 87;
var downKey1 = 83;
var leftKey1 = 65;
var rightKey1 = 68;
var attackKey1 = 75; 


var move1 = function(key) {

  if (key == upKey1) {
    linkObject.y -= 50;
  }else if(key == downKey1){

      var kGroundHeight = 500;

     if(linkObject.y + link.height ==  kGroundHeight){

      linkObject.y +=0;
    }else{

      linkObject.y +=50;

    }


    //console.log("down");
        console.log(linkObject.y);


  }else if(key == leftKey1){

    linkObject.x -=50;
  }else if(key == rightKey1 ){

    linkObject.x +=50;
  }
  // MORE DIRECTIONS!!!
};




var gravity1 = function() {
    var kGravityScale = 1;
  var kGroundHeight = 500;

  if (linkObject.y + link.height ==  kGroundHeight) {

    linkObject.y += 0;

  }else{
        linkObject.y += kGravityScale;
//console.log(link.width);
  }
};


var attack = function(a){



};



 var kGroundHeight = 500;


var keys = {};




$(document).keydown(function(e) {
    console.log(e);

   move(e.keyCode);

    if (keys[38] && keys[37]) {
       kirbyObject.y -=50;
       kirbyObject.x -=50;


    }else if(keys[38] && keys[39]){

      kirbyObject.y -=50;
      kirbyObject.x +=50;


    }else if(keys[40] && keys[39]){

      kirbyObject.y +=50;
      kirbyObject.x +=50;


    }else if(keys[40] && keys[37]){

      kirbyObject.y +=50;
      kirbyObject.x -=50;
    }

    keys[e.keyCode] = true;
}).keyup(function(e) {

   keys[e.keyCode] = false;



});


var kirbyReady = false;
var kirby = new Image();
kirby.onLoad = function() {
  kirbyReady = true;
};

kirbyObject = {};
kirby.src = "https://vignette3.wikia.nocookie.net/spritechronicles/images/5/5c/Kirby.png/revision/latest?cb=20101010225540";




kirbyObject.x = 300;
kirbyObject.y = 100;



var upKey = 38;
var downKey = 40;
var leftKey = 37;
var rightKey = 39;
var attackKey = 32; 



var move = function(key) {

  if (key == upKey) {
    kirbyObject.y -= 50;
  }else if(key == downKey){

      var kGroundHeight = 500;

     if(kirbyObject.y + kirby.height ==  kGroundHeight){

      kirbyObject.y +=0;
    }else{

      kirbyObject.y +=50;

    }


    //console.log("down");
        console.log(kirbyObject.y);


  }else if(key == leftKey){

    kirbyObject.x -=50;
  }else if(key == rightKey ){

    kirbyObject.x +=50;
  }
  // MORE DIRECTIONS!!!
};




var gravity = function() {
    var kGravityScale = 1;
  var kGroundHeight = 500;

  if (kirbyObject.y + kirby.height ==  kGroundHeight) {

    kirbyObject.y += 0;

  }else{
        kirbyObject.y += kGravityScale;

  }
};

你可以分两步来做

  • 检查边界圆是否重叠

  • 如果圆重叠,请检查(更精确的)边界矩形重叠

  • 如果有两个图像,则每个图像的中心都等于
    (x+w/2),(y+h/2)
    。它们的半径都等于
    sqrt((w/2)^2+(h/2)^2)

    当两个圆之间的距离(
    sqrt((x1-x2)^2+(y1-y2)^2)
    )小于
    max(radius1,radius2)
    时,两个圆重叠


    边界矩形碰撞检测是直观的,但计算可能更困难(可能会影响大量对象)。

    您可以通过两个步骤完成此操作

  • 检查边界圆是否重叠

  • 如果圆重叠,请检查(更精确的)边界矩形重叠

  • 如果有两个图像,则每个图像的中心都等于
    (x+w/2),(y+h/2)
    。它们的半径都等于
    sqrt((w/2)^2+(h/2)^2)

    当两个圆之间的距离(
    sqrt((x1-x2)^2+(y1-y2)^2)
    )小于
    max(radius1,radius2)
    时,两个圆重叠


    边界矩形碰撞检测直观,但计算可能更困难(可能影响大量对象)。

    径向周长测试

    通过使用一组极坐标来定义每个精灵的形状,可以实现几乎像素级的快速碰撞。每个坐标描述距中心的距离(中心是任意的,但必须位于精灵内部)以及距中心最远像素的中心沿该方向的方向。坐标数(n)由最外层像素的周长确定。我不知道之前是否已经描述过它,因为我现在刚刚想到它,所以它的实际健壮性需要测试

    概念

    下图显示了基本概念

    精灵(1.)覆盖极坐标和外边界圆(2)。从0-15(3)索引每个坐标,提取检查碰撞所需的信息。绿色(a)是每个精灵的角度原点,黄色线P是a和B之间的向量,标记为与角度原点(4)的角度

    要获得需要访问像素信息的坐标,可以在制作过程中添加为代码,或者在游戏设置过程中进行。它将产生一个类似于

    var sprite = {
        ...
        collisionData : {
            maxRadius : 128,
            minRadius : 20,
            coords : [128,30,50, ... ],
        }
    }
    
    片段1

    现在,您已经将每个精灵描述为进行测试所需的一组极坐标

    假设精灵将有一个位置(x,y像素)、一个旋转(r弧度)和一个比例(s方形)

    片段2

    碰撞测试

    对于
    pA
    pB
    引用精灵位置数据(代码段2)和
    cA
    cB
    引用每个精灵碰撞数据(代码段1)的两个精灵,我们首先进行距离测试,检查最大半径和最小半径。如果发生冲突,代码将返回true

    const TAU = Math.PI * 2; // use this alot so make it a constant
    var xd = pA.x - pB.x;          // get x distance
    var yd = pA.y - pB.y;          // get y distance
    var dist = Math.hypot(xd,yd);  // get the distance between sprites
                                   // Please note that legacy browsers will not 
                                   // support hypot 
    // now scale the max radius of each sprite and test if the distance is less 
    // than the sum of both.
    if (dist <= cA.maxRadius * pA.s + cB.maxRadius * pB.s){
         // passed first test sprites may be touching
         // now check the min radius scaled
         if (dist <= Math.min(cA.minRadius * pA.s, cB.minRadius * pB.s) * 2 ){
              // the sprites are closer than the smallest of the two's min
              // radius scaled so must be touching
              return true;  // all done return true
         }
    
    下一步将归一化相对方向转换为极坐标阵列中的正确索引需要考虑每个极坐标的角宽度。见图3。在图中,每个极坐标顶部的扁平位是角宽度。要做到这一点,我们使用
    Math.round
    从0-1缩放到坐标数。由于接近1的值将四舍五入到错误的索引,您还必须使用模
    %
    ,以确保它不会超出范围

        var indexA = Math.round(dirA * cA.coords.length) % cA.coords.length;
        var indexB = Math.round(dirB * cB.coords.length) % cB.coords.length;
    
        // now we can get the length of the coordinates.
        // also scale them at the same time
        var la = cA.coords[indexA] * pA.s;
        var lb = cB.coords[indexB] * pB.s;
    
        // now test if the distance between the sprites is less than the sum
        // of the two length
        if( dist <= la + lb ){
            // yes the two are touching
            return true;
        }
    }
    
    var indexA=Math.round(dirA*cA.coords.length)%cA.coords.length;
    var indexB=数学圆(dirB*cB.coords.length)%cB.coords.length;
    //现在我们可以得到坐标的长度。
    //同时也可以对其进行缩放
    var la=cA.coords[indexA]*pA.s;
    var lb=cB.coords[indexB]*pB.s;
    //现在测试精灵之间的距离是否小于总和
    //两头
    
    如果(距离)径向周长测试

    通过使用一组极坐标定义每个精灵的形状,可以实现几乎像素级的快速完美碰撞。每个坐标描述到中心的距离(中心是任意的,但必须在精灵内部)从最远像素的中心沿该方向的方向。坐标数(n)由最外层像素的周长决定。我不知道之前是否已经描述过,因为我现在刚刚想到了,所以它的实际鲁棒性需要测试

    概念

    下图显示了基本概念

    精灵(1.)覆盖极坐标和外边界圆(2)。从0-15(3)索引每个坐标,提取检查碰撞所需的信息。绿色(a)是每个精灵的角度原点,黄色线p是标记为角度原点(4)角度的a和B之间的向量

    要获取坐标,您需要访问像素信息,这可以在制作过程中完成,并作为代码添加,或者在游戏设置过程中完成

    var sprite = {
        ...
        collisionData : {
            maxRadius : 128,
            minRadius : 20,
            coords : [128,30,50, ... ],
        }
    }
    
    片段1

    现在,您已经将每个精灵描述为进行测试所需的一组极坐标

    假设精灵将有一个位置(x,y像素)、一个旋转(r弧度)和一个比例(s方形)

    片段2

    碰撞测试

    对于
    pA    var indexA = Math.round(dirA * cA.coords.length) % cA.coords.length;
        var indexB = Math.round(dirB * cB.coords.length) % cB.coords.length;
    
        // now we can get the length of the coordinates.
        // also scale them at the same time
        var la = cA.coords[indexA] * pA.s;
        var lb = cB.coords[indexB] * pB.s;
    
        // now test if the distance between the sprites is less than the sum
        // of the two length
        if( dist <= la + lb ){
            // yes the two are touching
            return true;
        }
    }
    
     // get the angle step for A and B
     var angleStepA = TAU / cA.coord.length;
     var angleStepB = TAU / cB.coord.length;
    
     // the number of coordinates to offset 
     var offCount = 1;
    
     // get next coord clockwise from A and scale it
     var lengOffA = cA.coord[(index + offCount) % cA.coord.length] * pA.s;
    
     // get next coordinate counter clockwise from B and scale it
     var lengOffB = cB.coord[(index + cB.coord.length - offCount) % cB.coord.length] * pB.s;
    
     // Now correct for the offest angle
     lengOffA *= Math.cos(offCount * angleStepA);
     lengOffB *= Math.cos(offCount * angleStepB);
    
     // Note that as you move away the length will end up being negative because
     // the coord will point away.
    
     if( dist < lengOffA + lengOffB ){
         // yes a hit
         return true;
     }
    
     if (/*horizontal*/ (sprite.x + sprite.width >= sprite2.x && sprite.x <= sprite2.x + sprite2.width && sprite2.hidden == false) && /*vertical*/ (sprite.y + sprite.height >= sprite2.y && sprite.y <= sprite2.y + sprite2.height && sprite.hidden == false)) {
    }
    
    // draws sprite onto canvas
    var spriteCanvas = document.createElement("CANVAS");
    var spriteCtx = spriteCanvas.getContext("2d");
    spriteCanvas.width = canvas.width;
    spriteCanvas.height = canvas.height;
    
    var spriteImage = new Image();
    spriteImage.src = sprite.base64Src;
    spriteCtx.drawImage(spriteImage, sprite.x, sprite.y, sprite.width, sprite.height);
    
    // draws sprite2 onto canvas
    var sprite2Canvas = document.createElement("CANVAS");
    var sprite2Ctx = sprite2Canvas.getContext("2d");
    sprite2Canvas.width = canvas.width;
    sprite2Canvas.height = canvas.height;
    
    var sprite2Image = new Image();
    sprite2Image.src = sprite2.base64Src;
    sprite2Ctx.drawImage(sprite2Image, sprite2.x, sprite2.y, sprite2.width, sprite2.height);
    
    // gets the overlap of the two
    var spriteOverlap;
    var sprite2Overlap;
            
    var cropX = (sprite.x > sprite2.x) ? [sprite.x, (sprite2.x + sprite2.width) - sprite.x + 1] : [sprite2.x, (sprite.x + sprite.width) - sprite2.x + 1];
            var cropY = (sprite.y + sprite.height > sprite2.y + sprite2.height) ? [sprite.y, (sprite2.y + sprite2.height) - sprite.y + 1] : [sprite2.y, (sprite.y + sprite.height) - sprite2.y + 1];
            
    spriteOverlap = spriteCtx.getImageData(cropX[0], cropY[0], cropX[1], cropY[1]).data;
    sprite2Overlap = sprite2Ctx.getImageData(cropX[0], cropY[0], cropX[1], cropY[1]).data;
                
    
    pixelOverlap = false;
    
    // loops through every overlaping pixel in sprite2
    for(var i = 0; i < (sprite2Overlap.length / 4); i++){
        // checks if the current pixel has an opacity greater than one on both sprite or sprite2
        if(sprite2Overlap[i * 3] > 0 && spriteOverlap[i * 3] > 0){
            pixelOverlap = true;
        }
    }
    
    // if a pixel overlap was already found, sprite2 makes the function run faster
    if(!pixelOverlap){
        // loops through every overlaping pixel in sprite
        for(var i = 0; i < (spriteOverlap.length / 4); i++){
            // checks if the current pixel has an opacity greater than one on both sprite or sprite2
            if(sprite2Overlap[i * 3] > 0 && spriteOverlap[i * 3] > 0){
                pixelOverlap = true;
            }
        }
    }
    
    return pixelOverlap;