使用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

相關文章
相關標籤/搜索