物理挖洞之分块 !Cocos Creator !

Posted by lamyoung on June 16, 2020

减少多边形计算!画饼分之~

效果预览

回顾

物理挖洞之链条!实现!(含视频讲解) 中介绍了用 PolyBool 和链条组件(cc.PhysicsChainCollider)实现物理挖洞的方法。

虽说这种方案可能不是最佳方案,但里面有一种 evenodd 的思想,觉得不错的。

物理挖洞之链条!优化!(含视频讲解) 中介绍了几个优化的地方。

其中,单位化的思想和平滑移动的思想在后续一直被使用。

不过,多边形链条组件有一个问题,容易穿透。

接着,经过多次查找和分析,在物理挖洞之多边形!实现! 中介绍用多边形碰撞组件(cc.PhysicsPolygonCollider)去实现物理挖洞。

整体思路是,先用 Clipper 去计算多边形 (效率比 PolyBool 高),接着用 poly2tri 将多边形分割成多个三角形,最后用多边形刚体填充。

但是呢,poly2tri 限制比较多,物理挖洞之多边形!填坑! 中介绍了填坑之路。

并利用 maskgraphics 实现好看的纹理。

当然,还有群内小伙伴们讨论分享的3D效果,在上面的基础上,修改了一个物理挖洞之3D效果,感谢各位小伙伴的分享!

强烈建议按顺序阅读上面几篇文章,有助于更好的理解这篇的文章哦!

实现原理

整体思路是对区域进行分块,点击的时候判断是对哪个区域块有操作,再对这些区域块进行多边形计算,最后再绘制所有的多边形。

这里与物理挖洞之多边形!实现! 中的区别是少了一步 poly2tri,这是怎么做到的?

首先得明白一点,之前使用 poly2tri 是因为会有内多边形出现。

所以,在分块的时候,只要满足分块的尺寸小于挖洞的尺寸,这样就不会出现内多边形了。

如何判断点击的是哪个区域呢?

在初始化的时候,用一个2D矩形(cc.Rect)数组记录每一个分块的信息。

private _rects: cc.Rect[] = [];

当点击的时候会生成一个多边形(参考物理挖洞之链条!优化! 中的触摸平滑连续)数据。

对于这个多边形的每个点,计算出坐标 xy 的最大值和最小值。

然后就可以算出这个的多边形的矩形(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)
原创文章导航