编辑器里
Regenerate Points
功能怎么实现?节点动态加载了spriteFrame
,怎么重新获取碰撞组件多边形顶点数组points
?
背景
在 Cocos Creator
编辑中,多边形碰撞组件中有一个 Regenerate Points
的功能,这个功能可以根据组件依附的节点上的 Sprite
组件的贴图的像素点来自动生成相应轮廓的顶点。Threshold
指明生成贴图轮廓顶点间的最小距离,值越大则生成的点越少。
白玉无冰源于兴趣,对其中的实现做一些研究。最终研究成果如下文所示。
实现
编辑器中的实现主要以下几步:
读取图片所有的像素点数据
计算围成轮廓的像素点
计算顶点(处理
Threshold
)对结果进行坐标转换
以下是在 Canvas
中计算轮廓(红边)和和计算顶点(蓝点)的效果。
获取像素点数据
编辑中读取像素数据用了sharp这个库去读取。
白玉无冰这里采用 Canvas
获取图片像素数据。
const img = new Image();
img.crossOrigin = 'anonymous';
img.src = './白玉无冰.png';
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
img.onload = function () {
main();
};
const main = function () {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0);
const canvasImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = canvasImageData.data;
// data 就是像素数据,从左往右,从上往下的像素数据
}
计算围成轮廓的像素点
核心思路是参考 Marching Squares
算法:
先从上到下,从左到右的顺序,找到第一个不透明的点。
根据当前点的 左上/左边/上边/当前点 四个点的透明值计算一个值,根据这个值判断往哪个方向移动
移动后继续计算,继续移动,直到回到第一个点
简单来说就是,找一个点,然后逆时针环绕一圈。
先看看如何根据当前点周围的透明度值计算一个值,这里巧妙地使用了二进制相加,可以通过和判断不同情况。
/*
checking the 2x2 pixel grid, assigning these values to each pixel, if not transparent
+---+---+
| 1 | 2 |
+---+---+
| 4 | 8 | <- current pixel (curx,cury)
+---+---+
*/
// 以下为伪代码
let state = 0;
let tl = {x:(x-1), y:(y-1)}
state += (containsPoint(tl)&&getAlphaByPos(tl)>0)? 1:0;
let tr = {x:(x-1), y:(y)}
state += (containsPoint(tr)&&getAlphaByPos(tr)>0)? 2:0;
let bl = {x:(x-1), y:(y)}
state += (containsPoint(bl)&&getAlphaByPos(bl)>0)? 4:0;
let br = {x:(x), y:(y)}
state += (containsPoint(br)&&getAlphaByPos(br)>0)? 8:0;
根据不同的值,走不同的方向。
这里