继续继续。
上一篇地址:【Cocos Creator 实战】03 - 如何「拿起」拼图游戏的每块图片
来看看今天要做的内容:
你问我什么是吸附?我咋解释,直接看看效果吧:
推荐大家先把项目 clone 到本地,然后参考着代码来看本篇文章。
我的每篇文章会对应一个分支,大家直接看对应的分支就可以,master 对应的是最新的内容,会整合各个分支。
演示地址也是对应每篇文章独立部署的。
Creator 中的坐标系原则与笛卡尔坐标系相同,原理如下图:
即,如果面向屏幕的话,向右是 x 轴
正方向,向上是 y 轴
正方向,垂直于屏幕的是 z 轴
正方向。
结合我们上一篇文章说的 zIndex
,这回是不是理解的更好了。
那对于 Creator 中来讲,我们的坐标系可以按照下图来画:
注意几点哈:
1、z轴
是垂直于屏幕的,有点立体空间的思维,不是斜着的
2、我图上每个轴的剪头的粗细没有关系,那是我使用工具的问题,主要看方向
3、我把三个轴的交叉点放在了正中间,那他们交叉点的位置坐标就是 (0,0,0)
我们先不考虑 z 轴
,为什么 xy 轴
的 (0,0)
坐标在中间呢?
这就涉及到下一个知识点:锚点(Anchor)
。
锚点其实主要有两个作用:
1、表示用自身节点的哪个位置作为在父节点中设置位置的点
2、表示自身节点本地坐标系的初始原点位置,即子节点使用的坐标系的原点位置
是不是有点蒙圈,我们看看例子(请忽略我粗糙的绘画技巧,对灯发誓,我尽力了):
假设在一个坐标系里面,有三个节点分别是红黄蓝,他们的锚点分别如上图标注,那么他们在坐标系中的位置将会如图中所示:
锚点(0,0)
的会把自己左下角的位置作为在父节点中设置位置的点锚点(0.5,0.5)
的会把自己中心位置作为在父节点中设置位置的点锚点(1,1)
的会把自己右上角的位置作为在父节点中设置位置的点同样的,该位置(左下、中心、右上)也是作为其子节点所使用坐标系的原点,如图上所画。
明白了么,刚开始没懂没事,其实锚点不只是从0到1,任何数值都可以。
目前我们需要的比较简单,如果大家理解起来实在困难的话,就先理解一下 (0.5,0.5)
,记住所有节点默认的位置都是指其中心点的位置就可以了。
我们的项目也都是使用的默认的 (0.5,0.5)
。
我的建议也是,除非你真的明白了这个东西是干什么的,不然就用默认的就好了,一般也没必要设置。
上面说了那么多,都是为了解释下面的吸附规则。
其实设计吸附的规则有很多,你也可以根据自己的理解写一套自己的算法出来,但无论哪种算法,都离不开节点位置的计算,而节点位置的的计算又肯定离不开上面的两个知识点:坐标系、锚点。
下来来说说我设计的吸附规则:
1、首先我们能得到每张拼图的位置(Position),即每张图的正中间
2、我们把这些点扩充一下,把每张图片相邻一个图片位置的点也标注出来:
这些点目前已经显示在屏幕之外了,不用管,位置肯定在那,只是屏幕没显示下而已。
3、我们以这些点为中心,各自画一个半径为 100 的圆(图中黄色部分)
大家不要看画功啊,领会精神。
4、然后我们就可以设计,当一块拼图被拿起又放下的时候,如果他的位置(Position)落在了我们这里某一个黄色圆圈内,那我们就把他的位置自动移动到该黄色圆圈对应的红色中心点上,即吸附过去。
比如上图中右下角绿色的点表示右下角拼图的位置,他落在了其中一个黄色圈内,那他就要自动移动到黄圈中的红点位置,完成吸附。
当然,这个位置可以是写死的,就是每个图片会被吸附的位置在程序初始化的时候就指定,我们也可以做成灵活的,在放下拼图的时候动态计算所有节点的周围的位置,这样能做到拼图的位置可以随意变换,效果好一点。
毕竟我们是要打造全世界最好的拼图游戏的,体验要好。
首先,修改 item.js
文件,在拖拽完成的回调中,把当前拖拽的节点作为参数返回去:
this.node.on(cc.Node.EventType.TOUCH_END, function () {
this.opacity = 255;
self.moveEndCb = self.moveEndCb || function () {
};
self.moveEndCb(self.node); // 把当前节点作为参数返回去
}, this.node);
然后,修改 item-manager.js
文件中拖放结束的回调方法:
__moveEnd(node) {
console.info('end');
let picHeight = node.height;
let picWidth = node.width;
let nodeVec = cc.v2({x: node.position.x, y: node.position.y});
let itemManager = node.parent.getComponent('item-manager');
let conditions = [
{x: picWidth, y: 0},
{x: (-1) * picWidth, y: 0},
{x: 0, y: picHeight},
{x: 0, y: (-1) * picHeight},
];
for (let i = 0; i < itemManager.items.length; i++) {
let itemNode = itemManager.items[i].node;
let itemPos = itemNode.position;
let isMoved = false;
for (let j = 0; j < conditions.length; j++) {
let con = conditions[j];
let targetVec = cc.v2({
x: itemPos.x + con.x,
y: itemPos.y + con.y
});
let distance = targetVec.sub(nodeVec).mag();
if (distance > 100) continue; // 100即为上面图中黄色圆圈的半径
isMoved = true;
let action = cc.moveTo(0.1, targetVec);
node.runAction(action);
}
if (!isMoved) continue;
break;
}
cc.loader.loadRes('sound/drop', cc.AudioClip, function (err, clip) {
cc.audioEngine.playEffect(clip, false);
});
},
我们的吸附位置,即上面图中红点的位置是实时计算的,获取各个节点位置要先获取到 item-manager
:
let itemManager = node.parent.getComponent('item-manager');
那对应的各个节点就可以直接拿出来了:
let itemNode = itemManager.items[i].node;
conditions
表示每个拼图对应的四个点的相对位置,分别是上下左右各距离一个图片的位置。
这里我们是用过 Vec2
也就是 cc.v2
的方式来表示节点的位置的,因为这样能比较方便的计算两个节点间的距离。
let distance = targetVec.sub(nodeVec).mag();
if (distance > 100) continue;
上面的 100
就是我们黄色圆的半径,大家可以根据需要灵活改变,但是要合理哈,太小了起不到吸附效果,太大的话,嘿嘿,你可以设置成 10000
看看效果。
还有最后一个,移动节点的动画,我们通过 动作(action)
来实现:
let action = cc.moveTo(0.1, targetVec);
node.runAction(action);
这里就是表示该节点会用 0.1s
移动到(moveTo) targetVec
位置,好理解吧。
关于动作,要讲的话会有很多,这里大家先简单了解一下就好。
1、Cocos Creator 中的坐标系
2、锚点的概念
3、设计了一套吸附规则
4、简单接触了 Vec2
5、简单接触了 动作(Action)
这篇文章花了我将近3天时间,其实代码很好写,但是想把其中的原理讲清楚,还真是麻烦。
可能理解能力比较好的朋友会觉得我写的啰嗦,那没办法,照顾一下牛顿同学吧。
想想下一步,我们是不是也该设计一下胜利规则了,不然玩了半天,也都是自嗨,都不知道拼的对不对。
那么,如何判断拼图游戏已经结束(胜利)呢?
请听下回分解吧。
下一篇地址:【Cocos Creator 实战】05 - 如何判断拼图完成(胜利)