前置条件
- 工程和代码均使用 physics-example-master
- 在工程里面的 cutting-object.fire 场景文件里,添加测试节点,绑定 PhysicsPolygonCollider 并编辑 points ,下图为编辑完毕后的效果:
- 运行预览,鼠标滑动使切割线经过 C 点附近,松开鼠标,出错,具体操作步骤见下方 gif 图
操作步骤
出错堆栈
CCPolygonSeparator.js:337 Uncaught TypeError: Cannot read property 'x' of undefined
at Area (CCPolygonSeparator.js:337)
at Right (CCPolygonSeparator.js:219)
at Reflex (CCPolygonSeparator.js:207)
at ConvexPartition (CCPolygonSeparator.js:77)
at ConvexPartition (CCPolygonSeparator.js:154)
at Object.ConvexPartition (CCPolygonSeparator.js:155)
at cc_PhysicsPolygonCollider._createShape (CCPhysicsPolygonCollider.js:56)
at cc_PhysicsPolygonCollider.__init (CCPhysicsCollider.js:172)
at CCClass.pushDelayEvent (CCPhysicsManager.js:180)
at cc_PhysicsPolygonCollider._init (CCPhysicsCollider.js:153)
一些线索
- 这个问题很容易复现,只要保证切割线经过 C 点的附近,而且是在刚体的外部(注意上图 C 点附近是 2 个挨得很近小白点)
- 浏览器断点到 CCPolygonSeparator.js:337,设置条件为 a===undefined
- 复现问题,步骤 2 条件断点触发了,可以定位到用户代码是 cutting-boject.js -> onTouchEnd -> collider.apply() 行
- 尝试编辑器里新建物理多边形 collider 节点,将 maxPointsResult 数据作为 points,如下图,依然看不出问题,但中间有个可疑点
- 放大可疑点
- 可以看到 C 点在切割线右方,而其他点在切割线左边,这应该是问题所在了。
进一步挖掘
仔细研读 cutting-object.js 源码,发现 maxPointsResult 的数据与下面这段代码有很大关系
这段的意图应该是说,一个碰撞器可能包含了多个 fixture,所以一次 raycast 过程可能会穿过内部 fixture,我们需要将他们从 result 数组里移除。而这里判断是否为内部 fixture 的关键是 r.point.sub(result.point).magSqr() <= POINT_SQR_EPSILON,如果 2 个点的距离小于 POINT_SQR_EPSILON 常亮 5,即认为是内部 fixture 碰撞点,需要移除掉。
结合操作步骤,猜测我们切割的线其实是穿过多边形的 BC/CD 边的,而因为两个点太靠近,导致判断 POINT_SQR_EPSILON 时被认为是内部 fixture 碰撞点,导致后续 maxPointsResult 的错误。
我的问题
以上是我对这个问题的理解和探索,我尝试将 POINT_SQR_EPSILON 缩小为 0.01,好像能减小问题出现的概率,但还是会出现,即无法根本解决。
研究了几天,实在不知如何解决,还请各位小伙伴和大神们支招