大质量立方体碰撞模拟穿模问题技术求助
大质量立方体碰撞模拟穿模问题技术求助
我做了一个还原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




