使用cocos2d-js制作游戏新手引导(二)
本文上接前我一篇博文《使用cocos2d-js制作游戏新手引导(一)》 一、定位器的实现定位器的目的是实现对场景树中的节点精确定位,获取对象实例,从而获取节点在界面中的位置、矩形大小等信息。 1.定位符规则在cocos2d中可以通过节点名字、节点tag值来表示一个节点,在js中还可以使用对象的变量名比如:this[‘_button’]来获取节点对象。 一共有三种有效方式来表示一个node节点对象,于是这里对应三种定位符号,如下:
还有为了简化定位器字符串的长度,借鉴css中的子选择器
2.定位器解析定位器字符串中只存在名字、tag、变量名、定位符,其中由定位符将名字、tag、变量名隔开。在js中最简单的就是使用String.split函数将其分开,但这里分隔符(/、#、. 、>)不止一个符号如何实现呢?之前我是自己写的一个遍历函数来解析,但感觉有些丑陋。思考之后觉得split不应该不支持多个分隔符,于是搜索了下,发现果真不出我所料splite还支持正则表达示的分隔规则,代码由n行变成1行,非常满意,越来越喜欢上了js。 > var locator = "a/b.c#1"
> locator.split(/[.,//,>,#]/g);
[ 'a','b','c','1' ]
其实分隔符是用于修饰名字、tag、变量名的,一个定位符配合一个名字,于是设计一个简单的对象,如下:
代码如下 //使用正则表达示分隔名字
var names = str.split(/[.,#]/g);
var segments = names.map(function(name) {
var index = str.indexOf(name);
var symbol = str[index - 1] || '>';
return {symbol: symbol,name: name.trim()};
});
segments中就是我们需要的东西了,而且这里我们为了编写方便或美观,在定位符与名字之间允许有空格,如:”a > b # 1” 3.定位函数实现细节有了上面定位器字符串的解析输出,定位其实是很容易的,因为cocos2d-js中已经提供了getChildByName、getWidgetByTag、seekWidgetByName、seekWidgetByTag,而对于变量定位符则更是简单,object[‘name’]即可。 /** * 定位节点 * @param locator 定位器字符串 * @param cb 回调函数 * @returns null/node 返回值 */
locateNode: function(locator,cb) {
//解析定位器字符串
var segments = this.parseLocatorString(locator);
if (_.isEmpty(segments)) {
return;
}
cc.log("定位器:" + locator);
var child,node = this._target; //this._target为检索起点节点
for (var i = 0; i < segments.length; i++) {
var item = segments[i];
switch (item.symbol) {
case '/':
child = node.getChildByName(item.name); break;
case '.':
child = node[item.name]; break;
case '>':
child = xl.UIHelper.seekNodeByName(node,item.name); break;
case '#':
child = xl.UIHelper.seekNodeByTag(node,item.name); break;
}
if (child) {
node = child
} else {
node = null;
break;
}
}
if (node) {
cb(node); //定位节点成功,回调返回结果
this._locatedNode = node;
} else {
//定位失败,等待0.1秒后重试。
this.scheduleOnce(function () {
this.locateNode(locator,cb);
},0.1);
}
return node;
}
以上代码实现了在场景树中定位检索的过程,自认代码还算清晰明了,也很简单。在代码最后一段中,当定位失败后,会启动定时器再次检索节点,这是为了解决在引导任务切换时UI界面还没有创建出来而导致定位设计的解决方法。 二、手形提示动画与坐标转换当我们在场景树中定位到节点获取到节点对象后,就可以通过节点属性获取它的位置、大小、描点等信息,从而计算出节点在屏幕上的位置。 1.节点位置与世界位置position: 我们可以通过node.getPosition()、node.setPosition()来获取和设置节点在其父节点中的位置,也可以使用属性node.x、node.y。这里需要注意的是一个节点的座标只是表示他在父节点位置,我们在大多数时候,节点是层层包含的。我们要获取一个节点在屏幕中的位置不能简单地使用xy属性。 2.获取定位节点在世界中的位置和矩形大小/** * 手形图标指向node节点 * @param node 节点对象 * @param cb 手指点击后的回调完成函数 */
pointToNode: function(node,cb) {
this.setTouchNode(null);
var pt = node.getParent().convertToWorldSpace(node.getPosition());
//设置手指图标,指定向pt位置
this.setFinger(pt);
//通过node锚点计算,矩形大小
pt.x -= node.width * node.anchorX;
pt.y -= node.height * node.anchorY;
this._touchRect = cc.rect(pt.x,pt.y,node.width,node.height);
//开启遮罩显示
this.showMask();
//保存回调函数,node节点事件完成后执行
this._callBack = cb;
},
3.手形提示动画手形提示动画非常简单,使用action动作 cc.MoveTo即可完成,只不过在这里setFighter函数我们有时传入一个point参数,有时可能传入的是一个point数组。当传入一个point数组时,希望手形精灵按照数组中的point位置一个一个的依次移动。 三、定位区遮罩显示我们获取到节点对象,世界座标位置、矩形大小这些信息,生成一个矩形遮罩非常容易。遮罩显示主要使用cocos2d中的ClippingNode来实现,关于ClippingNode相关的技术、教程、文章已经有很多了,这里就不在详细说明,等我把代码整理好后会提供开打、显示遮罩的开关已方便使用。 四、非定位区触摸事件屏蔽1.为引导层注册触摸事件关于为Node节点注册触摸事件请参考我另一篇博客《在cocos2d-js中实现自动绑定cocostudioUI控件和事件(三)》 2.在引导层TouchBegan事件中屏蔽触摸操作通常在引导过程中是不允许进行其它操作的,需要屏蔽所有UI行为,只能执行当前引导步骤规定的动作。我们通过之前的节点定位、座标转换、矩形区计算、遮罩显示一系列操作已经可以看到可操作区了。的区域。 使用cc.node的onTouchBegan事件在返回true后将触摸事件吞食掉,从而屏蔽下层事件。 onTouchBegan: function(touch) {
//触摸矩形区不存在,直接吞食事件
if (!this._touchRect) {
return true;
}
//获取触摸位置
var pt = touch.getLocation();
//检查触摸位置是否在可操作矩形区范围内
var ret = cc.rectContainsPoint(this._touchRect,pt);
if (ret && !this._touchNode) {
//隐藏手形
this._finger.setVisible(false);
//执行回调函数
this._callBack();
}
//在可操作区,不对屏蔽下层事件
return !ret;
},
五、定位区UI事件的检测当引导层,将触摸事件放入下层游戏界面时,正常情况下会触发下层UI中的控件事件,从而进行真实的游戏步骤。这时我们可以简单地认为当前引导任务被完成。 如何解决这个问题呢?如何检查下层UI已经真实进行了事件的触发? sz.UILoader.prototype._onWidgetEvent = function(sender,type) {
if (type === ccui.Widget.TOUCH_ENDED) {
//使用观察者模式,发送按钮点击事件
xl.postMessage(xl.Message.BUTTON_CLICKED,sender);
}
};
xl.postMessage封装了cc.NotificationCenter,用于向xl.Message.BUTTON_CLICKED事件观察者广播消息,参数为当前控件对象。 利于观察者模式检查UI事件被执行将引导层对象注册为xl.Message.BUTTON_CLICKED事件的观察者,一但有控件的ccui.Widget.TOUCH_ENDED事件被触发,引导层都能知道,注册代码如下:
touchNodeClicked为引导层观察者响应函数 touchNodeClicked: function(sender) {
if (this._touchNode &&
(sender === this._touchNode ||
sender.getName() === this._touchNode.getName() )) {
this.setTouchNode(null);
this._touchRect = null;
this._finger.setVisible(false);
//在此时才能执行任务回调函数,进行下一个任务的开始
this._callBack();
}
},
到此关于UI定位、提示动画、事件屏蔽与检查的所有细节已经全部完成。 (未完待继) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |