摘要
物理切割第二部分,切割多个物体,利用 Mask 切割图片!
正文
使用版本
CocosCreator 版本 2.1.3
最终效果


多切割原理
通过上一部分的教程,我们已经知道了单切割的原理,但是很显然,KUOKUO 的第一篇博客显得不通用,只能切割一个物体。那么如何切割多个物体呢?
- 第一步:分类,将不同碰撞体的点放入一个数组
- 第二步:排除,将同碰撞体内部的点干掉
- 第三步:排序,按照顺序两两分组,两两切割
第一步和第二步放在一起做,其中排除碰撞体内部的点很简单,两个结果合并,找同一点!
const result1 = cc.director.getPhysicsManager().rayCast(point1, point2, cc.RayCastType.All); const result2 = cc.director.getPhysicsManager().rayCast(point2, point1, cc.RayCastType.All);
result2.forEach(r => { r.fraction = 1 - r.fraction; });
const results = result1.concat(result2); cc.log(results);
let pairs = []; for (let i = 0; i < results.length; i++) { let find = false; let result = results[i]; for (let j = 0; j < pairs.length; j++) { let pair = pairs[j]; if (pair[0] && result.collider === pair[0].collider) { find = true; let r = pair.find((r) => { return r.point.sub(result.point).magSqr() <= 5; }); if (r) { pair.splice(pair.indexOf(r), 1); } else { pair.push(result); } break; } } if (!find) { pairs.push([result]); } } cc.log(pairs);
|
这样我们就获得了一个数组,这个数组里每个数组都是一个碰撞体的点,再将每个碰撞体内部点,两两切割。
for (let i = 0; i < pairs.length; i++) { let pair = pairs[i]; if (pair.length < 2) { continue; } pair = pair.sort((a, b) => { if (a.fraction > b.fraction) { return 1; } else if (a.fraction < b.fraction) { return -1; } return 0; }); cc.log(pair) let splitResults = []; for (let j = 0; j < pair.length - 1; j+=2) { let r1 = pair[j]; let r2 = pair[j+1]; if (r1 && r2) { this.split(r1.collider, r1.point, r2.point, splitResults); } } if (splitResults.length <= 0) { continue; } let collider = pair[0].collider; let maxPointsResult; for (let j = 0; j < splitResults.length; j++) { let splitResult = splitResults[j]; for (let k = 0; k < splitResult.length; k++) { if (typeof splitResult[k] === 'number') { splitResult[k] = collider.points[splitResult[k]]; } } if (!maxPointsResult || splitResult.length > maxPointsResult.length) { maxPointsResult = splitResult; } } if (maxPointsResult.length < 3) { continue; } collider.points = maxPointsResult; collider.apply(); collider.node.getComponent(Item).draw(); for (let j = 0; j < splitResults.length; j++) { let splitResult = splitResults[j]; if (splitResult.length < 3) continue; if (splitResult == maxPointsResult) continue; const cloneNode = cc.instantiate(collider.node); this.gameLayer.addChild(cloneNode); const comp = cloneNode.getComponent(cc.PhysicsPolygonCollider); comp.points = splitResult; comp.apply(); cloneNode.getComponent(Item).draw(); }
}
|
this.splite 这个方法实现原理同第一部分教程,不同的地方是,比如一个凹多边形,一刀切成了三部分,我们第一次将其分成两部分后,要找下第三部分的点在哪个部分,然后插入点,分割。
split (collider, point1, point2, splitResults) { let body = collider.body; let points = collider.points; let localPoint1 = cc.Vec2.ZERO; let localPoint2 = cc.Vec2.ZERO; body.getLocalPoint(point1, localPoint1); body.getLocalPoint(point2, localPoint2); let newSplitResult1 = [localPoint1, localPoint2]; let newSplitResult2 = [localPoint2, localPoint1]; let index1 = undefined; let index2 = undefined; for (let i = 0; i < points.length; i++) { let p1 = points[i]; let p2 = i === points.length - 1 ? points[0] : points[i + 1]; if (this.pointInLine(localPoint1, p1, p2)) { index1 = i; } if (this.pointInLine(localPoint2, p1, p2)) { index2 = i; } if (index1 !== undefined && index2 !== undefined) { break; } } let splitResult = undefined; let indiceIndex1 = index1; let indiceIndex2 = index2; if (splitResults.length > 0) { for (let i = 0; i < splitResults.length; i++) { let indices = splitResults[i]; indiceIndex1 = indices.indexOf(index1); indiceIndex2 = indices.indexOf(index2); if (indiceIndex1 !== -1 && indiceIndex2 !== -1) { splitResult = splitResults.splice(i, 1)[0]; break; } } } if (!splitResult) { splitResult = points.map((p, i) => { return i; }); } for (let i = indiceIndex1 + 1; i !== (indiceIndex2+1); i++) { if (i >= splitResult.length) { i = 0; } let p = splitResult[i]; p = typeof p === 'number' ? points[p] : p; if (p.sub(localPoint1).magSqr() < 5 || p.sub(localPoint2).magSqr() < 5) { continue; } newSplitResult2.push(splitResult[i]); } for (let i = indiceIndex2 + 1; i !== indiceIndex1+1; i++) { if (i >= splitResult.length) { i = 0; } let p = splitResult[i]; p = typeof p === 'number' ? points[p] : p; if (p.sub(localPoint1).magSqr() < 5 || p.sub(localPoint2).magSqr() < 5) { continue; } newSplitResult1.push(splitResult[i]); } splitResults.push(newSplitResult1); splitResults.push(newSplitResult2); }
|
讲 10 句不如给出实际代码 1 句,所以 KUOKUO 给出了源码,在下方。
Mask 实现
如图,根节点加 Mask 组件,子节点为精灵图片

修改 draw 方法:
draw () { const points = this.getComponent(cc.PhysicsPolygonCollider).points; const mask = this.getComponent(cc.Mask); const ctx = mask._graphics; ctx.clear(); const len = points.length; ctx.moveTo(points[len - 1].x, points[len - 1].y); for (let i = 0; i < points.length; i++) { ctx.lineTo(points[i].x, points[i].y); } ctx.fill(); }
|
源码更新
源码还是在我的微信公众号回复关键词【物理切割】即可获得,我把第一部分的示例合并到了这里,所以打了集合包放了上去,还是回复【物理切割】即可获取最新的源码包!
结语
觉得写得不错,欢迎转载分享!点个在看吧!
O(∩_∩)O~~
源码在我的微信公众号回复关键词【物理切割】即可获得
微信公众号
