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

类Agar.io游戏开发:碰撞分离时如何避免Blob移动速度改变

嘿,这个问题我太熟了!做Agar.io这类物理类吃豆游戏时,重叠分离最容易踩的就是「忽略质量/动量」的坑——你现在的分离逻辑应该是直接把两个Blob往反方向挪,没考虑谁大谁小,所以才会出现小Blob带着大Blob跑的诡异情况。

核心解决思路:用动量守恒处理分离位移

Agar.io里的Blob运动本质是符合动量守恒的:质量大的物体惯性大,受到相同作用力时位移更小。这里的质量我们可以直接用Blob的体积(或者简化成半径的平方,因为面积和半径平方成正比,完全能代表质量权重)来代替。

第一步:先算出重叠的核心参数

首先得明确两个重叠Blob的关键数据:

  • 中心距离:distance = 根号下[(b.x - a.x)² + (b.y - a.y)²]
  • 重叠量:overlap = (a.radius + b.radius) - distance(只有当overlap > 0时才需要分离)
  • 分离方向向量:把(b.x - a.x, b.y - a.y)归一化(除以distance),得到纯粹的方向,避免距离影响位移幅度

第二步:按质量分配位移比例

假设Blob的质量和半径平方成正比(简化计算,效果完全一致),那么两个Blob的位移需要满足:
a的位移 × a的质量 = b的位移 × b的质量
同时两者位移总和要刚好抵消重叠量,也就是a的位移 + b的位移 = overlap

推导后就能得到各自的位移:

  • 总质量:totalMass = a.radius² + b.radius²
  • a的位移:moveA = overlap × (b.radius² / totalMass)
  • b的位移:moveB = overlap × (a.radius² / totalMass)

这样一来,小Blob会被弹开更多,大Blob几乎不动——完全符合物理直觉,不会出现小Blob带跑大Blob的情况!

第三步:修改你的separatePlayer函数

把上面的逻辑整合进去,替换原来的直接位移逻辑,给你个完整示例:

function separatePlayer() {
  // 用双层循环避免重复检测(每对Blob只检查一次)
  for (let i = 0; i < blobsPlayer.length; i++) {
    const a = blobsPlayer[i];
    for (let j = i + 1; j < blobsPlayer.length; j++) {
      const b = blobsPlayer[j];
      
      // 计算中心距离
      const dx = b.x - a.x;
      const dy = b.y - a.y;
      const distance = Math.sqrt(dx * dx + dy * dy);
      const minSafeDistance = a.radius + b.radius;
      
      // 仅处理重叠且距离不为0的情况(避免除以0)
      if (distance < minSafeDistance && distance > 0) {
        const overlap = minSafeDistance - distance;
        // 归一化分离方向
        const dirX = dx / distance;
        const dirY = dy / distance;
        
        // 计算质量权重(用半径平方简化)
        const massA = a.radius * a.radius;
        const massB = b.radius * b.radius;
        const totalMass = massA + massB;
        
        // 按质量分配位移
        const moveA = overlap * (massB / totalMass);
        const moveB = overlap * (massA / totalMass);
        
        // 应用位移:a往反方向挪,b往正方向挪
        a.x -= dirX * moveA;
        a.y -= dirY * moveA;
        b.x += dirX * moveB;
        b.y += dirY * moveB;
      }
    }
  }
}

额外优化:严格保持移动速率

如果分离后你想让Blob完全保持原来的移动速率(大Blob本来就慢,小Blob快),可以在分离完成后,对每个Blob重新归一化速度向量,再乘以它的原始速率:

// 假设每个Blob有speed(固定速率)和velocity(方向向量)属性
function resetBlobSpeed(blob) {
  const velLength = Math.sqrt(blob.velocity.x * blob.velocity.x + blob.velocity.y * blob.velocity.y);
  if (velLength > 0) {
    // 归一化后乘以原始速率,保证速率不变
    blob.velocity.x = (blob.velocity.x / velLength) * blob.speed;
    blob.velocity.y = (blob.velocity.y / velLength) * blob.speed;
  }
}

在separatePlayer函数最后,遍历所有Blob调用这个方法就行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:17:18