类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
相关产品推荐
相关产品推荐

