使用cocos2d-js制作游戏新的引导 (四)-应用篇

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‘,
    command: sz.GuideCommand.GC_FINGER_HINT,
    locator:"_btnHome",
    eventType:0  //"_btnHome"控件的事件函数为touchBegan
}
...
eventType: 2

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

isShowMask

isShowMask: true //是否显示遮罩

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

而且在单个任务步骤中也可以临时开打或关闭遮罩的显示:

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

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

源码地址:https://github.com/ShawnZhang2015/Guide.git

本篇代码演示:git checkout step1

时间: 2024-10-26 20:36:37

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

(转)CocosCreator零基础制作游戏《极限跳跃》四、添加游戏主场景控制脚本

CocosCreator零基础制作游戏<极限跳跃>四.添加游戏主场景控制脚本 前面简单的实现了主界面的UI设置,现在我们开始制作游戏的控制脚本. 在资源管理器的Script文件夹中,点击右键新建javascript文件,命名为GAME. 双击打开GAME脚本,在properties: 中添加属性代码. 01 //GAME.js 02   03 cc.Class({ 04     extends: cc.Component, 05     properties: { 06       07   

初学JS——利用JS制作的别踩白块儿(街机模式) 小游戏

初学JS--利用JS制作的别踩白块儿(街机模式) 小游戏 这个是上个星期5写的了,当时是突然想写个游戏,就想到了别踩白块儿,当时的想法是 可能普通模式的别踩白块儿因为他的"块儿"是滚动的向上这种,以我目前会的技术想不出怎么写, 但是如果是街机模式,通过你每按一次按键之后他像下跳一格这样的就非常好实现了. 通过我目前会的知识,实现的步骤大概是这样的: 建一个4X4的表格,制作2张150X100的图片,一张全白色,一张全黑色,命名为0.JPG,1.JPG 就是说当文件名为0的时候就是白色的

cocos2d js ClippingNode 制作标题闪亮特效

1.效果图: 之前在<Android 高仿 IOS7 IPhone 解锁 Slide To Unlock>中制作了文字上闪亮移动的效果,这次我们来看下怎样在cocos2d js 中做出类似的效果. 顺便给我公司的游戏打下广告.https://itunes.apple.com/cn/app/kuang-zhan-san-guo/id691116157? mt=8 2.效果原理 很easy.就是一张白色两边羽化的图片在标题上从左往右移动.可是普通的移动会穿帮.我们须要以标题作为模板来截取白色的图片

使用cocos2d-js制作游戏新手引导(二)

本文上接前我一篇博文<使用cocos2d-js制作游戏新手引导(一)> 一.定位器的实现 定位器的目的是实现对场景树中的节点精确定位,获取对象实例,从而获取节点在界面中的位置.矩形大小等信息. 定位器:在cocos2d(js)游戏引擎中用于精确描述场景树中的某一节点的字符串,其实现方式借鉴了css(层叠样式表)选择器设计思路,以下我们将实现一个简单的从定位器字符串解析到节点定位的整个过程. 1.定位符规则 在cocos2d中可以通过节点名字.节点tag值来表示一个节点,在js中还可以使用对象的

使用cocos2d-js制作游戏新手引导(一)

?  想到新手引导的功能时可能很多人都会觉得头痛,难以下手.特别是在游戏本身功能或需求还不稳定的情况,更是难以应付,本人就是在这种情况下接受了一个艰巨的任务.在痛定思痛之后,开始了引导功能开发.在做的过程中一点点发现很多有意思的东西,想分享给大家. 一.痛点:新手引导制作的难点及弊端 需要在具有引导功能的代码单元插入引导代码或逻辑判断,干扰正常流程. 引导代码的加入会影响原有的代码逻辑与流程,使代码变得复杂加大维护难度. 界面或需求发生变化后引导功能需要大幅修改或重新制作. 指引(手指提示)对应

(转)CocosCreator零基础制作游戏《极限跳跃》八、添加游戏积分系统

CocosCreator零基础制作游戏<极限跳跃>八.添加游戏积分系统 前面我们实现了整个游戏的流程,下面我们来完善游戏的积分系统..先来分析下游戏的积分,第一次展示积分的地方就是我们的游戏主场景MainScene,玩家通过一些机制来获取积分,实现积分的更新,当玩家游戏结束后展示玩家所获的的当前积分. 我们一开始制作游戏场景的时候就制作了一个积分节点score,其string属性值为0.打开GAME.js脚本可以发现,一开始的时候我们onLoad方法就初始化了积分. 接着我们在GAME.js脚

【Cocos2D研究院之游戏开发】

http://www.xuanyusong.com/archives/category/ios/cocos2d_game 分类目录归档:[Cocos2D研究院之游戏开发] 201211-19 Cocos2D研究院之打开全新ViewController与返回(八) 雨松MOMO [Cocos2D研究院之游戏开发] 围观5745次 17条评论          之前cocos2d的文章都是由魏凯同学维护,从今天开始我也会抽时间写点cocos2d的文章.最近在研究如何将IOS游戏与软件结合起来.通常游

Unity制作游戏中的场景

Unity制作游戏中的场景 1.2.3  场景 在Unity中,场景(Scene)就是游戏开发者制作游戏时,所使用的游戏场景.它是一个三维空间,对应的三维坐标轴分别是X轴.Y轴和Z轴本文选自Unity 2D游戏开发从入门到精通清华大学出版社. 要创建一个新的场景,只需单击File|New Scene命令,或者按下快键键Ctrl+N,如图1-16所示. 图1-16  创建程序的命令,以及场景 默认情况下,新创建游戏项目的同时,也新创建了游戏的场景,只不过还没有保存罢了.使用快捷键Ctrl+S即可保

用JS制作一个信息管理平台完整版

  前  言 JRedu 在之前的文章中,介绍了如何用JS制作一个实用的信息管理平台. 但是那样的平台功能过于简陋了,我们今天来继续完善一下. 首先我们回顾一下之前的内容.   1.JSON的基础知识 1.1  什么是JSON JSON是数据交互中,最常用的一种数据格式. 由于各种语言的语法都不相同,在传递数据时,可以将自己语言中的数组.对象等转换为JSON字符串. 传递之后,可以将JSON字符串,再解析为JSON对象. JSON对象的使用与JS中的对象基本相同,唯一需要区别的是,JSON中的键