Vanilla JS伪物理引擎圆与凹边线段碰撞重叠问题求解
问题成因
- 碰撞处理与速度应用顺序颠倒:当前逻辑是先做碰撞修正、再叠加移动速度,叠加速度后的新位置未经过碰撞校验,直接进入下一帧渲染,天然就会出现穿模问题。
- 多碰撞串行处理逻辑缺陷:两条边的碰撞修正串行执行,修正第一条边碰撞后的位置,在修正第二条边碰撞时会被覆盖,很容易出现修正后再次穿透第一条边的情况,凹角场景下这个问题会被放大。
- 碰撞检测存在硬编码bug:
collide方法中判断碰撞阈值时使用了全局circle.radius,而非当前实例的this.radius,属于潜在稳定性问题。
修复方案
按照以下步骤修改代码,即可解决凹角重叠问题:
- 调整move方法的执行顺序,先叠加速度,再执行碰撞检测与修正
- 新增迭代碰撞修正逻辑,多次校验修正后的位置,避免多碰撞相互抵消的问题
- 修复碰撞检测里的硬编码问题
修改后的核心代码示例
先修正collide方法的硬编码:
collide(object) { let dx31 = this.center.x - object.start.x let dx21 = object.end.x - object.start.x let dy31 = this.center.y - object.start.y let dy21 = object.end.y - object.start.y let d = ((dx21 * dx21) + (dy21 * dy21)) if (d != 0) d = ((dx31 * dx21) + (dy31 * dy21)) / d if (d < 0.0) d = 0 if (d > 1.0) d = 1 let dx = this.center.x - (object.start.x + (dx21 * d)) let dy = this.center.y - (object.start.y + (dy21 * d)) let magnitude = Math.sqrt((dx * dx) + (dy * dy)) // 把原来的circle.radius改成this.radius if (this.radius >= magnitude) { return { collided: true, intersection: { x: this.center.x - dx, y: this.center.y - dy } } } else return { collided: false } }
再替换Circle类的move方法:
move() { // 先应用移动速度 this.center.x += this.velocity.x this.center.y += this.velocity.y // 迭代3次处理多碰撞,简单场景下足够覆盖所有冲突 for (let iter = 0; iter < 3; iter++) { let hasCollision = false // 处理第一条线碰撞 let collision = this.collide(line) if (collision.collided) { hasCollision = true // 计算修正方向 const fixVec = new Vector( this.center.x - collision.intersection.x, this.center.y - collision.intersection.y ) fixVec.normalize() // 计算重叠深度,直接修正位置 const overlap = this.radius - Math.hypot(this.center.x - collision.intersection.x, this.center.y - collision.intersection.y) this.center.x += fixVec.x * overlap this.center.y += fixVec.y * overlap } // 处理第二条线碰撞 collision = this.collide(line2) if (collision.collided) { hasCollision = true const fixVec = new Vector( this.center.x - collision.intersection.x, this.center.y - collision.intersection.y ) fixVec.normalize() const overlap = this.radius - Math.hypot(this.center.x - collision.intersection.x, this.center.y - collision.intersection.y) this.center.x += fixVec.x * overlap this.center.y += fixVec.y * overlap } // 本轮没有碰撞,提前退出迭代 if (!hasCollision) break } }
修改完成后按住D键进入凹角位置,不会再出现同时穿透两条边的问题。
内容的提问来源于stack exchange,提问作者0blivio
相关产品推荐
相关产品推荐

