通過前面的製做,使用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 }
對於這裏簡單講一下,參看第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儲存遊戲進度數據。