在上一篇中,简单的使用界面元素快速实现了一个游戏中的二级页面,这种直接在游戏页面上做UI的做法并不太好,原因是,UI会让游戏的压力变大,即使它是隐蔽的,如果同样的功能在其它的地方也是一样的,那么就要写多个同样的逻辑吗?例如设置界面,游戏中的设置界面基本上功能都是一样,如果每个UI中都做一遍,是多么愚蠢的办法?在UI的代码设计中,一般来说,单独的功能不会在其它的地方用到,如GameOver,就直接写在UI里,而如果是通用功能,则最好的做法是做一个通用的单例类或者工厂类在需要的时候将它们初始化,在多个UI中复用逻辑规则。我们就直接用声音设置来实现上面的核心思想,通用类的UI制作,在开始之前,先把SoundMenager类准备好,顾名思义声音管理者,来管理这些声音。
声音管理和播放
请确保你的assets里有sound目录,里面的mp3都在,如buttonclick.mp3
直接实现下面的类:

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(,);
} }
public StopBGM() {
if(this._bgm_channel != null) {
this._bgm_channel.stop();
}
}
public PlayClick() {
if(this.IsSound) {
this._click.play(,);
}
}
public PlayRight() {
if(this.IsSound) {
this._right.play(,);
}
}
public PlayWrong() {
if(this.IsSound) {
this._wrong.play(,);
}
}
public PlayWord() {
if(this.IsSound) {
this._word.play(,);
}
}
//音乐是否播放,保存设置
public set IsMusic(value) {
if(!value) {
egret.localStorage.setItem("ismusic","");
this.StopBGM();
} else {
egret.localStorage.setItem("ismusic","");
this.PlayBGM();
}
}
public get IsMusic(): boolean {
var b = egret.localStorage.getItem("ismusic");
if(b == null || b == "") {
return true;
}
else {
return b == "";
}
}
//声效是否播放,保存设置
public set IsSound(value) {
if(value) {
egret.localStorage.setItem("isSound","");
} else {
egret.localStorage.setItem("isSound","");
}
}
public get IsSound(): boolean {
var b = egret.localStorage.getItem("isSound");
if(b == null || b == "") {
return true;
}
else {
return b == "";
}
}
}

我想这个代码就不做太多的解释了,它是用了异步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类里做一个判断:

它的意思很简单,如果拼写的检查字段是4个汉字,就会提示错误的声音,胜利的声音之前就处理过了,所以逻辑上没有问题。
好了,打开游戏,测试声音,感觉一下哈
通用的设置界面
设置界面都是通用的,所以我们可以使用一个类配一个皮肤来实现它,建立一个名为GameSettingSkin的exml皮肤文件然后设计设置界面,由于没有准备相关的素材,只得就地取材,将MoneyBG_png这个图片做一下改造,变成九宫格的图形,这样就可以自由拉伸,当设置界面的底板了:

同样,利用YesBtn_jpg和其它的素材组成游戏设置界面:

最终的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的逻辑:

//使用一个全局通用的设置界面
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来做通用的底版,省事就行了。

在各个类中添加对它的定义和处理事件

//第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资源方面请到第二篇的后面下载

Html5 Egret游戏开发 成语大挑战(九)设置界面和声音管理的更多相关文章

  1. Html5 Egret游戏开发 成语大挑战(一)开篇

    最近接触了Egret白鹭引擎,感觉非常好用,提供了各种各样的开发工具让开发者和设计者更加便捷,并且基于typescript语言开发省去了很多学习成本,对于我们这种掉微软坑许久的童鞋来说,确实很有吸引力 ...

  2. Html5 Egret游戏开发 成语大挑战(二)干净的eui项目和资源准备

    现在我们使用egret来起步开发一个名叫<成语大挑战>的小游戏,关于egret的开发环境就不在这里啰嗦了,直接去官方下载安装就可,egret是我见过开发环境部署最简单的解决方案,这个系列教 ...

  3. Html5 Egret游戏开发 成语大挑战(八)一般性二级页面处理

    在游戏中,我们一般会有各种各样的二级页面,比如游戏暂停界面或者游戏结束界面,这些界面组成了对玩家交互主要手段,在游戏开发中,对于这些界面的coding组织是非常有学问的,如果倒退到十年前,游戏开发的老 ...

  4. Html5 Egret游戏开发 成语大挑战(六)游戏界面构建和设计

    本篇将主要讲解游戏界面的构建和设计,会应用到egret.eui的自定义组件,可以很直观的构建一个游戏整体,这里我们仍然只需要使用EgretWing就可以达到目的,本篇可能是篇幅最少的一个,但是涉及自定 ...

  5. Html5 Egret游戏开发 成语大挑战(七)游戏逻辑和数据处理

    本篇在前面的基础上,将进行逻辑的编码开发让游戏能够正式的玩起来,这里没有注重太多的体验细节,而是直接实现游戏的规则逻辑,将分成两个部分说明:数据处理和游戏逻辑. 初始化游戏数据 在前面的第五篇中,我们 ...

  6. Html5 Egret游戏开发 成语大挑战(四)选关界面

    通过前面的开始界面基本上了解了eui的使用方法,可以简单快速的制作一个UI界面,本篇使用第二界面选关界面展示更为难一点的代码控制,来展现关卡地图的内容,请确保素材和资源完整,可以在前面的教程中找到下载 ...

  7. Html5 Egret游戏开发 成语大挑战(三)开始界面

    本篇需要在前面的素材准备完毕,才可以开始,使用egret的eui结合代码编辑,快速完成基本的界面搭建,这里写的可能比较细,目的是减少大家对于其中一些操作疑问,我去掉了很多无用的步骤,以最精简的流程来完 ...

  8. Html5 Egret游戏开发 成语大挑战(五)界面切换和数据处理

    经过前面的制作,使用Egret的Wing很快完成了开始界面和选关卡界面,下面通常来说就是游戏界面,但此时界面切换和关卡数据还没有准备好,这次讲解界面的切换和关卡数据的解析.前面多次修改了Main.ts ...

  9. Phaser是一款专门用于桌面及移动HTML5 2D游戏开发的开源免费框架

    Phaser是一款专门用于桌面及移动HTML5 2D游戏开发的开源免费框架,提供JavaScript和TypeScript双重支持,内置游戏对象的物理属性,采用Pixi.js引擎以加快Canvas和W ...

随机推荐

  1. 【代码笔记】iOS-点击搜索跳转到另外一个页面

    一,效果图. 二,工程图. 三,代码. RootViewController.h #import <UIKit/UIKit.h> @interface RootViewController ...

  2. 【读书笔记】iOS网络-底层网络

    在iOS上,有一个库叫做Core Foundation networking或CFNetwork,它是对原始Socket的轻量级封装,不过它很快对于大多数常见场景来说变得非常笨重了.最后,添加了另一层 ...

  3. cocoapods遇到的问题 (pod: command not found的问题)

    在使用CocoaPod为项目添加第三方类库时,出现了-bash: pod: command not found的问题: 在网上看到了一位哥的方法:确实有效:

  4. 安卓开发第一步:Android Studio安装配置

    虽然本人是JAVA开发工程师平时主要开发Web App,但因为项目需求需要开发对应的移动端.一时又找不到合适的安卓开发人员,兄弟我只好被项目经理"抓来当壮丁了".俗话说好" ...

  5. 使用git的分支功能实现定制功能摘取与组合的想法

    前言,这个想法应该是git比较通用的做法,只是我还没用过,所以把自己的想法记录在这里,督促自己以后按这个方式执行. 我们公司现在面临一个问题, 就是客户的定制需求很多,很杂,其中坑爹需求很多. 我还没 ...

  6. Java开发人员最常犯的10个错误

    这个列表总结了10个Java开发人员最常犯的错误. Array转ArrayList 当需要把Array转成ArrayList的时候,开发人员经常这样做: List<String> list ...

  7. 按要求编写Java应用程序。 (1)建立一个名叫Cat的类: 属性:姓名、毛色、年龄 行为:显示姓名、喊叫 (2)编写主类: 创建一个对象猫,姓名为“妮妮”,毛色为“灰色”,年龄为2岁,在屏幕上输 出该对象的毛色和年龄,让该对象调用显示姓名和喊叫两个方法。

    package zuoye; public class Cat { String name="妮妮"; String color="灰色"; int age=1 ...

  8. C#委托学习

    标签(空格分隔): C# 看Markdown效果支持的不大好. 买来<CLR Via C#>这本书很久了,一直也没有对其进行总结,看的非常凌乱,趁此机会好好总结一下,也算对C#学习的一个总 ...

  9. cocos2d-x 3.10 屏幕适配问题

    cocos2d-x 的屏幕适配问题困扰了我很久,差不多有一个星期吧.通过亲身实践才解决了问题,分享一下解决办法,供大家借鉴学习. 其实解决办法很简单,把下面代码注释掉就好了 // if (frameS ...

  10. Java api 入门教程 之 JAVA的文件操作

    I/O类使用 由于在IO操作中,需要使用的数据源有很多,作为一个IO技术的初学者,从读写文件开始学习IO技术是一个比较好的选择.因为文件是一种常见的数据源,而且读写文件也是程序员进行IO编程的一个基本 ...