共计 2779 个字符,预计需要花费 7 分钟才能阅读完成。
减少多边形计算!画饼分之~
效果预览
回顾
在 物理挖洞之链条!实现!(含视频讲解) 中介绍了用 PolyBool
和链条组件 (cc.PhysicsChainCollider
) 实现物理挖洞的方法。
虽说这种方案可能不是最佳方案,但里面有一种 evenodd
的思想,觉得不错的。
在 物理挖洞之链条!优化!(含视频讲解) 中介绍了几个优化的地方。
其中,单位化的思想和平滑移动的思想在后续一直被使用。
不过,多边形链条组件有一个问题,容易穿透。
接着,经过多次查找和分析,在物理挖洞之多边形!实现!中介绍用多边形碰撞组件 (cc.PhysicsPolygonCollider
) 去实现物理挖洞。
整体思路是,先用 Clipper
去计算多边形 (效率比 PolyBool
高),接着用 poly2tri
将多边形分割成多个三角形,最后用多边形刚体填充。
但是呢,poly2tri
限制比较多,物理挖洞之多边形!填坑!中介绍了填坑之路。
并利用 mask
的 graphics
实现好看的纹理。
当然,还有群内小伙伴们讨论分享的 3D 效果,在上面的基础上,修改了一个物理挖洞之 3D 效果,感谢各位小伙伴的分享!
强烈建议按顺序阅读上面几篇文章,有助于更好的理解这篇的文章哦!
实现原理
整体思路是对区域进行分块,点击的时候判断是对哪个区域块有操作,再对这些区域块进行多边形计算,最后再绘制所有的多边形。
这里与物理挖洞之多边形!实现!中的区别是少了一步 poly2tri
,这是怎么做到的?
首先得明白一点,之前使用 poly2tri
是因为会有内多边形出现。
所以,在分块的时候,只要满足分块的尺寸小于挖洞的尺寸,这样就不会出现内多边形了。
如何判断点击的是哪个区域呢?
在初始化的时候,用一个 2D 矩形 (cc.Rect
) 数组记录每一个分块的信息。
private _rects: cc.Rect[] = [];
当点击的时候会生成一个多边形 (参考物理挖洞之链条!优化!中的触摸平滑连续) 数据。
对于这个多边形的每个点,计算出坐标 x
和 y
的最大值和最小值。
然后就可以算出这个的多边形的矩形(aabb (Axis-Aligned Bounding Box)
)。
let xMin = Number.MAX_SAFE_INTEGER, xMax = Number.MIN_SAFE_INTEGER, yMin = Number.MAX_SAFE_INTEGER, yMax = Number.MIN_SAFE_INTEGER;
// 计算最小最大值
xMin = p.x < xMin ? p.x : xMin;
yMin = p.y < yMin ? p.y : yMin;
xMax = p.x > xMax ? p.x : xMax;
yMax = p.y > yMax ? p.y : yMax;
// 得出矩形
const rect_r = cc.Rect.fromMinMax(cc.v2(xMin, yMin), cc.v2(xMax, yMax));
再用这个矩形和初始化矩形做一次相交判断,这样就可以粗略的确定要计算的块了。
for (let index = 0; index < this._rects.length; index++) {const rect = this._rects[index];
if (rect.intersects(rect_r)) {this.polyEx.pushCommand('polyDifference', [regions, index])
}
}
多边形计算用的是 Clipper
,使用接口可以参考官网或者物理挖洞之多边形!。
// polyDifference(poly: cc.Vec2[], index: number) {
// 计算新的多边形
// https://sourceforge.net/p/jsclipper/wiki/documentation
const cpr = new ClipperLib.Clipper(ClipperLib.Clipper.ioStrictlySimple);
const subj_paths = this._polys[index];
const clip_paths = [this._convertVecArrayToClipperPath(poly)]
cpr.AddPaths(subj_paths, ClipperLib.PolyType.ptSubject, true);
cpr.AddPaths(clip_paths, ClipperLib.PolyType.ptClip, true);
const subject_fillType = ClipperLib.PolyFillType.pftEvenOdd;
const clip_fillType = ClipperLib.PolyFillType.pftEvenOdd;
const solution = new ClipperLib.Paths();
cpr.Execute(ClipperLib.ClipType.ctDifference, solution, subject_fillType, clip_fillType);
this._polys[index] = solution || [];
在所有分块计算之后,最后整体绘制多边形碰撞体和纹理。
// private draw() {ctx.clear();
for (let index = 0; index < this._polys.length; index++) {const polygons = this._polys[index];
for (let index2 = 0; index2 < polygons.length; index2++) {const polygon = polygons[index2];
let c = this._physicsPolygonColliders[_physicsPolygonColliders_count];
c.points = this._convertClipperPathToVecArray(polygon);
c.apply();
for (let index3 = 0; index3 < c.points.length; index3++) {const p = c.points[index3];
if (index3 === 0) ctx.moveTo(p.x, p.y);
else ctx.lineTo(p.x, p.y);
}
ctx.close();}
}
ctx.fill();
当然,群 (859642112
) 内小伙伴 @吴先生
也实现了这个分块,分块计算多边形同时,也进行分块绘制,欢迎加群一起讨论!
小结
生命不息,挖坑不止!
以上为白玉无冰使用 Cocos Creator v2.3.3
开发 "物理挖洞之分块!"
的技术分享。如果对你有点帮助,欢迎分享给身边的朋友。
天下事有难易乎?为之,则难者亦易矣;不为,则易者亦难矣。人之为学有难易乎?学之,则难者亦易矣;不学,则易者亦难矣。–《为学》
原文链接
完整代码(见 readme)
原创文章导航