您的位置:首页 > Web前端 > HTML5

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

2016-01-26 10:00 513 查看
经过前面的制作,使用Egret的Wing很快完成了开始界面和选关卡界面,下面通常来说就是游戏界面,但此时界面切换和关卡数据还没有准备好,这次讲解界面的切换和关卡数据的解析。前面多次修改了Main.ts文件中startCreateScene的方法,这个方法就是当游戏的前置Loading全部完成之后,执行的开始方法,Main本身就是UI容器,所以直接Add进去做好的UI逻辑即可。

界面切换

这里涉及到一个界面切换的代码设计问题,以开始界面为例,当“开始游戏”按钮点击之后,应将开始界面移除,进入到选关卡界面,写法看起来简单,但是可能我们需要获取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());
}


现在运行起来看看跳转效果吧,其他的类似的界面同理可以用这种方法,

关卡数据处理

下面就要考虑游戏数据问题,游戏的数据已经做好了一个json文件:questions.json

将这个文件添加到资源中,并保证提前读取完毕,在给出的项目代码中,是放在了项目根目录下,拖进去的时候会自动复制到assets目录下面一份。

 class SceneLevels extends eui.Component {
//单例 private static shared: SceneLevels; public static Shared() { if(SceneLevels.shared == null) { SceneLevels.shared = new SceneLevels(); } return SceneLevels.shared; }
private btn_back: eui.Button;
private group_levels:eui.Group;
private img_arrow: eui.Image;
private sel_level: number = 0;
private LevelIcons:LevelIcon[] = [];
public constructor() {
super();
this.skinName = "src/Game/SceneLevelsSkin.exml";
this.btn_back.addEventListener(egret.TouchEvent.TOUCH_TAP,this.onclick_back,this);
//创建地图选项
var row = 20;
var col = 10;
var spanx = 720 / col; //计算行x间隔
var spany = 1136 / row; //计算列y间隔
var group = new eui.Group();//地图背景
group.width = 720;
group.height = (spany * 400 );//算出最大尺寸
//填充背景
for(var i = 0;i <= (group.height / 1138) ;i++) {
var img = new eui.Image();
img.source = RES.getRes("GameBG2_jpg");
img.y = i * 1138;
img.touchEnabled = false;
this.group_levels.addChildAt(img,0);
}
//以正弦曲线绘制关卡图标的路径
var milestone: number = LevelDataManager.Shared().Milestone;
for(var i = 0; i<400;i++){
var icon = new LevelIcon();
icon.Level = i + 1;
icon.y = spany * i /2;
icon.x = Math.sin(icon.y / 180 * Math.PI) * 200 + group.width / 2;
icon.y += spany * i /2;
icon.y = group.height - icon.y - spany;
group.addChild(icon);
icon.addEventListener(egret.TouchEvent.TOUCH_TAP,this.onclick_level,this);
//依据进度设置关卡显示
icon.enabled = i < milestone;
//保存到一个列表中
this.LevelIcons.push(icon);
}
//开启位图缓存模式
group.cacheAsBitmap = true;
this.group_levels.addChild(group);
//卷动到最底层
this.group_levels.scrollV = group.height - 1100;
//跟踪箭头
this.img_arrow = new eui.Image();
this.img_arrow.source = RES.getRes("PageDownBtn_png");
this.img_arrow.anchorOffsetX = 124 / 2 - group.getChildAt(0).width / 2;
this.img_arrow.anchorOffsetY = 76;
this.img_arrow.touchEnabled = false;
this.img_arrow.x = group.getChildAt(0).x;
this.img_arrow.y = group.getChildAt(0).y;
group.addChild(this.img_arrow);

}
private onclick_back() { this.parent.addChild(SceneBegin.Shared()); this.parent.removeChild(this); }
private onclick_level(e:egret.TouchEvent){
var icon = <LevelIcon>e.currentTarget;
if(this.sel_level != icon.Level){
this.img_arrow.x = icon.x;
this.img_arrow.y = icon.y;
this.sel_level = icon.Level;
}else{
//进入并开始游戏
}
}
//打开指定的关卡,如果大于最远关卡,则保存数据也跟着调整
public OpenLevel(level:number){
var icon = this.LevelIcons[level - 1];
icon.enabled = true;
if(level > LevelDataManager.Shared().Milestone){
LevelDataManager.Shared().Milestone = level;
//同时将选定标记置于其上
this.img_arrow.x = icon.x;
this.img_arrow.y = icon.y;
this.sel_level = icon.Level;
}
}
}


SceneLevels.ts
对于这里简单讲一下,参看第47行,在创建图标的时候,我们将通过判断当前的进度来决定按钮的开启状态,71行的onclick_level()方法增加了一个当前选择sel_level变量来帮助完成选定后进入的关卡才开始游戏,最后,OpenLevel是为了将来做准备,当关卡完成后就会开启对应关卡icon。

数据创建优化

现在可以试试运行一下看看效果,但仍然不完美,因为当在开始界面点开始的时候,就会产生一次卡顿,是因为解析JSON数据源造成的,要知道有400多道题目,光json文件就有100多KB,卡顿是必然发生的事情,优化的方法有以下几种:

1、展现层面,在Loading的时候进行处理,如在资源读取完成之后,调用一下LevelDataManager.Shared(),这样就得要求预读

2、将数据源拆分,分开加载处理,50个题目一个文件,或者1道题1个文件,我的连连看数据就是这样处理

3、将数据源变成源码的一部分,如.ts或.js,这种方式读取处理效率倒是提升了,但是维护起来比较麻烦

本篇项目源码:ChengyuTiaozhan2.zip(由于博客园的文件大小限制,resource资源方面请到第二篇的后面下载

本篇主要学习使用了单例设计模式、JSON数据使用、egret.localStorage储存游戏进度数据。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: