Html5 Egret游戏开发 成语大挑战(九)设置界面和声音管理
在上一篇中,简单的使用界面元素快速实现了一个游戏中的二级页面,这种直接在游戏页面上做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游戏开发 成语大挑战(九)设置界面和声音管理的更多相关文章
- Html5 Egret游戏开发 成语大挑战(一)开篇
最近接触了Egret白鹭引擎,感觉非常好用,提供了各种各样的开发工具让开发者和设计者更加便捷,并且基于typescript语言开发省去了很多学习成本,对于我们这种掉微软坑许久的童鞋来说,确实很有吸引力 ...
- Html5 Egret游戏开发 成语大挑战(二)干净的eui项目和资源准备
现在我们使用egret来起步开发一个名叫<成语大挑战>的小游戏,关于egret的开发环境就不在这里啰嗦了,直接去官方下载安装就可,egret是我见过开发环境部署最简单的解决方案,这个系列教 ...
- Html5 Egret游戏开发 成语大挑战(八)一般性二级页面处理
在游戏中,我们一般会有各种各样的二级页面,比如游戏暂停界面或者游戏结束界面,这些界面组成了对玩家交互主要手段,在游戏开发中,对于这些界面的coding组织是非常有学问的,如果倒退到十年前,游戏开发的老 ...
- Html5 Egret游戏开发 成语大挑战(六)游戏界面构建和设计
本篇将主要讲解游戏界面的构建和设计,会应用到egret.eui的自定义组件,可以很直观的构建一个游戏整体,这里我们仍然只需要使用EgretWing就可以达到目的,本篇可能是篇幅最少的一个,但是涉及自定 ...
- Html5 Egret游戏开发 成语大挑战(七)游戏逻辑和数据处理
本篇在前面的基础上,将进行逻辑的编码开发让游戏能够正式的玩起来,这里没有注重太多的体验细节,而是直接实现游戏的规则逻辑,将分成两个部分说明:数据处理和游戏逻辑. 初始化游戏数据 在前面的第五篇中,我们 ...
- Html5 Egret游戏开发 成语大挑战(四)选关界面
通过前面的开始界面基本上了解了eui的使用方法,可以简单快速的制作一个UI界面,本篇使用第二界面选关界面展示更为难一点的代码控制,来展现关卡地图的内容,请确保素材和资源完整,可以在前面的教程中找到下载 ...
- Html5 Egret游戏开发 成语大挑战(三)开始界面
本篇需要在前面的素材准备完毕,才可以开始,使用egret的eui结合代码编辑,快速完成基本的界面搭建,这里写的可能比较细,目的是减少大家对于其中一些操作疑问,我去掉了很多无用的步骤,以最精简的流程来完 ...
- Html5 Egret游戏开发 成语大挑战(五)界面切换和数据处理
经过前面的制作,使用Egret的Wing很快完成了开始界面和选关卡界面,下面通常来说就是游戏界面,但此时界面切换和关卡数据还没有准备好,这次讲解界面的切换和关卡数据的解析.前面多次修改了Main.ts ...
- Phaser是一款专门用于桌面及移动HTML5 2D游戏开发的开源免费框架
Phaser是一款专门用于桌面及移动HTML5 2D游戏开发的开源免费框架,提供JavaScript和TypeScript双重支持,内置游戏对象的物理属性,采用Pixi.js引擎以加快Canvas和W ...
随机推荐
- IOS 手势详解
在IOS中手势可以让用户有很好的体验,因此我们有必要去了解一下手势. (在设置手势是有很多值得注意的地方) *是需要设置为Yes的点击无法响应* *要把手势添加到所需点击的View,否则无法响应* 手 ...
- NTP服务器引起的上行带宽超大
2014年2月11日,centos服务器突然上行带宽8M,耗光所有带宽,不能远程SSH登录维护. 到机房直接使用界面登录,安装iptraf,运行后选择 Statistical breakdowns - ...
- 从MVC框架看MVC架构的设计
尽管MVC早已不是什么新鲜话题了,但是从近些年一些优秀MVC框架的设计上,我们还是会发现MVC在架构设计上的一些新亮点.本文将对传统MVC架构中的一些弊病进行解读,了解一些优秀MVC框架是如何化解这些 ...
- ORACLE SQL调优案例一则
收到监控告警日志文件(Alert)的作业发出的告警邮件,表空间TEMPSCM2不能扩展临时段,说明临时表空间已经被用完了,TEMPSCM2表空间不够用了 Dear All: The Instanc ...
- ERROR [org.apache.hadoop.security.UserGroupInformation] - PriviledgedActionExcep
换了个环境,出现此异常 016-10-18 23:54:01,334 WARN [org.apache.hadoop.util.NativeCodeLoader] - Unable to load n ...
- 记录一些在用wcf的过程中走过的泥巴路 【第一篇】
自从转移战场之后,比以前忙多了,博客也没能及时跟上,原本准备继续mvc系列,但是在那边技术比较陈旧还没能用得上,话说有3年没接触这玩意了,东西也 都忘了差不多了,既然再次接触,我也就继续温习温习,记录 ...
- mysql-4 数据检索(2)
用通配符进行过滤 like操作符 %通配符 %可以匹配任意字符 SELECT prod_id , prod_name FROM products WHERE prod_name LIKE 'je ...
- C# 和 C++ 数据类型对照表
又要用C#调用C++写好的api函数,为了方便,将网上的数据类型做个整理,方便以后查找,以后遇到需要的在进行查找 C++ C# WORD ushort DWORD ui ...
- 记一次使用openrowset 的坑
前几天被老大训斥连openrowset 都不会用,然后我就去看了文档,想测试一下栗子~ openrowset 的具体语法我就不贴了,戳这里:https://msdn.microsoft.com/zh- ...
- Linux IPC udp/ip socket 编程
模型 #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include < ...