Cocos多边形点击和解数独
前言:本文记录了Cocos多边形点击的两种实现方式,和解数独过程中的优化和思考。
01
Cocos多边形点击
背景:正在用cocos做一个七巧板拼图小游戏,成品效果如下~
由于cocos提供的节点点击事件只支持矩形,这种模式下七巧板中的三角形实际点击区域是个矩形,表现就是视觉上没有点到三角形却响应了点击事件,严重影响体验,所以需要优化点击区域的判断
我总结了下面两种方法:
方法1:像素透明度判断
观察三角形图片发现:图片分为三角形显示区域和透明区域,需要响应点击的是显示区域,只需要判断出是否是透明区域即可。
判断方法如下:将点击坐标转换为图片对应像素点,获取像素点alpha值进行判断:
/**
* 检查点击位置的像素alpha值,检查阈值OPACITY
* @param x
* @param y
* @returns 是否不透明
*/
checkHit(x: number, y: number) {
let sprite = this.node.getComponent(Sprite);
if (!sprite) {
return false
}
let texture = <Texture2D>sprite.spriteFrame.texture;
// 3.x
let data = <HTMLImageElement>texture.image.data
// 2.x
// let data = texture.getHtmlElementObj();
let cvs = document.createElement("canvas");
let ctx = cvs.getContext("2d");
cvs.width = 1;
cvs.height = 1;
ctx.drawImage(data, x, y, 1, 1, 0, 0, 1, 1);
// 获取像素数据
let pxData = ctx.getImageData(0, 0, 1, 1).data;
console.log(pxData)
return pxData[3] > OPACITY;
}
方法2:使用PolygonCollider2D组件
上图是PolygonCollider2D组件根据图片透明度自动圈出的多边形顶点
通过判断点击位置是否在多边形内来判断点中,这种方法更灵活,允许开发者手动修改点击区域,适用范围更广。
多边形和点的位置关系判断有一个简单高效的方法:射线法,该算法思想是从点出发向右水平做一条射线,计算该射线与多边形的边的相交点个数,当点不在多边形边上时,如果是奇数,那么点就一定在多边形内部,否则,在外部。最重要的是这个算法也适用于凹多边形。
/**
* 检测点击位置是否在多边形内
* @param event 点击事件
* @returns
*/
checkHitPoly(event:EventTouch) {
let targetPos = event.touch.getUILocation();
let poly = this.node.getComponent(PolygonCollider2D)
if (!poly) {
return false;
}
let count = 0;
// 获取多边形的顶点,有序
let arrPoints = poly.worldPoints;
for (let i = 0; i < arrPoints.length; i++) {
let point1 = arrPoints[i];
let point2 = arrPoints[0];
if (i != arrPoints.length - 1) {
point2 = arrPoints[i + 1];
}
// 两点y值差包含目标点的入选
if (point1.y < targetPos.y && point2.y > targetPos.y || point1.y > targetPos.y && point2.y < targetPos.y) {
let t = (targetPos.y - point1.y) / (point2.y - point1.y);
let xt = point1.x + t * (point2.x - point1.x);
// 在边上,返回true
if (targetPos.x == xt) return true;
// 目标点向右射线与边相交,加计数
if (targetPos.x < xt) ++count;
}
}
// 相交变数为奇数,在多边形内
return count % 2 == 1
}
02
解数独
另一个小游戏是数独,数独规则很简单,在9 * 9的表格内填入1 ~ 9使得行、列、宫内没有重复数字,像这样~
这个游戏的难点是题目的来源,数独题目的质量很重要,所以我们选了专业的数独书籍,从中摘取题目。
但是有个问题,怎么检查有没有录错,即使不录错怎么检查原题目有且仅有唯一解。
人为检验不靠谱,那就写个脚本检测下吧
第一步:初始化数据
其中this.rows每一行已使用数字应该是一个二维数组,声明一个一维数组怎么存的下呢?答案是未压缩。
需要存储的数据是9行,每行9个数字是否已使用,可以使用长度为9的数组表示9行,每个元素用9位二进制表示该行9个数字的使用情况,这样二维降为一维
第二步:根据题目,填充数据
存储空格子的set this.emptys同理this.rows,用一个8位2进制表示行列,左四位表示行,右四位表示列
第三步:解题
本步骤就是深度遍历找可行解,找到一个之后可行解数量加一,由于要判断是不是唯一解所以此时并不会结束而是回溯继续找其他可行解。
解数独技巧中有一项是从缺少数字最少的行、列、宫入手,这个技巧也可以加到程序中,就是每次选取空格子的时候选择待尝试解最少的格子,这个技巧与后续加入自动讲解功能相契合。
脚本完成,跑一下400关数独可行解的数量:
结果显示有29个关卡不符合唯一解,其中有录入错误的,也有题目本身就不是唯一解的,定位题目重新录入问题解决。
解个数独用这么花里胡哨吗?直接暴力法不就解决了吗?
这个解数独的方法不是只用于查录入错误,还要用到数独小游戏的自动解题和讲解,有优化的必要性。
跑了一下暴力法,部分题目运行不通过,报JavaScript heap out of memory,更加印证了优化的必要性。
我知道你“在看”哟~