使用Cocos2d-JS制做游戏新手引导(一)

想到新手引导的功能时可能不少人都会以为头痛,难如下手。特别是在游戏自己功能或需求还不稳定的状况,更是难以应付,本人就是在这种状况下接受了一个艰巨的任务。在痛定思痛以后,开始了引导功能开发。在作的过程当中一点点发现不少有意思的东西,想分享给你们。css

 

1、痛点:新手引导制做的难点及弊端html

  • 须要在具备引导功能的代码单元插入引导代码或逻辑判断,干扰正常流程。html5

  • 引导代码的加入会影响原有的代码逻辑与流程,使代码变得复杂加大维护难度。node

  • 界面或需求发生变化后引导功能须要大幅修改或从新制做。c++

  • 指引(手指提示)对应的矩形区定位麻烦,特别是须要适应不一样尺寸屏幕的时候更加困难。web

  • 编写引导配置文件也很头痛,须要策划、程序的高度配合。编程

 

2、指望:新手引导编程体验json

笔者进入游戏开发应该说是手机游戏开发并非很长时间,虽然参于过多个项目,但亲自编写新手引导这仍是头一次。当时接到新人引导任务时,咱们的项目只完成了:登陆->主界面->抽卡->布阵->章节->关卡->战斗这样一个基本流程,界面美术、功能需求都极不稳定。但在公司的硬性要求下,冒着九死一辈子的危险开始了新手引导功能开发。在了解到传统的引导制做过程当中的难点与弊端后,一直在思考没有更好的实现方式,我心中的引导编程的方式有如下几点:服务器

  • 不须要在每一个单元中去插入引导代码,游戏代码与引导代码应该尽可能分离。本人很难忍受漂亮的代码被无情引导打乱,更难忍受原本糟糕的代码被引导弄得支离破碎。框架

  • 界面只发生简单UI位移、节点层次改变不须要修改引导代码。

  • 定位指引矩形区应该尽可能的简单,且自适应不一样尺寸屏幕。最好能作到策划人员均可以来制做部分流程引导。

  • 在引导需求明确、游戏功能正常的状况下,制做一个常规的引导步骤应该是很是快捷的,不会超过3分钟,快的话1分钟内就应该搞定(不是笔者说大话,确实已经实现)。

 

3、思想:引导功能的设计思路

在描述引导功有设计思路以前,有个重要的前题:命名规范。

 

命名规范主要有两个方面:

  1. Cocos Studio中的控件名字

  2. 代码中动态建立的控件名字,以及类成员变量的名字。

在笔者的项目中使用了sw.UILoader来管理cocostudio的UI命名和事件。 如不了解请参见个人另一篇教程《在Cocos2d-JS中实现自动绑定Cocos Studio UI控件与事件

 

咱们这里引入两个概念:任务与任务组。任务:把引导中的一个最小步骤称之为一个任务,好比提示点击某个按钮。任务组:把一系列的任务放在一个任务组中,当这个任务组中的任务所有完成,咱们会保存一次任务进度。此时从新进入游戏将不会再执行这个任务,而是执行它的下一个任务组中的任务。能够理解任务组是引导中的一个步骤。

 

用json格式表示如:

{

    [{任务1},{任务2},{任务3}]

    [{任务7},{任务8},{任务9}]

    [{任务4},{任务5},{任务6}]

}

当从一个任务组中的任务中断后,再次进入引导 须要从新从这个任务组的第一个任务开始。

见下图演示了一个从主界面点击召唤->灵石召唤一次->点击得到->肯定->仙玉召唤一次->点击得到->肯定->点击空白退出召唤界面的流程。

js1.gif

上图演示的引导我分红两个任务组:灵石召唤、仙玉召唤。 任务配置以下:

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
34
35
36
37
38
39
40
41
42
"3" :[
     {
         "name" "4.提示指向灵石召唤按钮" ,
         "command" "手型提示" ,
         "tag" "_oneMoneyButton"
     },
     {
         "name" "保存进度" ,
         "command" "保存进度"
     },
     {
         "name" "5.提示指向角色肯定按钮" ,
         "command" "手型提示" ,
         "tag" "_UILotteryHero > _confirmBtn"
     },
     {
         "name" "6.提示指向角色图标肯定按钮" ,
         "command" "手型提示" ,
         "tag" "_UILotteryTimes > _confirmBtn"
     }
],
 
"4" :[
     {
         "name" "7.提示指向仙玉召唤按钮" ,
         "command" "手型提示" ,
         "tag" "_oneGoldButton"
     },
     {
         "name" "保存进度" ,
         "command" "保存进度"
     },
     {
         "name" "8.提示指向角色肯定按钮" ,
         "command" "手型提示" ,
         "tag" "_UILotteryHero/Panel_33/Image_10/_confirmBtn"
     },
     {
         "name" "9.提示指向角色图标肯定按钮" ,
         "command" "手型提示" ,
         "tag" "_UILotteryTimes/Panel_11/Image_1/_confirmBtn"
     },],

其中每一个任务中的name用于调试打印的对引导自己无实际用处,在任务开始和结速都会有提示,若是出错方便定位。 command这里应该叫作指令,对应一段具体功能的代码或函数,我这里设置了两个:手型提示、保存进度。

  • 手型提示:须要配合tag字段的值,tag描述了一个当前任务状态下的一个node节点的索引。具体tag的编写方式请看下面一节"实如今节点树中定位控件"。

  • 进度保存:手动进度保存是为了确保在任务中断后,游戏流程不受影响。 在招唤这个功能里,是只能召唤一次的,若是已经召唤成功了,服务器已经更新数据 ,后面的引导都是客户端的界面显示、关闭引导。若是在召唤以后,作一次进度保存,任务中断后再次进入引导会跳过这个任务组中的任务。

在理解了任务的功能后,须要有一个上层框架来一个一个的执行这些任务。

 

引导框架:在任务条件知足时(好比:等级要达到多少或者无任何条件),指示用户进行某项任务(好比按钮的点击)。当任务完成后,执行下一个任务,直接到所有任务被完成。它须要具备如下几点功能:

  • 条件检查:检查是否该执行该任务,默认为无条件执行。这须要检查任务是否有onTaskBegan函数 ,不存在或返回ture才能执行任务指令

  • UI定位:找到出当前任务中UI节点对应的矩形区。在指引任务中准确编写UI定位描述,由框架去检索UI节点,当检索到节点后调用任务的onLocateNode函数,传入节点对像,这可让整个引导能够有更多的扩展。

  • 指引动画:当定位成功后,引导框播放指引提示动画,提示用户操做该矩形区。

  • 触摸限制:屏蔽定位节点矩形区外的操做所有。

  • 事件检查:矩形区对应的UI事件是否被执行。

  • 任务完成:通知引导框架任务完成,进入下一个任务。

 

4、定位:实如今节点树中定位控件

以上几点中首要解决的是对UI控件的定位,对UI定位最直接有效的方法是在拿到这个UI控件对象,而后取出他的BoundingBox、锚点信息,进行座标转换。但如何才能拿到这个控件对象呢? 这里有两种实现方式:

1. 遍历场景树,把它搜索出来。

2. 事先把这个控件对象注册到你的引导框架中。

 

我采起的是第一种方法来定位控件,由于我不想在处处代码中添加游戏逻辑之外的东西。并且cocos2d-js中提供有现成的函数cc.helper.seekWidgetByName,若是你作的是手机游戏是不能直接使用这个函数的。在HTML5上这个函数能够遍历整个节点树 ,在jsb上只是遍历的Widget节点。 有两种方法解决这个问题:

1.把cc.helper.seekWidgetByName函数复制到本身代码文件中,从新取个名字叫:xxx.helper.seekNodeByName。在html5和jsb上都使用这个函数。

2.在c++ jsb上把cc.Helper.seekWidgetByName的参数修改为在Node节点上作遍历,或都从新封装一个jsb上的seekNodeByName函数 。

 

我这里偷懒仍是使用第一种方法。经过上面的方法是否已经解决UI定位的问题呢?应该没那么简单吧!经过这种方法定位控件,就不用在引导配置文件里填写坐标或矩形数据,那是极其愚蠢的办法。

打住,还有问题!!! 若是一个场景树中有两个相同节点名字怎搞?

这个问题确实问的很正确。由于咱们常常会有名字相同的节点存在。好比下图:

20150301092305852.jpg

若是他们名字都叫button,使用seekNodeByName是来定位控件的话只能找到其中一个。具体是那个是根据你addChild时的顺序来决定的。这个问题如何解决?能惟一肯定一个控件在场景树中的方法就是他的“完整路径”,像这样一下来描述两个button:

"招唤界面/灵石招唤/召唤一次"

"招唤界面/仙玉招唤/召唤一次"

其实咱们已经能定位到灵石招唤和仙玉招唤了(我这里为了方便理解使用中文名字)只须要这样写:

"灵石招唤/召唤一次"

"仙玉招唤/召唤一次"

这样也能精肯定位到你想要的那个按钮。

 

在这里我实现了一个简易的定位器描述规则,咱们之后经过如下方式在任务中定位一个控件 :

  • 名字描述:在场景中有独一无二的名字时,直接描述控件名如:'_loginButton'。

  • 路径名描述:在场景中须要定位的节点可能有重名时,找到其父节点,确保父节点不会有重名时使用:'parentName/button'。若是父节点也有重名,那就再向上使用其父节点名的父节点以此类推。

  • js属性描述:有一种状况经过getChildByName没法直接访问的节点,如ccui.ScollView容器中的节点。我定义了一种简单的获取方式,例如 'layer1.button' 经过“.”这个符号来定位layer1下的一个属性为button。这种方式是在js中最为直接的方式。

  • 子节点描述:使用完整路径描述一个控件时,有时会以为比较长,例如: 'mainLayer/layer1/button' 能够简写成 'mainLayer>button' 表示定位mainLayer下一个名字叫button的子节点,有能够是1级子节点,也可能为二、三、n级子节点。

  • 复合描述:将以上几个方式组合使用,来描述一个控件:'mainLayer>homeLayer/layer1.button' 。用人话翻译下就是:mainLayer下有一个homeLayer子节点(不论是几级)下的一级子节点layer1下的一个变量名为button的节点.

 

描述符号总结:

  • “/”: 表示一级子节点

  • “>”: 表示一级~n级子节点

  • “.”: 表示属性名

这里就体现了为何要注意名命规范的问题。有web开发经验的人一眼就能看出这里有一点css选择器的味道,呵呵!很是正确,正是借鉴了css选择器的思想,实现一个十分简单的选择器,咱们这里能够称之为“定位器”,由于咱们只须要定位出一个节点。

(未完待续)

相关文章
相关标签/搜索