二维非对心弹性碰撞的算法
该算法适合常见的二维完全弹性碰撞的场合,支持对心碰撞(正碰)和非对心碰撞(斜碰),不考虑碰撞过程中的机械能损耗,不考虑物体表面摩擦以及恢复系数。
/*
* this是自身对象,sp是碰撞的对象
* this.m 质量
* this.r 半径
* this.vx 水平速度
* this.vy 竖直速度
* 为了便于理解,代码未经优化!
*/
collide(sp) {
if (this.isCollideWith(sp)) {
// 利用弹性碰撞公式计算水平、竖直方向上的速度分量 let vx = this.vx let vy = this.vy this.vx = ((this.m - sp.m) * this.vx + 2 * sp.m * sp.vx) / (this.m + sp.m) this.vy = ((this.m - sp.m) * this.vy + 2 * sp.m * sp.vy) / (this.m + sp.m) sp.vx = (2 * this.m * vx + (sp.m - this.m) * sp.vx) / (this.m + sp.m) sp.vy = (2 * this.m * vy + (sp.m - this.m) * sp.vy) / (this.m + sp.m)
// 关键!在速度突变的情况下(例如碰壁反弹,或者另一物体被挤压以至于无法移动),必须防止位置重叠的情况出现
// nextXPos和nextYPos是提前判断二者下一帧的位置。若下一帧位置重叠,则反弹。 let ax = this.nextXPos() let ay = this.nextYPos() let bx = sp.nextXPos() let by = sp.nextYPos() if (Math.sqrt((ax - bx) ** 2 + (ay - by) ** 2) < (this.r + sp.r)) { let agl = 0 // 反弹方向相对屏幕坐标系的角度 if (this.x != sp.x) agl = Math.atan((this.y - sp.y) / (this.x - sp.x))
// 反弹的速度的方向需根据二者位置来确定 let v = Math.sqrt(this.vx ** 2 + this.vy ** 2) this.vx = v * Math.cos(agl) * (this.x > sp.x ? 1 : -1) this.vy = v * Math.sin(agl) * (this.y > sp.y ? 1 : -1) v = Math.sqrt(sp.vx ** 2 + sp.vy ** 2) sp.vx = v * Math.cos(agl) * (sp.x > this.x ? 1 : -1) sp.vy = v * Math.sin(agl) * (sp.y > this.y ? 1 : -1)} }