在上一篇中,簡單的使用界面元素快速實現了一個遊戲中的二級頁面,這種直接在遊戲頁面上作UI的作法並不太好,緣由是,UI會讓遊戲的壓力變大,即便它是隱蔽的,若是一樣的功能在其它的地方也是同樣的,那麼就要寫多個一樣的邏輯嗎?例如設置界面,遊戲中的設置界面基本上功能都是同樣,若是每一個UI中都作一遍,是多麼愚蠢的辦法?在UI的代碼設計中,通常來講,單獨的功能不會在其它的地方用到,如GameOver,就直接寫在UI裏,而若是是通用功能,則最好的作法是作一個通用的單例類或者工廠類在須要的時候將它們初始化,在多個UI中複用邏輯規則。咱們就直接用聲音設置來實現上面的核心思想,通用類的UI製做,在開始以前,先把SoundMenager類準備好,顧名思義聲音管理者,來管理這些聲音。
聲音管理和播放
請確保你的assets裏有sound目錄,裏面的mp3都在,如buttonclick.mp3
直接實現下面的類:html
class SoundMenager { private static shared: SoundMenager; public static Shared(): SoundMenager { if(SoundMenager.shared == null) SoundMenager.shared = new SoundMenager(); return SoundMenager.shared; } private _click: egret.Sound;//點擊聲音 private _word: egret.Sound;//點擊字塊的聲音 private _right: egret.Sound;//若是勝利 private _wrong: egret.Sound;//若是錯誤 private _bgm: egret.Sound;//背景音樂 private _bgm_channel: egret.SoundChannel;//保存用來靜音用 public constructor() { this._click = new egret.Sound(); this._click.load("resource/assets/sound/buttonclick.mp3"); this._bgm = new egret.Sound(); this._bgm.load("resource/assets/sound/Music.mp3"); this._right = new egret.Sound(); this._right.load("resource/assets/sound/right.mp3"); this._wrong = new egret.Sound(); this._wrong.load("resource/assets/sound/wrong.mp3"); this._word = new egret.Sound(); this._word.load("resource/assets/sound/type_word.mp3"); } public PlayBGM() { if(this.IsMusic) { this._bgm_channel = this._bgm.play(0,0); } } public StopBGM() { if(this._bgm_channel != null) { this._bgm_channel.stop(); } } public PlayClick() { if(this.IsSound) { this._click.play(0,1); } } public PlayRight() { if(this.IsSound) { this._right.play(0,1); } } public PlayWrong() { if(this.IsSound) { this._wrong.play(0,1); } } public PlayWord() { if(this.IsSound) { this._word.play(0,1); } } //音樂是否播放,保存設置 public set IsMusic(value) { if(!value) { egret.localStorage.setItem("ismusic","0"); this.StopBGM(); } else { egret.localStorage.setItem("ismusic","1"); this.PlayBGM(); } } public get IsMusic(): boolean { var b = egret.localStorage.getItem("ismusic"); if(b == null || b == "") { return true; } else { return b == "1"; } } //聲效是否播放,保存設置 public set IsSound(value) { if(value) { egret.localStorage.setItem("isSound","1"); } else { egret.localStorage.setItem("isSound","0"); } } public get IsSound(): boolean { var b = egret.localStorage.getItem("isSound"); if(b == null || b == "") { return true; } else { return b == "1"; } } }
我想這個代碼就不作太多的解釋了,它是用了異步load聲音文件,經過幾個Play方法來播放,實現了兩個屬性:IsSound和IsMusic,來控制是否靜音和播放音樂,這個類裏_bgm_channel保存了bgm的聲音通道,當靜音的時候就會stop聲音,確保UI交互是正確的。
將它們加到Begin.ts裏異步
運行一下,你會發現出現了這個錯誤:函數
大概的意思是和它所寫的不太同樣,若是你有耐心跟蹤斷點會有驚喜,在這裏就不賣關子,出這個錯誤的緣由是,egret.Sound.load方法中初始化的一個屬性是null,因此,避免這個問題的方式也很簡單,在LoadingUI的構造函數中調用SoundMenager.Shared()完成預先加載。測試
public constructor() { super(); //預先加載聲音 SoundMenager.Shared(); this.createView(); }
這個時間差別其實只有幾個毫秒,可是能正確的讓聲音輸出,估計是底層的問題,就不深究其緣由了,反正能解決就行,對於異步加載仍是預先加載,這屬於我的習慣問題,但聲音在如今的手機遊戲中並非主要的組成部分,每次打開在加載遊戲聲音上耗費大量時間得不償失,不如先玩起來再播放體驗來的好。
在你想要加的地方都加上聲音,這裏就不一一列舉,只須要提一下關於錯誤聲音,須要在SceneGame類裏作一個判斷:ui
它的意思很簡單,若是拼寫的檢查字段是4個漢字,就會提示錯誤的聲音,勝利的聲音以前就處理過了,因此邏輯上沒有問題。
好了,打開遊戲,測試聲音,感受一下哈
通用的設置界面
設置界面都是通用的,因此咱們可使用一個類配一個皮膚來實現它,創建一個名爲GameSettingSkin的exml皮膚文件而後設計設置界面,因爲沒有準備相關的素材,只得就地取材,將MoneyBG_png這個圖片作一下改造,變成九宮格的圖形,這樣就能夠自由拉伸,當設置界面的底板了:this
一樣,利用YesBtn_jpg和其它的素材組成遊戲設置界面:spa
最終的exml的文件應該是這樣的:設計
<?xml version='1.0' encoding='utf-8'?> <e:Skin class="GameSettingSkin" width="720" height="1136" xmlns:e="http://ns.egret.com/eui" xmlns:w="http://ns.egret.com/wing"> <e:Rect right="0" top="0" bottom="0" left="0" fillAlpha="0.6" locked="true"/> <e:Image source="MoneyBG_png" scale9Grid="17,8,196,51" width="400" height="255" horizontalCenter="0" verticalCenter="0.5"/> <e:Button id="btn_agree" y="623" horizontalCenter="0.5"> <e:skinName> <e:Skin states="up,down,disabled"> <e:Image width="100%" height="100%" source="YesBtn_jpg" source.down="YesBtn1_jpg"/> <e:Label id="labelDisplay" horizontalCenter="0" verticalCenter="0"/> </e:Skin> </e:skinName> </e:Button> <e:Group width="102" height="94" x="238" y="512"> <e:Button id="btn_music" y="0" x="0"> <e:skinName> <e:Skin states="up,down,disabled"> <e:Image width="100%" height="100%" source="btn_music_png" source.down="btn_music_down_png"/> <e:Label id="labelDisplay" horizontalCenter="0" verticalCenter="0"/> </e:Skin> </e:skinName> </e:Button> <e:Image id="img_music_disable" x="6" y="2" source="btn_disable_png" touchEnabled="false"/> </e:Group> <e:Group x="403" y="512" width="102" height="94"> <e:Button id="btn_sound" y="0" x="0"> <e:skinName> <e:Skin states="up,down,disabled"> <e:Image width="100%" height="100%" source="btn_sound_png" source.down="btn_sound_down_png"/> <e:Label id="labelDisplay" horizontalCenter="0" verticalCenter="0"/> </e:Skin> </e:skinName> </e:Button> <e:Image id="img_sound_disable" y="2" x="6" source="btn_disable_png" touchEnabled="false"/> </e:Group> <e:Label text="設置" y="466" horizontalCenter="0"/> </e:Skin>
那麼配以.ts類來實現UI的邏輯:code
//使用一個全局通用的設置界面 class GameSetting extends eui.Component { private static shared: GameSetting; public static Shared(): GameSetting { if(GameSetting.shared == null) GameSetting.shared = new GameSetting(); return GameSetting.shared; } private btn_agree:eui.Button; //贊成按鈕,至關於直接關閉界面 private img_music_disable: eui.Image;//音樂靜音顯示 private img_sound_disable: eui.Image;//聲音靜音顯示 private btn_sound: eui.Button; //聲音按鈕 private btn_music: eui.Button; //音樂按鈕 public constructor() { super(); this.skinName = "src/Game/GameSettingSkin.exml"; this.btn_agree.addEventListener(egret.TouchEvent.TOUCH_TAP,this.click_agree,this); this.btn_sound.addEventListener(egret.TouchEvent.TOUCH_TAP,this.click_sound,this); this.btn_music.addEventListener(egret.TouchEvent.TOUCH_TAP,this.click_music,this); //經過聲音管理類來處理界面顯示 this.update_buttonstate(); } private click_agree(){ SoundMenager.Shared().PlayClick(); this.parent.removeChild(this); } private click_sound(){ SoundMenager.Shared().PlayClick(); SoundMenager.Shared().IsSound = !SoundMenager.Shared().IsSound; this.update_buttonstate(); } private click_music(){ SoundMenager.Shared().PlayClick(); SoundMenager.Shared().IsMusic = !SoundMenager.Shared().IsMusic; this.update_buttonstate(); } private update_buttonstate(){ this.img_music_disable.visible = !SoundMenager.Shared().IsMusic; this.img_sound_disable.visible = !SoundMenager.Shared().IsSound; } }
這個代碼我就不作太多的講解,就是對於一些元素的控制,eui大法真好啊。
下面在SceneBegin、SceneGame、SceneLevels的皮膚文件中分別加入btn_setting,一樣,使用MoneyBG_png來作通用的底版,省事就好了。xml
在各個類中添加對它的定義和處理事件
//第9章新加設置按鈕 private btn_setting: ui.Button; //第9章設置事件 this.btn_setting.addEventListener(egret.TouchEvent.TOUCH_TAP,this.onclick_setting,this);
實現onclick_setting方法:
private onclick_setting() { SoundMenager.Shared().PlayClick(); this.addChild(GameSetting.Shared()); }
當點擊設置的時候,直接將設置界面的單例UI給添加到本界面中,對應的在GameSetting類中也有this.parent.removeChild(this);將本身移除的方法,全部的設置界面都是一個,結構看起來清晰了不少。
一種比較笨的方式就是挨個添加,還有一種方式是創造一個設置按鈕的獨立按鈕,將它的邏輯寫入本身內部,雖然是一個好方法,但是使用起來比較麻煩,當沒有大量的獨立處理需求時(如ICON),仍是用挨個添加比較簡單一些。
本篇已經完結,使用聲音管理類來加載和播放聲音,用單例來實現通用的界面UI的邏輯處理,在多個場景中重複使用。
到此爲止這個遊戲的完成度已經超過80%,剩下的就是慢慢雕琢以及各類功能的添加,有了前面的基礎,後面的擴展開發已經變的很是easy。此篇拋磚引玉之做可以幫助新學egret的朋友快速上手,其中的一些作法也許不是最好的,問題也同樣不少,歡迎批評指正。
本篇項目源碼:ChengyuTiaozhan6.zip(因爲博客園的文件大小限制,resource資源方面請到第二篇的後面下載)