使用Cocos2d-JS制做游戏新手引导(四)应用篇

上一篇讲解实现引导的组成模块及整个引导流程,并给出整个引导的源码及演示代码。本文来看看怎么应用。node

 

sz.Guide引导库已经能够简单地工做了,但离真实的游戏项目、使用场景时还须要本身作一些事情。git


进度读取与保存
github

sz.Guide默认对进度的读取和保存,是记录在localStorage中的,请看以下代码:缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 读取进度
*/ loadProgress: function() {
     //获取localStorage对象,同时兼容jsb
     var localStorage = localStorage || cc.sys.localStorage;     //sz.GuideIndexName为一字符串常量作为key,读取进度,不存在时进度为0
     this ._index = parseInt(localStorage.getItem(sz.GuideIndexName)) || 0;
}, /**
  * 保存进度
  * @param isForward 进度是否前进
  * @param cb        保存完的回调
  */ saveProgress: function(isForward, cb) {
     var localStorage = localStorage || cc.sys.localStorage;
     localStorage.setItem(sz.GuideIndexName, isForward ? ++ this ._index :  this ._index + 1);     if  (cb) {
         cb();
     }
}

_index便是进度的记录器,又是任务队列的索引下标,所以进度保存有两种状况: 服务器

1.当任务完成(任务中的步骤都解决掉时),使用++this._index保存进度,并修改任务索引,进入下一个任务。 网络

2.做为保存进度的步骤时, 使用this._index + 1保存,并不修改当前任务进度,但游戏重启后,这这任务将会跳过。框架

有人可能会问saveProgress函数的cb回调参数是什么用了?请看下面的使用场景。异步


自定义进度的读取与保存
async

由于sz.Guide只简单实现了本地保存,对于如今大多数网络游戏来讲并不适合,因此你须要根据本身的项目状况来扩展sz.Guide,这里简单说明几种方法:ide

  1. 修改sz.Guide的源码来适应你的项目,但不推荐这种作法,由于sz.Guide还会持续改进、修改BUG。

  2. 写两个新函数来覆盖sz.GuideLayer上的loadProgress、saveProgress方法。 此方法能够,但不够完美,若是一个项目中有多个引导实例时怎么办呢?

  3. 继承sz.GuideLayer生成一个子类,重写loadProgress、saveProgress方法,比较推荐使用这个方法。

如下是我在项目中具体使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
xl.MyGuideLayer = sz.GuideLayer.extend({     //保存进度到服务器上
     saveProgress: function(isForward, cb) {
         //生成当前进度
         var index = isForward ? ++ this ._index :  this ._index + 1;         //经过网络服务器NetClient发送进度,服务器作保存
         NetClient.send(ActionCode.SAVE_NEW_PLAYER_GUIDE_STEP, index, function(isSucc) {
             //当接收到保存成功的服务器响应后,执行cb回调函数 
             if  (isSucc && cb) {
                 cb()
             }
         })  
     },
 
     loadProgress: function() {
         //缓存对象上获取玩家对象,并读取新手引导步骤id
         this ._index = CacheManager.getPlayer().newPlayerGuideStep || 0;
     },  
});

这时应该能明白saveProgress函数的cb函数的意义了吧!由于将进度保存到网络时,绝大多数是异步操做,当服务器真实保存成功能,才能继续。所以cb函数就是在通知引导框架,进度保存完毕了,能够进行下一个任务或步骤了。


步骤对象上的事件函数

为了使用引导配置适应更多的需求,在步骤对象上目前能够配置三个事件函数,分别为:

  • onEnter 当步骤将要开始时 

  • onLocateNode 当定位到节点时(须要配合定位器和相应指令时) 

  • onExit 当步骤结束时

确定有不少人看到个人演示程序,发现下面这个BUG:

1.jpg

这是bug的缘由是,表示灯火的粒子对象,默认没有高、宽,锚点为0,高、宽是在代码启动时设置上去的。 手型图标默认指向的位置是 node.getPosition(),也就是锚点位置。

这确实是一个bug,在不修改sz.Guide源码的状况下,咱们使用步骤配置来解决这个问题

onLocateNode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1: [
       {
           log "关闭第一盏灯" ,
           command: sz.GuideCommand.GC_FINGER_HINT,
           locator: "_fire1" ,           //onLocateNode,当定位到_fire1节点时响应些函数
           onLocateNode: function(node) {
               //node为'_fire1'节点对象
               var pt = node.getPosition();
               pt = node.getParent().convertToWorldSpace(pt);
               pt.x += node.width / 2;
               pt.y += node.height / 2;               //this为sz.GuideLayer对象实例,调用_fingerToPoint函数指向新的位置
               this ._fingerToPoint(pt,  true );
           }
       },
       ...

从新运行代码,效果以下: 

2.jpg

步骤中事件函数的this上下文为sz.GuideLayer对象实例,你能够在这里方便调用sz.GuideLayer上的方法,作一些事情。这里就使用了sz.Guide._fingerToPoint方法修正手指的位置。

可是这里也有问题,能够从遮罩区看出,定位矩形并无把灯火所有包裹住,在体验上不好。 咱们从新再改进一次配置onLocateNode函数:

1
2
3
4
5
6
7
onLocateNode: function(node) {
     //修改_touchRect触摸矩形的起点
     this ._touchRect.x -= node.width / 2;     this ._touchRect.y -= node.height / 2;     //计算矩形中心位置
     var point = cc.p( this ._touchRect.x + node.width / 2,  this ._touchRect.y + node.height / 2);     //刷新遮罩显示
     this .showMask( true );     //指向新的位置
     this ._fingerToPoint(point,  true );
     }

再次运行,效果以下: 

3.jpg

onEnter&onExit

onEnter与onExit从名字上就应该很好理解,是由步骤处理开始前和处理完成后触发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//步骤开始_processTasks: function() {
     ...     //一个step
     var stepHandle = function(step, callback) {
         self._curStepConfig = step;
         async.series({             //步骤开始
             stepBegin: function(cb) {
                 self._guideLayer._setLocateNode(null);                 if  (step.onEnter) {                     //执行步骤对象上的onEnert函数,注意cb函数参数
                     step.onEnter.call(self._guideLayer, cb);
                 else  {
                     cb();
                 }
             },             //步骤处理
             stepProcess: function(cb) {
                 if  (step.delayTime) {
                     self._guideLayer.scheduleOnce(function() {
                         self._processStep(step, cb);
                     }, step.delayTime);
                 else  {
                     self._processStep(step, cb);
                 }
             },             //步骤完毕
             stepEnd: function() {
                 if  (step.onExit) {                     //步骤完毕,退出时执行onExit方法,注意第二个callback参数
                     step.onExit.call(self._guideLayer, callback);
                 else  {
                     callback();
                 }
             }
         });
     };
 
     ...
}

onEnter、onExit事件函数都有一个cb回调函数的参数,表示事件完成后的通知。 

使用场景常会出如今onEnter时播放一个动画或显示一个临时窗口, 须要动画完成后执行步骤命令。 这都是一个异步过程,因此须要招待一次cb()操做才能让步骤向下执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
1:[
{
     ...     //在步骤开始时,播放一个动画
     onEnter: function(cb) {
         var label =  new  cc.LabelTTF( "关闭第一盏灯" "宋体" , 48);        var pt = cc.p( this .width / 2,  this .height / 2);
         label.setPosition(pt);         this .addChild(label);        var faceOut = cc.fadeOut(3);        var call = cc.callFunc(function() {
             label.removeFromParent();
             cb();  //当执行cb函数时才进入指令处理
         },  this );
         label.runAction(cc.sequence(faceOut, call))
     }
     ...
}


引导配置中的参数功能

1
2
3
4
5
6
7
8
9
var guideConfig = {     //编写具体引导任务
     tasks: {
         ...
     }     //定位器搜索节点的间隔时间
     locateNodeDurationTime: 0.1,     //手型提示图片资源路径
     fingerImage:  'res/finger.png' ,     //常规事件响应的事件类型:0=touchBegan,1=touchMoved,2=touchEnded
     eventType: 2,  //表示在touchEnded中检查事件是否完成
     //是否显示遮罩
     isShowMask:  true   };

这里解释下这些参数的功能可能的使用场景:

locateNodeDurationTime

1
locateNodeDurationTime: 0.1  //定位器搜索节点的间隔时间

有时在引导步骤中定位一个节点时,这个节点并未建立在当前场景的渲染树中,在第一次定位节点时并无找到节点对象。所以须要一个持续的节点定位的操做,操做的间隔时间由此参数控制。

fingerImage

1
fingerImage: ‘res/finger.png’  //手型提示图片资源路径

fingerImage很是简单,就不作过多解释了。

eventType

1
eventType: 2  //表示在touchEnded中检查事件是否完成

前面几篇文章中介绍了,如何检查定位节点的事件函数已经被执行。以前的讲解中说到通常都是在Widget控件的touchEnded中来处理事件函数。这样的设定不够灵活,万一有时须要在touchBegan时呢? 

eventType:2为引导的全局配置,若是某一个步骤定位节点的事件处理函数放在touchBegan时能够以下处理:

1
2
3
4
5
6
7
... //一个任务步骤对象{
     log : '点击home' ,
     command: sz.GuideCommand.GC_FINGER_HINT,
     locator: "_btnHome" ,
     eventType:0   //"_btnHome"控件的事件函数为touchBegan}
...
eventType: 2

在演示代码中你能够发现 _onBtnHomeTouchBegan: function(){…}函数,因此这里上步骤中的事件检测须要在eventType:0

isShowMask

1
isShowMask:  true  //是否显示遮罩

此开关方便开始遮罩,特别是在调试时,能够方便看到咱们的触摸矩形区大小。 

并且在单个任务步骤中也能够临时开打或关闭遮罩的显示:

1
2
3
4
5
{
      log "点亮第二盏灯" ,
      command: sz.GuideCommand.GC_FINGER_HINT,
      locator: "_fire2" ,
      showMask:  true  //强制打开当前步骤的遮罩显示},

上面总结了sz.Guide引导库的基本功能的使用,能够经过配置、事件函数灵活实现特殊的引导需求。

 

补充

delayTime

步骤对象上还有一个隐藏的属性为delayTime,值为一个Number,它是间于步骤的onEnter事件与步骤处理操做之间的延时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
async.series({
       //步骤开始
       stepBegin: function(cb) {
           ... 
       },
       //步骤处理
       stepProcess: function(cb) {
           //若是配置有delayTime属性,使用scheduleOnce推迟步骤处理
           if  (step.delayTime) {
               self._guideLayer.scheduleOnce(function() {
                   self._processStep(step, cb);
               }, step.delayTime);
           else  {
               self._processStep(step, cb);
           }
       },
     //步骤完毕
     stepEnd: function() {
         ...
     }

 

我主要的使用场景是这样的状况:

一个UI界面中有一个按钮button,建立时在A位置,一个子类继承成了他,修改成B位置。若是当即定位button节点,所指向的位置并非按钮的最终位置,这里使用delayTime能够轻松解决此问题。

 

指令扩展计划

sz.Guide内部定义了四个指令,其中实现了三个:

1
2
3
4
5
6
sz.GuideCommand = {
     GC_NULL: undefined,  //空指令
     GC_SET_PROPERTY: 1,  //设置属性
     GC_FINGER_HINT: 2,   //手型提示
     GC_SAVE_PROGRESS: 3  //保存进度
};

对于一个上线的游戏项目来讲,估计这三个指令是远远不够的。咱们也能经过步骤对象上的onEnter和onExite事件来丰富一些操做,但须要编写较多的代码,且代码是为一个特定的操做而作的,不可以很好的复用。好比在步骤处理开始前,先滚动TableView。

 

目前sz.Guide的指令是简单的用switch后分别调用不一样的函数实现的。

1
2
3
4
5
6
7
8
9
10
switch  (step.command) {
     case  sz.GuideCommand.GC_SET_PROPERTY:
         ...; break ;
     case  sz.GuideCommand.GC_FINGER_HINT:
         ...; break ;
     case  sz.GuideCommand.GC_SAVE_PROGRESS:
         ...; break ;
     default :
         cc. log ( "guide command is not define" );  
}

本人计划将指令的实现独立于引导框架,能够灵活的向引导框架注册指令,你能够编写本身的指令操做。

 

源码地址:点此下载

本篇代码演示:git checkout step1

相关文章
相关标签/搜索