You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Canvas中两个fill.Rect玩家实现半径触发的碰撞检测?

为Canvas双人游戏添加距离碰撞检测

需求说明

我开发了一款双人游戏:

  • 一名玩家通过方向键控制,另一名通过WSAD键控制
  • 游戏画布尺寸为600×600
  • 两个玩家均通过fillRect绘制
  • 需要添加碰撞检测:当两名玩家处于指定半径范围内时,结束游戏或显示“game over”文本

现有代码

HTML代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />
<canvas id="mycan"></canvas>
    
    <font color='blue'><h1>Player1 = blue</h1></font>
    <font color='red'><h1>Player2 = red</h1></font>

  </head>
  <body>
    <main></main>
    <script src="script.js"></script>
  </body>
</html>

CSS代码

html, body {
  margin:20;
  padding: 20;
}
canvas {
  display: block;
}

#mycan {
  background-size: 30px 30px;
  background-image:
    linear-gradient(to right, black 1px, transparent 1px),
    linear-gradient(to bottom, black 1px, green 1px);
}

JavaScript代码

//Canvas
mycan.style.display = "block";
mycan.height = 600;
mycan.width = 600;
//make players
let player = {x:511, y: 541, w:29, h:29};
let player2 = {x:60, y:31, w:30, h:29};


//Context
const ctx = mycan.getContext("2d");


//Start-position
ctx.fillRect(player.x, player.y, player.w, player.h);
ctx.fillRect(player2.x, player2.y, player2.w, player2.h);
//No-smooth-movement
window.onkeydown = move = (e) => {
    let key = e.keyCode;
  //player1(red)
    if     (key === 68 && player2.x <= mycan.width-30) {player2.x += 30;} //right
    else if(key === 65 && player2.x >= 10) {player2.x -= 30;} //left
    else if(key === 83 && player2.y <= mycan.height-30) {player2.y += 30;} //down
    else if(key === 87 && player2.y >= 10) {player2.y -= 30;} //up  
  
  
  //player2(blue)
    if     (key === 39 && player.x <= mycan.width-25) {player.x += 30;} //right
    else if(key === 37 && player.x >= 10) {player.x -= 30;} //left
    else if(key === 40 && player.y <= mycan.height-25) {player.y += 30;} //down
    else if(key === 38 && player.y >= 10) {player.y -= 30;} //up
}

const draw = ()=>{
//player draw, and player colors
  ctx.clearRect(0,0, mycan.width, mycan.height);
  ctx.fillRect(player.x, player.y, player.w, player.h);
  ctx.fillStyle = "blue";
  ctx.fillRect(player2.x,player2.y, player2.w, player2.h);
  ctx.fillStyle = 'red';
};

setInterval(()=>{
  draw();
},1000/60);

//collision
//this is where i want to add collision

解决方案

1. 实现距离碰撞检测逻辑

先计算两个玩家的中心坐标,再通过勾股定理计算两点间的距离平方(避免开根号提升性能),最后和设定的碰撞半径平方对比:

// 检测碰撞的函数
function checkCollision() {
  // 计算两个玩家的中心坐标
  const playerCenterX = player.x + player.w / 2;
  const playerCenterY = player.y + player.h / 2;
  const player2CenterX = player2.x + player2.w / 2;
  const player2CenterY = player2.y + player2.h / 2;

  // 计算两点间距离的平方
  const distanceSquared = Math.pow(playerCenterX - player2CenterX, 2) + Math.pow(playerCenterY - player2CenterY, 2);
  // 设定碰撞半径的平方(示例半径为50)
  const collisionRadius = 50;
  const collisionRadiusSquared = Math.pow(collisionRadius, 2);

  // 距离小于等于设定半径时触发碰撞
  return distanceSquared <= collisionRadiusSquared;
}

2. 整合到游戏循环并处理结束逻辑

修改游戏循环逻辑,添加碰撞检测后的终止操作:

// 保存定时器ID,方便后续清除
let gameLoop = setInterval(()=>{
  draw();
  // 每次绘制时检测碰撞
  if (checkCollision()) {
    // 停止游戏循环
    clearInterval(gameLoop);
    // 绘制Game Over文本
    ctx.fillStyle = 'black';
    ctx.font = '48px Arial';
    ctx.textAlign = 'center';
    ctx.fillText('GAME OVER', mycan.width / 2, mycan.height / 2);
    // 禁用键盘控制
    window.onkeydown = null;
  }
},1000/60);

3. 完整修改后的JavaScript代码

//Canvas
mycan.style.display = "block";
mycan.height = 600;
mycan.width = 600;
//make players
let player = {x:511, y: 541, w:29, h:29};
let player2 = {x:60, y:31, w:30, h:29};

//Context
const ctx = mycan.getContext("2d");

//Start-position
ctx.fillRect(player.x, player.y, player.w, player.h);
ctx.fillRect(player2.x, player2.y, player2.w, player2.h);

//No-smooth-movement
window.onkeydown = move = (e) => {
    let key = e.keyCode;
  //player1(red)
    if     (key === 68 && player2.x <= mycan.width-30) {player2.x += 30;} //right
    else if(key === 65 && player2.x >= 10) {player2.x -= 30;} //left
    else if(key === 83 && player2.y <= mycan.height-30) {player2.y += 30;} //down
    else if(key === 87 && player2.y >= 10) {player2.y -= 30;} //up  
  
  //player2(blue)
    if     (key === 39 && player.x <= mycan.width-25) {player.x += 30;} //right
    else if(key === 37 && player.x >= 10) {player.x -= 30;} //left
    else if(key === 40 && player.y <= mycan.height-25) {player.y += 30;} //down
    else if(key === 38 && player.y >= 10) {player.y -= 30;} //up
}

const draw = ()=>{
//player draw, and player colors
  ctx.clearRect(0,0, mycan.width, mycan.height);
  ctx.fillStyle = 'red';
  ctx.fillRect(player.x, player.y, player.w, player.h);
  ctx.fillStyle = "blue";
  ctx.fillRect(player2.x,player2.y, player2.w, player2.h);
};

// 检测碰撞的函数
function checkCollision() {
  const playerCenterX = player.x + player.w / 2;
  const playerCenterY = player.y + player.h / 2;
  const player2CenterX = player2.x + player2.w / 2;
  const player2CenterY = player2.y + player2.h / 2;

  const distanceSquared = Math.pow(playerCenterX - player2CenterX, 2) + Math.pow(playerCenterY - player2CenterY, 2);
  const collisionRadius = 50; // 可根据需求调整半径值
  const collisionRadiusSquared = Math.pow(collisionRadius, 2);

  return distanceSquared <= collisionRadiusSquared;
}

// 游戏循环
let gameLoop = setInterval(()=>{
  draw();
  if (checkCollision()) {
    clearInterval(gameLoop);
    ctx.fillStyle = 'black';
    ctx.font = '48px Arial';
    ctx.textAlign = 'center';
    ctx.fillText('GAME OVER', mycan.width / 2, mycan.height / 2);
    window.onkeydown = null;
  }
},1000/60);

说明

  • 可通过修改collisionRadius值调整碰撞触发的严格程度,数值越小触发越灵敏
  • 使用距离平方对比是为了避免开根号运算,提升游戏性能
  • 碰撞触发后的逻辑可自定义,比如添加重新开始按钮、弹窗提示等

内容的提问来源于stack exchange,提问作者Jude Mauldin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 01:05:45