繁体   English   中英

HTML5 Canvas JS 上的碰撞检测

[英]Collision Detection on HTML5 Canvas JS

我查看了有关此主题的其他帖子,但找不到任何实际适用于我的场景的帖子

基本上,我正在制作一个 2d 剑游戏,玩家可以在其中互相战斗。 “玩家”只是一个圆圈,剑只是附在圆圈上并指向鼠标指向的地方......

在此处输入图片说明

基本上我必须检测剑何时接触另一个玩家。

这是我在画布上渲染播放器的代码...

function drawImageLookat(img, sword, x, y, lookx, looky, mouseisdown){
   ctx.setTransform(1, 0, 0, 1, x, y);  
   ctx.rotate(Math.atan2(looky - y, lookx - x));
   ctx.drawImage(img,-img.width / 2, -img.height / 2);
   ctx.save();

   // draw second image
   ctx.transform(1, 0, 0, 1, img.width / 2, img.height / 2); 
   if(mouseisdown) {
     ctx.rotate(30 * Math.PI / 180);
 
   ctx.drawImage(sword, -sword.width / 2+30, -sword.height / 2-20);
   } else {

ctx.drawImage(sword, -sword.width / 2+30, -sword.height / 2-20);
   }

   ctx.restore();  // get the parent transform
ctx.setTransform(1, 0, 0, 1, 0, 0); 
}
imgready = false
    const smiley = new Image()
 const cupcake = new Image()

    const sword1 = new Image()

smiley.src = "./assets/images/smiley.png"
cupcake.src = "./assets/images/cupcake.png"

sword1.src = "./assets/images/sword1.png"
smiley.onload = () => {
imgready = true;
}


const drawPlayer = (player) => {
if(imgready) {
if(player.show) {
if(player.msg) {
  ctx.font = "20px Arial";
ctx.fillText(player.msgcontent, player.x, player.y-50);
}

drawImageLookat(eval(player.image), eval(player.sword), player.x, player.y, player.lookx, player.looky, player.mousedown)
}
}
  };
  
  socket.on('state', (gameState) => {
      ctx.clearRect(0,0, 1200, 999999999999999999)
    for (let player in gameState.players) {
      drawPlayer(gameState.players[player])
    }
  })

每 1000/60 毫秒,'state' 由服务器发出,客户端绘制 gameState 拥有的所有玩家。 现在基本上我想如果鼠标按下,检查剑是否接触其他玩家。 它可以是一个布尔结果。 如何检查剑是否接触?

任何帮助表示感谢! 我的完整代码也在这里: https : //repl.it/@CoderGautamYT/FunChat-1

使用线段的距离点

我假设它的剑(作为线段)要检查是否接触角色(圆圈)

如果线段接触圆,以下函数将返回true

// x1, y1, x2, y2  line segement
// cx, cy  center of circle
// r radius of circle
function isLineTouchingCircle(x1, y1, x2, y2, cx, cy, r){
    const self = isLineTouchingCircle;  // expecting that you will want the closest point
    const v1x = x2 - x1;
    const v1y = y2 - y1;
    var u = ((cx - x1) * v1x + (cy - y1) * v1y) / (v1y * v1y + v1x * v1x);
    u = u < 0 ? 0 : u > 1 ? 1 : u; 
    const x = (self.x = x1 + v1x * u) - cx;
    const y = (self.y = y1 + v1y * u) - cy;
    
    return (x * x + y * y) - r * r < 0;
}

将转换后的剑精灵转换为线段

   const angle = Math.atan2(looky - y, lookx - x);
   const angle1 = 30 * Math.PI / 180; // You should use radians

    const swordLength = sword.height
    // base of sword relative to player
    var x1 = img.width / 2;       
    var y1 = img.height / 2;      
    const ax = Math.cos(angle);
    const ay = Math.sin(angle);

    // find base of sword rotated to player
    var x2 = x1 * ax - y1 * ay + x;
    var y2 = x1 * ay + y1 * ax + y;
    
    // find tip of sword (I dont know which way the sword points 
    // so rotated -90 deg  "the (- Math.PI / 2) part" )
    var x3 = Math.cos(angle + angle1 - Math.PI / 2) * swordLength + x2;
    var y3 = Math.sin(angle + angle1 - Math.PI / 2) * swordLength + y2;

    // check contact
    if (isLineTouchingCircle(x2, y2, x3, y3, player2.x, player2.y, player2.r)) {

    }

请注意,我有点不确定剑图像的方向。

笑脸 V 笑脸

为了确保它有效

 const player = (x, y, r, dir, col="green") => ({x,y,r, dir, col, dx: 0, dy: 0, strikeCount: 0, lefty: false, sDir: 0, hit: false, home: false}); const SIZE = 400; const PLY1 = player(100,SIZE/2, 30, 0); const PLY2 = player(SIZE - 100,SIZE/2, 30, 0); PLY2.lefty = true; const ctx = canvas.getContext("2d"); canvas.height = canvas.width = SIZE; const blood = document.createElement("canvas"); blood.height = blood.width = SIZE; const ctxB = blood.getContext("2d"); const playerImg = { w: 60, h: 60, path: ((p=new Path2D())=> (p.addPath(new Path2D("M16 3a23 23 0 0112-2h5a28 28 0 0118 9h1a28 28 0 017 20l-1 7a21 21 0 01-4 13h-1a26 26 0 01-9 7v1a23 23 0 01-12 1h-5a28 28 0 01-18-9 28 28 0 01-8-20l1-7a21 21 0 015-13 26 26 0 019-7zm-2 9a7 7 0 00-5 2 7 7 0 00-2 5v4a7 7 0 002 5 7 7 0 005 2h4a7 7 0 005-2 7 7 0 002-5v-4a7 7 0 00-2-5 7 7 0 00-5-2zm28 0a7 7 0 00-5 2 7 7 0 00-2 5v4a7 7 0 002 5 7 7 0 005 2h4a7 7 0 005-2 7 7 0 002-5v-4a7 7 0 00-2-5 7 7 0 00-5-2zm0 6a3 3 0 014-1 2 2 0 011 2v2a3 3 0 01-4 3 2 2 0 01-2-4zm-29 1a3 3 0 014-1 2 2 0 011 2l-1 2a2 2 0 01-3 1 2 2 0 01-1-3zm1 22a1 1 0 00-2 2 7 7 0 004 4l4 2a20 20 0 0012 2h1a19 19 0 009-4l2-3a2 2 0 001-2 2 2 0 00-3 0l-2 1a14 14 0 01-7 4h-1a15 15 0 01-10-1z"), new DOMMatrix([1,0,0,1,-30, -30])), p))(), } const swordImg = { w: 29, h: 126, path: ((p=new Path2D())=> (p.addPath(new Path2D("M14 0l4 7a30 30 0 014 15v74h6v8h-8l-1 22-9-1v-21l-9-1v-7h7V22a37 37 0 013-15zm0 8l-2 88h6zm-1 95l-1 20 5 1 1-20z"),new DOMMatrix([1,0,0,1,-14.5, -120])), p))(), } function isLineTouchingCircle(x1, y1, x2, y2, cx, cy, r){ const self = isLineTouchingCircle; const v1x = x2 - x1; const v1y = y2 - y1; var u = ((cx - x1) * v1x + (cy - y1) * v1y) / (v1y * v1y + v1x * v1x); u = u < 0 ? 0 : u > 1 ? 1 : u; const x = (self.x = x1 + v1x * u) - cx; const y = (self.y = y1 + v1y * u) - cy; return (x * x + y * y) - r * r < 0; } function updatePlayer(p, lookTo) { p.dir = Math.atan2(py - lookTo.y, px - lookTo.x); const dist = Math.hypot(py - lookTo.y, px - lookTo.x); if (Math.random() < (p.hit ? 0.1 : 0.01)) { const move = p.dir + Math.random() ** 2 * Math.sign(Math.random() - 0.5) + (p.hit ? Math.PI : 0); const hs = p.home ? 0.5 : 1; p.dx = Math.cos(move) * (p.hit ? 1 + Math.random() : hs); p.dy = Math.sin(move) * (p.hit ? 1 + Math.random() : hs); } if (p.strikeCount < 0 && dist < 120 && Math.random() < 0.05) { p.strike = true; p.strikeCount = 30; } else { if (p.strikeCount < 20) { p.strike = false } p.strikeCount --; } px += p.dx; py += p.dy; px = (px % SIZE + SIZE) % SIZE; py = (py % SIZE + SIZE) % SIZE; } function drawPlayer(p) { const ax = Math.cos(p.dir); const ay = Math.sin(p.dir); ctx.setTransform(ax, ay, -ay, ax, px, py); if (p.hit) { ctx.strokeStyle = "#800"; ctx.lineWidth = 8; ctx.stroke(playerImg.path); } ctx.fillStyle = p.col; ctx.fill(playerImg.path, "evenodd"); if(p.lefty) { ctx.transform(1, 0, 0, 1, playerImg.w / 2, playerImg.h / 2); if (p.strike) { ctx.rotate(Math.PI * 0.4); p.sDir = Math.PI * 0.4; } else { p.sDir = 0 } } else { ctx.transform(1, 0, 0, 1, -playerImg.w / 2, playerImg.h / 2); if (p.strike) { ctx.rotate(-Math.PI * 0.4); p.sDir = -Math.PI * 0.4; } else { p.sDir = 0 } } ctx.fillStyle = p.home ? "#F00" : "#000"; ctx.fill(swordImg.path, "evenodd"); p.home = p.hit = false; } function testHit(p, p1) { const left = p.lefty ? 1 : -1; const x1 = playerImg.w / 2 * left; const y1 = playerImg.h / 2; const ax = Math.cos(p.dir) const ay = Math.sin(p.dir) const x2 = x1 * ax - y1 * ay + px; const y2 = x1 * ay + y1 * ax + py; const x3 = Math.cos(p.dir + p.sDir - Math.PI / 2) * swordImg.h + x2; const y3 = Math.sin(p.dir + p.sDir - Math.PI / 2) * swordImg.h + y2; if (isLineTouchingCircle(x2, y2, x3, y3, p1.x, p1.y, p1.r)) { p.home = p1.hit = true; drawBlood(isLineTouchingCircle.x, isLineTouchingCircle.y, Math.random() * 4 + 1); } } const bCol = ["#F00","#E02","#D04"]; function drawBlood(x, y, count) { const d = Math.random() * Math.PI * 2; const s = Math.random() ** 3 * 40; const r = Math.random() ** 4 * 6 + 1 ctxB.setTransform(1,0,0,1,Math.cos(d) * s + x, Math.sin(d) * s + y); ctxB.beginPath(); ctxB.fillStyle = bCol[Math.random() * 3 | 0]; ctxB.arc(0,0, r, 0, Math.PI * 2); ctxB.fill(); count > 0 && drawBlood(x, y, count - 1); } requestAnimationFrame(mainLoop); var frame = 0; function mainLoop() { ctx.setTransform(1,0,0,1,0,0); ctx.clearRect(0,0,SIZE,SIZE); ctx.drawImage(blood, 0, 0); if (frame++ % 8 === 0) { ctxB.setTransform(1,0,0,1,0,0); ctxB.fillStyle = "white"; ctxB.globalAlpha = 0.1; ctxB.fillRect(0,0,SIZE,SIZE); ctxB.globalAlpha = 1; } updatePlayer(PLY1, PLY2); updatePlayer(PLY2, PLY1); drawPlayer(PLY1); drawPlayer(PLY2); PLY1.strike && testHit(PLY1, PLY2); PLY2.strike && testHit(PLY2, PLY1); requestAnimationFrame(mainLoop); }
 div { border: 2px solid black; overflow: hidden; width: 150px; height: 150px; } canvas { width: 170px; height: 170px; transform: matrix(1, 0, 0, 1, -10, -10); }
 <div><canvas id="canvas"></canvas></div>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM