GC DevKit 快速入门 -- 游戏概览(二)
接上节 http://www.cnblogs.com/hangxin1940/archive/2013/04/11/3011555.html
## 创建屏幕视图
当导入屏幕视图类后,我们会在`initUI`函数中对它进行实例化,这时,游戏引擎就准备就绪了。
var titlescreen = new TitleScreen(),
gamescreen = new GameScreen();
之后的内容会详细介绍屏幕视图的构造的。
当游戏引擎将场景创建好后,它会被存储在 `GC.app.view` 的根节点。任何 `View` 只要附加到根节点上,都会被呈现到屏幕上,而根节点视图有些特殊,它是 `ui.StackView` 的实例,`ui.StackView` 又是 `ui.View` 的子类, 它另外的功能就是压入和弹出视图栈,并且做相应的转换。
这里还有一些声音相关的代码,我们将会在结尾看到详细的说明。`soundcontroller`模块会返回一个 `AudioManager` 的单例对象,当我们转到游戏视图时,它会播放关卡音乐。
## 事件管理
在事件处理代码中,我们在两个屏幕视图中监听了游戏开始与结束的事件,并且管理 `StackView`
titlescreen.on('titlescreen:start', function () {
//...
GC.app.view.push(gamescreen);
gamescreen.emit('app:start');
});
gamescreen.on('gamescreen:end', function () {
//...
GC.app.view.pop();
});
在收到游戏开始事件后,游戏视图会被压入`rootView`视图栈中,这里没有必要将已存在与试图栈中的标题视图删除,因为压入栈顶后这个游戏视图已经可见。默认情况下,压入另一个视图进入视图栈时会有一个横向滚动的动画,当然也可以关闭动画。下面我们通过标题视图来看看整个程序的事件流程的细节。
## 游戏等待状态: `TitleScreen.js`
标题视图是 `TitleScreen`类 的一个实例,在 `./src/TitleScreen.js` 文件中定义,在 `./src/Application.js` 中被实例化一次并加入到根书图,在整个生命周期中存在。
## 视图剖析
`TitleScreen`类的视图结构相对来说是比较简单的。它由一个图片来填充背景,以及被放置在背景中央并且看不见的一个视图,它被作为`Play`按钮。这个按钮将检测输入事件,然后向主程序发送一个事件并通知用户准备好开始游戏。下面来分析这个类:
import ui.View;
import ui.ImageView;
exports = Class(ui.ImageView, function (supr) {
this.init = function (opts) {
opts = merge(opts, {
x: 0,
y: 0,
image: "resources/images/title_screen.png"
});
supr(this, 'init', [opts]);
var startbutton = new ui.View({
superview: this,
x: 58,
y: 313,
width: 200,
height: 100
});
startbutton.on('InputSelect', bind(this, function () {
this.emit('titlescreen:start');
}));
};
});
首先,需要导入一下两个模块:
import ui.View;
import ui.ImageView;
`ui.View`类被用作一个基础的显示对象将元素渲染在屏幕上,要做到这点,一个视图必须得附加到游戏的场景中(视图树中的节点)。 视图具有样式属性,用来控制如何被渲染到屏幕上,并且可以触发和订阅事件,也可以增加/删除子视图或父视图。
`ui.ImageView` 类是 `ui.View`的子类,不但`ui.View`继承了父类的属性,而且还可以在视图中设置图像。
现在我们已经导入了所依赖的模块,现在可以定义我们的 `TitleScreen` 类。
exports = Class(ui.ImageView, function (supr) {
this.init = function (opts) {
opts = merge(opts, {
//...
});
supr(this, 'init', [opts]);
};
});
每一个类中定义的`init`方法都会在实例化的时候被执行,`merge`函数用来合并属性(犹如两个集合选取合集),用来将它合并的属性传递给构造函数。这之后,调用父类的`init`方法,并传入合并后的属性对象。
下面是完整的`init`方法:
this.init = function (opts) {
opts = merge(opts, {
x: 0,
y: 0,
image: "resources/images/title_screen.png"
});
supr(this, 'init', [opts]);
this.build();
};
`title_screen.png`将会作为 `ui.ImageView`类的`image`属性加载。`supr`函数将当前对象以参数形式传递给父类来进行初始化。这三个参数分别是当前对象,以及将要调用的当前对象的方法名,还有包含当前对象属性的数组。
## `Play`按钮
还记得在`init`结束时调用的这个`build`函数么:
this.build = function() {
var startbutton = new ui.View({
superview: this,
x: 58,
y: 313,
width: 200,
height: 100
});
startbutton.on('InputSelect', bind(this, function () {
this.emit('titlescreen:start');
}));
};
在标题视图中,我们创建了一个不可见的开始按钮,它位于背景图的中央,这个按钮视图通过`superview`属性附加于当前类,然后通过`InputSelect`监听器来捕获点击与触摸事件,最后,我们出发一个事件用以通知标题视图已经运行。
## 事件流程
我们目前只看到如何捕获用户的输入并传递给主程序,下面来介绍整个游戏的事件流程,之后会看到其余的事件
![devkit](http://docs.gameclosure.com/guide/assets/game-walkthrough/game-event-flow.png "devkit")
程序成功运行,并装载好游戏后,用户首先会进入标题视图。单击开始按钮后`titlescreen:start`事件会被发出,并被上层的程序捕获,在那里,游戏视图将会加载,之后,用户开完游戏,直到 `gamescreen:end` 事件发出,上层代码删除游戏试图,最终又回到标题视图。
## 进行游戏: `GameScreen.js`
`GameScreen`类定义在 `./src/GameScreen.js` 文件,它也是项目中最长的代码了。其中大部分代码都是在构建视图结构,我们在前面已经了解其中的一些细节。除了建立子视图与游戏资源,它还定义了游戏逻辑相关的函数以及游戏结束时显示得分的方法。后面我们将会看到比较重要的一些代码。
## 设置屏幕
与之前的代码一样,首先会导入一些模块
import animate;
import device;
import ui.View;
import ui.ImageView;
import ui.TextView;
import src.MoleHill as MoleHill;
之前已经讲过 `ui.View`, `ui.ImageView`, 与 `device`,现在主要看看其他的模块。
`ui.TextView`的作用,应该很容易猜到,它主要是显示文字,除了常规试图的样式之外,还可以对它进行字号与颜色的设置。
`animate`模块主要用来为视图、对象以及样式生成动画,它所生成的是`补间动画`,也就在移动位置之间进行插值,以生成动画。更重要的是,它针对原生设备进行了专门的优化,我们应该在游戏中善加利用此模块,而不是手动的进行额外的计算。之后的代码中我们会遇到它,到时候在进行详细的说明。
最后的一个模块导入语句比较有意思:
import src.MoleHill as MoleHill;
`src.MoleHill`类引用了工程目录中的`./src/MoleHill.js` 文件。除了Devkit引擎的模块与类之外,我们还可以自己定义,并且被项目导入并引用。`as`语句用来给模块或类起个别名,方便我们更加方便的引用,不必输入繁长的路径。
同`TitleScreen`类相同,`GameScreen`也只在`Application.js`中被实例化一次,`init`函数用来定义尺寸以进行屏幕适配,并且将绿草地填充为背景,通过`supr`函数为父类传递构造属性。
this.init = function (opts) {
opts = merge(opts, {
x: 0,
y: 0,
width: 320,
height: 480,
backgroundColor: '#37B34A'
});
supr(this, 'init', [opts]);
this.build();
};
this.build = function() {
this.on('app:start', bind(this, start_game_flow));
this._scoreboard = new ui.TextView({
superview: this,
x: 0,
y: 15,
width: device.width,
height: 50,
autoSize: false,
size: 38,
verticalAlign: 'middle',
textAlign: 'center',
multiline: false,
color: '#fff'
});
var x_offset = 5;
var y_offset = 160;
var y_pad = 25;
var layout = [[1, 0, 1], [0, 1, 0], [1, 0, 1]];
this._molehills = [];
//循环,布局网格,先是行然后是列
for (var row = 0, len = layout.length; row < len; row++) {
for (var col = 0; col < len; col++) {
//如果是1,则创建一个鼹鼠
if (layout[row][col] !== 0) {
var molehill = new MoleHill();
molehill.style.x = x_offset + col * molehill.style.width;
molehill.style.y = y_offset + row * (molehill.style.height + y_pad);
this.addSubview(molehill);
this._molehills.push(molehill);
//打到鼹鼠时,更新得分
molehill.on('molehill:hit', bind(this, function () {
if (game_on) {
score = score + hit_value;
this._scoreboard.setText(score.toString());
}
}));
}
}
}
this._countdown = new ui.TextView({
superview: this._scoreboard,
visible: false,
x: 260,
y: -5,
width: 50,
height: 50,
size: 24,
color: '#fff',
opacity: 0.7
});
};
`build`函数的代码比之前看到过的稍微有点长,但是它很容易看懂。首先,在启动游戏时监听`app:start`事件,`app:start`是游戏的根所派发的事件,当开始按钮被处理后,会执行`start_game_flow`函数。
定义好用户得分牌后,会在屏幕上定义鼹鼠洞网格以及定义它们的位置与大小,这里创建了一定数量的`MoleHill`对象,将它们作为子视图附加到`GameScreen`对象,并且为每一个`MoleHill`对象都注册了敲打事件用以更新用户得分。
`MoleHill`类定义在工程中的`./src/MoleHill.js`文件,稍后会说到。基本上它是一个图像的集合,用来生成鼹鼠出洞,敲头等复合图像,以及动画相关的代码。
最后,创建一个倒计时文本,作为得分牌的子视图。
GC DevKit 快速入门 -- 游戏概览(三) http://www.cnblogs.com/hangxin1940/archive/2013/04/13/3017640.html