sz.Guide引导库已经可以简单地工作了,但离真实的游戏项目、使用场景时还需要自己做一些事情。

进度读取与保存

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

/** * 读取进度 */
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回调参数是什么用了?请看下面的使用场景。

自定义进度的读取与保存

因为sz.Guide只简单实现了本地保存,对于现在大多数网络游戏来说并不适合,所以你需要根据自己的项目情况来扩展sz.Guide,这里简单说明几种方法:

  1. 修改sz.Guide的源码来适应你的项目,但不推荐这种做法,因为sz.Guide还会持续改进、修改BUG。
  2. 写两个新函数来覆盖sz.GuideLayer上的loadProgress、saveProgress方法。 此方法可以,但不够完美,如果一个项目中有多个引导实例时怎么办呢?
  3. 继承sz.GuideLayer生成一个子类,重写loadProgress、saveProgress方法,比较推荐使用这个方法。

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

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:


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

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

onLocateNode

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);
          }
      },...

重新运行代码,效果如下:

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

但是这里也有问题,可以从遮罩区看出,定位矩形并没有把灯火全部包裹住,在体验上很差。 我们重新再改进一次配置onLocateNode函数:

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);
    }

再次运行,效果如下:

onEnter&onExit

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

//步骤开始
_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:[
{
    ...
    //在步骤开始时,播放一个动画
    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))
    }
    ...
}

引导配置中的参数功能

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

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

locateNodeDurationTime

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

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

fingerImage

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

fingerImage非常简单,就不做过多解释了。

eventType

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

前面几篇文章中介绍了,如何检查定位节点的事件函数已经被执行。之前的讲解中说到一般都是在Widget控件的touchEnded中来处理事件函数。这样的设定不够灵活,万一有时需要在touchBegan时呢?
eventType:2为引导的全局配置,如果某一个步骤定位节点的事件处理函数放在touchBegan时可以如下处理:

...
//一个任务步骤对象
{
    log:'点击home',locator:"_btnHome",eventType:0  //"_btnHome"控件的事件函数为touchBegan
}
...
eventType: 2

在演示代码中你可以发现 _onBtnHometouchBegan: function(){…}函数,所以这里上步骤中的事件检测需要在eventType:0

isShowMask

isShowMask: true //是否显示遮罩
此开关方便开始遮罩,特别是在调试时,可以方便看到我们的触摸矩形区大小。
而且在单个任务步骤中也可以临时开打或关闭遮罩的显示:

{
     log: "点亮第二盏灯",locator:"_fire2",showMask: true //强制打开当前步骤的遮罩显示
},

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

补充

delayTime

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

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内部定义了四个指令,其中实现了三个:

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

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

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

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");  
}

本人计划将指令的实现独立于引导框架,可以灵活的向引导框架注册指令,你可以编写自己的指令操作。

源码地址:https://github.com/ShawnZhang2015/Guide.git 本篇代码演示:git checkout step1

使用cocos2d-js制作游戏新的引导 (四)-应用篇的更多相关文章

  1. localStorage的过期时间设置的方法详解

    这篇文章主要介绍了localStorage的过期时间设置的方法详解的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  2. HTML5 Web缓存和运用程序缓存(cookie,session)

    这篇文章主要介绍了HTML5 Web缓存和运用程序缓存(cookie,session),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  3. html5超简单的localStorage实现记住密码的功能实现

    这篇文章主要介绍了html5超简单的localStorage实现记住密码的功能实现,非常具有实用价值,需要的朋友可以参考下

  4. 详解前端HTML5几种存储方式的总结

    本篇文章主要介绍了前端HTML5几种存储方式的总结 ,主要包括本地存储localstorage,本地存储sessionstorage,离线缓存(application cache),Web SQL,IndexedDB。有兴趣的可以了解一下。

  5. localstorage和sessionstorage使用记录(推荐)

    通过阅读各路大神对web存储locastorage和sessionstorage的用法解析,深有感触,下面小编把localstorage和sessionstorage使用记录分享到脚本之家平台,供大家参考

  6. ios – 如何使用Objective C类中的多个参数调用Swift函数?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  7. ios – 在iPad Safari上的localStorage是否保持持续?

    我已经看到网络上的意见分歧.据说,从iOS5.1开始,iPad上的HTML5内容的本地数据存储不再保证是持久的,但是在苹果当前的Safari开发者页面(https://developer.apple.com/technologies/safari/html5.html)上,建议离线持久性被保证.有没有人有任何最近的经验与这个问题,并能评论如何可靠的数据库HTML5的功能与Safari在iPad上?

  8. ios – 将两个字符串转换为一组布尔值的快速方法是什么?

    我有一个长字符串,我想转换为一个布尔值数组.而且它需要很多次,很快.我天真的尝试是这样的:但这比我想要的要慢很多.我的剖析告诉我,地图是减速的地方,但我不知道我能做多么简单.我觉得如果没有Swift’s/ObjC的开销,这样做会很快.在C中,我认为这是一个简单的循环,其中一个字节的内存与一个常量进行比较,但我不知道我应该看的是什么函数或语法.有更好的办法吗?

  9. swift中string操作

    在swift中得string操作和OC稍有不同。一些基本操作苹果文档已经有描述。但是关于index的操作则没有提到。如果想删除或者得到字串,首先需要得到String.Index这个类型和Int不同,不能直接转换,所以需要用到advance函数。比如上面的例子,就是获取最后一个字符,然后删除之。

  10. Swift enumerate函数与??操作符

    本文的Swift语言版本,1.2先看看问题把这两个数组合成一个字典期望的合成后结果我给出的答案:能解决问题,但是不够优雅,不够简洁,因为我对Swift很多global的函数掌握的不是很好,最近开发项目还是用OC。Accept的答案enumerate函数-Swift1.2遍历数组和字符串的方式,同时返回index以及数据输出遍历数组的例子输出??操作符例如理解为这个a不为nil的话返回a,否则返回b。所以,在Swift中,这样的语法都可以进行简化。上文的例子就是如果dic[date]为nil,则dic[da

随机推荐

  1. 【cocos2d-x 3.x 学习笔记】对象内存管理

    Cocos2d-x的内存管理cocos2d-x中使用的是上面的引用计数来管理内存,但是又增加了一些自己的特色。cocos2d-x中通过Ref类来实现引用计数,所有需要实现内存自动回收的类都应该继承自Ref类。下面是Ref类的定义:在cocos2d-x中创建对象通常有两种方式:这两中方式的差异可以参见我另一篇博文“对象创建方式讨论”。在cocos2d-x中提倡使用第二种方式,为了避免误用第一种方式,一般将构造函数设为protected或private。参考资料:[1]cocos2d-x高级开发教程2.3节[

  2. 利用cocos2dx 3.2开发消灭星星六如何在cocos2dx中显示中文

    由于编码的不同,在cocos2dx中的Label控件中如果放入中文字,往往会出现乱码。为了方便使用,我把这个从文档中获取中文字的方法放在一个头文件里面Chinese.h这里的tex_vec是cocos2dx提供的一个保存文档内容的一个容器。这里给出ChineseWords,xml的格式再看看ChineseWord的实现Chinese.cpp就这样,以后在需要用到中文字的地方,就先include这个头文件然后调用ChineseWord函数,获取一串中文字符串。

  3. 利用cocos2dx 3.2开发消灭星星七关于星星的算法

    在前面,我们已经在GameLayer中利用随机数初始化了一个StarMatrix,如果还不知道怎么创建星星矩阵请回去看看而且我们也讲了整个游戏的触摸事件的派发了。

  4. cocos2dx3.x 新手打包APK注意事项!

    这个在编译的时候就可以发现了比较好弄这只是我遇到的,其他的以后遇到再补充吧。。。以前被这两个问题坑了好久

  5. 利用cocos2dx 3.2开发消灭星星八游戏的结束判断与数据控制

    如果你看完之前的,那么你基本已经拥有一个消灭星星游戏的雏形。开始把剩下的两两互不相连的星星消去。那么如何判断是GameOver还是进入下一关呢。。其实游戏数据贯穿整个游戏,包括星星消除的时候要加到获得分数上,消去剩下两两不相连的星星的时候的加分政策等,因此如果前面没有做这一块的,最好回去搞一搞。

  6. 利用cocos2dx 3.2开发消灭星星九为游戏添加一些特效

    needClear是一个flag,当游戏判断不能再继续后,这个flag变为true,开始消除剩下的星星clearSumTime是一个累加器ONE_CLEAR_TIME就是每颗星星消除的时间2.连击加分信息一般消除一次星星都会有连击信息和加多少分的信息。其实这些combo标签就是一张图片,也是通过控制其属性或者runAction来实现。源码ComboEffect.hComboEffect.cpp4.消除星星粒子效果消除星星时,为了实现星星爆裂散落的效果,使用了cocos2d提供的粒子特效引擎对于粒子特效不了

  7. 02 Cocos2D-x引擎win7环境搭建及创建项目

    官网有搭建的文章,直接转载记录。环境搭建:本文介绍如何搭建Cocos2d-x3.2版本的开发环境。项目创建:一、通过命令创建项目前面搭建好环境后,怎样创建自己的Cocos2d-x项目呢?先来看看Cocos2d-x3.2的目录吧这就是Cocos2d-x3.2的目录。输入cocosnew项目名–p包名–lcpp–d路径回车就创建成功了例如:成功后,找到这个项目打开proj.win32目录下的Hello.slnF5成功了。

  8. 利用cocos2dx 3.2开发消灭星星十为游戏添加音效项目源码分享

    一个游戏,声音也是非常的重要,其实cocos2dx里面的简单音效引擎的使用是非常简单的。我这里只不过是用一个类对所有的音效进行管理罢了。Audio.hAudio.cpp好了,本系列教程到此结束,第一次写教程如有不对请见谅或指教,谢谢大家。最后附上整个项目的源代码点击打开链接

  9. 03 Helloworld

    程序都有一个入口点,在C++就是main函数了,打开main.cpp,代码如下:123456789101112131415161718#include"main.h"#include"AppDelegate.h"#include"cocos2d.h"USING_NS_CC;intAPIENTRY_tWinMain{UNREFERENCED_ParaMETER;UNREFERENCED_ParaMETER;//createtheapplicationinstanceAppDelegateapp;return

  10. MenuItemImage*图标菜单创建注意事项

    学习cocos2dx,看的是cocos2d-x3.x手游开发实例详解,这本书错误一大把,本着探索求知勇于发现错误改正错误的精神,我跟着书上的例子一起调试,当学习到场景切换这个小节的时候,出了个错误,卡了我好几个小时。

返回
顶部