Html5 Egret遊戲開發 成語大挑戰(五)界面切換和數據處理

通過前面的製做,使用Egret的Wing很快完成了開始界面和選關卡界面,下面一般來講就是遊戲界面,但此時界面切換和關卡數據尚未準備好,此次講解界面的切換和關卡數據的解析。前面屢次修改了Main.ts文件中startCreateScene的方法,這個方法就是當遊戲的前置Loading所有完成以後,執行的開始方法,Main自己就是UI容器,因此直接Add進去作好的UI邏輯便可。html

界面切換json

這裏涉及到一個界面切換的代碼設計問題,以開始界面爲例,當「開始遊戲」按鈕點擊以後,應將開始界面移除,進入到選關卡界面,寫法看起來簡單,可是可能咱們須要獲取Main並將其移出的同時還要new一個新的選關界面進來,此時不少人的寫法可能將Main保存,方便其餘類訪問,或是在Main裏面寫上一大堆的UI處理邏輯,這樣看來代碼就不怎麼優雅了,並且,爲了性能和安全問題,一般還要手動移出掉內部使用的EventListener,比較周全的作法是,添加RemoveStage監聽,而後移除本身的同時,將子對象給移除,這就是爲何Main中LoadingUI處理時,出現了大量的addEventListener和removeEventListener的緣由。設計模式

在本項目中,使用單例模式來保證代碼的簡潔性,讓它看起來沒有那麼多調來調去的方法,單例會一直保證一個實例在內存中,若是這個對象會被頻繁使用,並且遊戲中一隻有一個的話,就比較適用,好比說遊戲中的界面類,以本項目來講,總計也就是3-4個界面,並且老是它們跳來跳去,因此,在這裏使用單例控制,爲SceneBegin和SceneLevels添加單例,如下是SceneBegin類的代碼。緩存

class SceneBegin extends eui.Component {
    //單例
    private static shared: SceneBegin;
    public static Shared() {
        if(SceneBegin.shared == null) {
            SceneBegin.shared = new SceneBegin();
        }
        return SceneBegin.shared;
    }
    private btn_begin:eui.Button;
    public constructor() {
          super();
          this.skinName = "src/Game/SceneBeginSkin.exml";
          this.btn_begin.addEventListener(egret.TouchEvent.TOUCH_TAP,this.onclick_begin,this);
    }
    private onclick_begin(){
        //console.log("game begin!");
        this.parent.addChild(SceneLevels.Shared());
        this.parent.removeChild(this);
    }
}

上面的onclick_begin()使用了一個經常使用的父對象便捷操做法,直接調用父對象的addChild把另一個界面添加進來,同時把本身移除掉,比起獲取全局對象處理跳轉是否是簡單清晰了呢?想進階的童鞋,能夠研究一下更深刻的玩法,一樣的SceneLevels就不寫全了,直接在onclick_back()使用上面一樣的技巧:安全

//單例
private static shared: SceneLevels;
public static Shared() {
    if(SceneLevels.shared == null) {
        SceneLevels.shared = new SceneLevels();
    }
    return SceneLevels.shared;
}
private onclick_back() {
    this.parent.addChild(SceneBegin.Shared());
    this.parent.removeChild(this);
}

如今打開Main.ts把startCreateScene修改一下:數據結構

    protected startCreateScene(): void {
        this.addChild(SceneBegin.Shared());
    }

如今運行起來看看跳轉效果吧,其餘的相似的界面同理能夠用這種方法,異步

關卡數據處理ide

下面就要考慮遊戲數據問題,遊戲的數據已經作好了一個json文件:questions.json性能

將這個文件添加到資源中,並保證提早讀取完畢,在給出的項目代碼中,是放在了項目根目錄下,拖進去的時候會自動複製到assets目錄下面一份。學習

 

固然,你也能夠異步來處理,異步會帶來更多邏輯複雜性,這裏僅僅是簡單的教程,之後再說更復雜的,如今建立一個LevelDataManager類,用來管理關卡數據:

//每一個問題(關卡)的數據結構
class LevelDataItem{
    public answer:string;
    public img:string;
    public word:string;
    public tip:string;
    public content:string;
}
//關卡數據管理器
class LevelDataManager {
    //單例
    private static shared:LevelDataManager;
    public static Shared(){
        if(LevelDataManager.shared == null){
            LevelDataManager.shared = new LevelDataManager();
        }
        return LevelDataManager.shared;
    }
    //一個關卡的保存數據組
    private items:LevelDataItem[] = [];
    
    public constructor() {
          //使用RES讀取和構建JSON數據,JSON數據能夠直接解析到目標結構
        this.items = RES.getRes("questions_json");
    }
    //經過關卡號得到一個關的數據
    public GetLevel(level:number):LevelDataItem{
          if(level < 0) level = 0;
          if(level >= this.items.length) level = this.items.length - 1;
        return this.items[level];
    }
    //得到當前的遊戲最遠進度
    public get Milestone():number{
        var milestone = egret.localStorage.getItem("CYDTZ_Milestone");
        //若是沒有數據,那麼默認值就是第一關
        if(milestone == "" || milestone == null){
            milestone = "1";
        }
        return parseInt(milestone);
    }
    //設置當前的遊戲最遠進度
    public set Milestone(value:number){
        egret.localStorage.setItem("CYDTZ_Milestone",value.toString());
    }
}

一樣也使用了單例方式,咱們利用這種方式來保存遊戲的關卡數據,並能隨時方便的訪問它,並且在構造的時候就把數據給處理好了,使用egret.localStorage來管理遊戲的進度,你會發如今最頂部寫了一個LevelDataItem類,這個類用來解析每一個關卡的具體數據,能夠經過json來對應其中各類屬性。

組合到選關界面中

那麼咱們如今有了遊戲關卡數據,要組合到SceneLevels類中,作數據的訪問,以方便在進入到選關界面的時候,可以正確的顯示遊戲關卡狀態,因此要對SceneLevels.ts作一些修改:

 1 class SceneLevels extends eui.Component {
 2     //單例
 3     private static shared: SceneLevels;
 4     public static Shared() {
 5         if(SceneLevels.shared == null) {
 6             SceneLevels.shared = new SceneLevels();
 7         }
 8         return SceneLevels.shared;
 9     }
10     private btn_back: eui.Button;
11     private group_levels:eui.Group;
12     private img_arrow: eui.Image;
13     private sel_level: number = 0;
14     private LevelIcons:LevelIcon[] = [];
15     public constructor() {
16         super();
17         this.skinName = "src/Game/SceneLevelsSkin.exml";
18         this.btn_back.addEventListener(egret.TouchEvent.TOUCH_TAP,this.onclick_back,this);
19         //建立地圖選項
20         var row = 20;
21         var col = 10;
22         var spanx = 720 / col;      //計算行x間隔
23         var spany = 1136 / row;     //計算列y間隔
24         var group = new eui.Group();//地圖背景
25         group.width = 720;
26         group.height = (spany * 400 );//算出最大尺寸
27         //填充背景
28         for(var i = 0;i <= (group.height / 1138) ;i++) {
29             var img = new eui.Image();
30             img.source = RES.getRes("GameBG2_jpg");
31             img.y = i * 1138;
32             img.touchEnabled = false;
33             this.group_levels.addChildAt(img,0);
34         }
35         //以正弦曲線繪製關卡圖標的路徑
36         var milestone: number = LevelDataManager.Shared().Milestone;
37         for(var i = 0; i<400;i++){
38             var icon = new LevelIcon();
39             icon.Level = i + 1;
40             icon.y = spany * i /2;
41             icon.x = Math.sin(icon.y / 180 * Math.PI) * 200 + group.width / 2;
42             icon.y += spany * i /2;
43             icon.y = group.height - icon.y - spany;
44             group.addChild(icon);
45             icon.addEventListener(egret.TouchEvent.TOUCH_TAP,this.onclick_level,this);
46             //依據進度設置關卡顯示
47             icon.enabled = i < milestone;
48             //保存到一個列表中
49             this.LevelIcons.push(icon);
50         }
51         //開啓位圖緩存模式
52         group.cacheAsBitmap = true;
53         this.group_levels.addChild(group);
54         //捲動到最底層
55         this.group_levels.scrollV = group.height - 1100;
56         //跟蹤箭頭
57         this.img_arrow = new eui.Image();
58         this.img_arrow.source = RES.getRes("PageDownBtn_png");
59         this.img_arrow.anchorOffsetX = 124 / 2 - group.getChildAt(0).width / 2;
60         this.img_arrow.anchorOffsetY = 76;
61         this.img_arrow.touchEnabled = false;
62         this.img_arrow.x = group.getChildAt(0).x;
63         this.img_arrow.y = group.getChildAt(0).y;
64         group.addChild(this.img_arrow);
65         
66     }
67     private onclick_back() {
68         this.parent.addChild(SceneBegin.Shared());
69         this.parent.removeChild(this);
70     }
71     private onclick_level(e:egret.TouchEvent){
72         var icon = <LevelIcon>e.currentTarget;
73         if(this.sel_level != icon.Level){
74             this.img_arrow.x = icon.x;
75             this.img_arrow.y = icon.y;
76             this.sel_level = icon.Level;
77         }else{
78             //進入並開始遊戲
79         }
80     }
81     //打開指定的關卡,若是大於最遠關卡,則保存數據也跟着調整
82     public OpenLevel(level:number){
83         var icon = this.LevelIcons[level - 1];
84         icon.enabled = true;
85         if(level > LevelDataManager.Shared().Milestone){
86             LevelDataManager.Shared().Milestone = level;
87             //同時將選定標記置於其上
88             this.img_arrow.x = icon.x;
89             this.img_arrow.y = icon.y;
90             this.sel_level = icon.Level;
91         }
92     }
93 }
SceneLevels.ts

對於這裏簡單講一下,參看第47行,在建立圖標的時候,咱們將經過判斷當前的進度來決定按鈕的開啓狀態,71行的onclick_level()方法增長了一個當前選擇sel_level變量來幫助完成選定後進入的關卡纔開始遊戲,最後,OpenLevel是爲了未來作準備,當關卡完成後就會開啓對應關卡icon。

數據建立優化

如今能夠試試運行一下看看效果,但仍然不完美,由於當在開始界面點開始的時候,就會產生一次卡頓,是由於解析JSON數據源形成的,要知道有400多道題目,光json文件就有100多KB,卡頓是必然發生的事情,優化的方法有如下幾種:

一、展示層面,在Loading的時候進行處理,如在資源讀取完成以後,調用一下LevelDataManager.Shared(),這樣就得要求預讀

二、將數據源拆分,分開加載處理,50個題目一個文件,或者1道題1個文件,個人連連看數據就是這樣處理

三、將數據源變成源碼的一部分,如.ts或.js,這種方式讀取處理效率卻是提高了,可是維護起來比較麻煩

 

本篇項目源碼:ChengyuTiaozhan2.zip(因爲博客園的文件大小限制,resource資源方面請到第二篇的後面下載) 

本篇主要學習使用了單例設計模式、JSON數據使用、egret.localStorage儲存遊戲進度數據。

相關文章
相關標籤/搜索