減少多邊形計算!畫餅分之~
效果預覽
回顧
在 物理挖洞之鏈條!實現!(含視頻講解) 中介紹了用 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
開發"物理挖洞之分塊!"
的技術分享。如果對你有點幫助,歡迎分享給身邊的朋友。
天下事有難易乎?爲之,則難者亦易矣;不爲,則易者亦難矣。人之爲學有難易乎?學之,則難者亦易矣;不學,則易者亦難矣。 --《爲學》