You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

大质量立方体碰撞模拟穿模问题技术求助

大质量立方体碰撞模拟穿模问题技术求助

我做了一个还原YouTube上物理实验的网页——通过大小立方体碰撞来计算圆周率位数的模拟。现在功能基本能用,但遇到个头疼的问题:当输入的大立方体质量超过2-3千的时候,大立方体在墙和小立方体之间多次碰撞后,会直接“穿模”,甚至快跑到屏幕外面去了。我试着微调了碰撞公式,但完全没解决问题,求大家帮忙看看!

问题具体表现

当大立方体质量设置为2000以上时,模拟运行一段时间后:

  • 大立方体本该和小立方体/墙碰撞的位置,直接穿透过去
  • 后续大立方体甚至会往远离墙的屏幕右侧移动,完全违背预期的物理碰撞逻辑

完整代码

我把HTML、CSS和JS代码整理好了,方便大家复现问题:

HTML结构

<div id="menu">
  <h1 style="margin-bottom: -20px;">Physics simulation!</h1>
  <p style="font-weight: 400; font-size: 28px; margin-bottom: 5px;">Calculate the digits of PI with a physics simulation!</p>
  <p style="font-size:24px">[!Choose the mass of the Big Cube!]</p>
  <input class="inp" type='number' value="" min="1" max="1000000000000" style="width: 200px; height: 24px; font-size: 24px" placeholder="kg" id="massInput">
  <p style="font-size: 0.8em;">*Please dont put the number over 1 trillion!</p>
  <button id="start">Start</button>
  <button id="about">About</button>
</div>
<div id="simulation" style="display: none;">
  <button id="quit">Quit</button>
  <div id="counter">Collisions: 0</div>
  <canvas id="canvas" width="800" height="200"></canvas>
</div>

CSS样式

body {
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 0;
  background-color: black;
  color: white;
}

#menu {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  margin-top: 100px;
  gap: 16px;
  background-image: url('/images/TV1NQ_rZeMY.jpg');
  background-size: 100% 100%;
  background-position: center;
  background-repeat: no-repeat;
}

h1 {
  font-size: 120px;
  margin-bottom: 10px;
  margin-top: 10px
}

.inp {
  border: solid 2px white;
  background-color: transparent;
  color: white;
  border-radius: 60px;
  outline: none;
  padding: 5px;
}

#start, #about {
  font-size: 24px;
  padding: 3px;
  border: none;
  border-radius: 60px;
  cursor: pointer;
  background-color: transparent;
  color: white;
  transition: 0.5s;
}

#start:hover, #about:hover {
  scale: 1.5;
  transition: 0.5s;
}

#quit {
  margin: 10px 10px 0 0;
  font-size: 24px;
  padding: 3px;
  border: none;
  border-radius: 60px;
  cursor: pointer;
  background-color: transparent;
  color: white;
}

#simulation {
  display: none;
}

JavaScript逻辑

let m1 = 1 // 小立方体质量
let m2 = 10 // 默认大立方体质量
const massa = document.getElementById('massInput') // 质量输入框
const startButton = document.getElementById('start') // 开始按钮
const menu = document.getElementById('menu') // 菜单容器
const sim = document.getElementById('simulation') // 模拟容器
let counter = document.getElementById('counter') // 碰撞计数器
const quitButton = document.getElementById('quit'); // 退出按钮
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

let small, big, bigmass, size, color
let collisions = 0
let animationId

// 退出模拟回到菜单
quitButton.addEventListener('click', function() {
  sim.style.display = 'none';
  menu.style.display = 'flex';
  cancelAnimationFrame(animationId);
})

// 开始模拟逻辑
startButton.addEventListener('click', function() {
  bigmass = parseFloat(massa.value)
  // 输入合法性校验
  if (isNaN(bigmass) || bigmass < 1 || bigmass > 1000000000000) {
    alert('Please enter a valid mass (from 1 to 1 trillion)')
    return;
  }
  // 根据质量设置大立方体的尺寸和颜色
  if (bigmass < 10) {
    size = 30;
    color = 'orange';
  } else if (bigmass >= 10 && bigmass < 100) {
    size = 60;
    color = 'blue';
  } else if (bigmass >= 100 && bigmass < 1000000) {
    size = 90;
    color = 'grey';
  } else if (bigmass >= 1000000 && bigmass <= 1000000000000) {
    size = 120;
    color = 'grey';
  }
  // 切换显示状态,初始化模拟
  menu.style.display = 'none'
  sim.style.display = 'block'
  initSimulation(bigmass)
  animationId = requestAnimationFrame(loop)
})

// 初始化模拟对象
function initSimulation(bigmass) {
  collisions = 0
  small = {
    x: 80,
    y: canvas.height - 30,
    size: 30,
    mass: m1,
    vx: 0,
    color: 'orange'
  }
  big = {
    x: 225,
    y: canvas.height - size,
    size: size,
    mass: bigmass,
    vx: -1,
    color: color
  }
}

// 物理更新逻辑
function updatePhysics() {
  // 更新位置
  small.x += small.vx
  big.x += big.vx

  // 小立方体撞墙反弹
  if (small.x <= 0) {
    small.x = 0
    small.vx *= -1
    collisions++
  }

  // 大小立方体碰撞弹性碰撞计算
  if (small.x + small.size >= big.x) {
    let u1 = small.vx, u2 = big.vx
    // 弹性碰撞速度公式
    small.vx = ((small.mass - big.mass) * u1 + 2 * big.mass * u2) / (small.mass + big.mass);
    big.vx = ((big.mass - small.mass) * u2 + 2 * small.mass * u1) / (small.mass + big.mass);
    collisions++
  }
}

// 画布绘制逻辑
function draw() {
  // 清空画布
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  // 绘制地面
  ctx.fillStyle = "white"
  ctx.fillRect(0, canvas.height - 2, canvas.width, 2)
  // 绘制墙
  ctx.fillStyle = "white"
  ctx.fillRect(0, 0, 2, canvas.height)
  // 绘制小立方体
  ctx.fillStyle = small.color
  ctx.fillRect(small.x, small.y, small.size, small.size)
  // 绘制大立方体
  ctx.fillStyle = big.color
  ctx.fillRect(big.x, big.y, big.size, big.size)
  // 更新碰撞计数显示
  counter.innerText = `Collisions: ${collisions}`
}

// 动画循环
function loop() {
  updatePhysics()
  draw()
  animationId = requestAnimationFrame(loop)
}

我自己怀疑过是不是浮点数精度的问题?毕竟大质量下速度计算的数值会很小或者很大?但不确定怎么验证和修复,希望有大佬能指点一下!

内容来源于stack exchange

火山引擎 最新活动