GC DevKit 快速入門 -- 游戲概覽(二)
接上節 http://www.rzrgm.cn/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`監聽器來捕獲點擊與觸摸事件,最后,我們出發一個事件用以通知標題視圖已經運行。
## 事件流程
我們目前只看到如何捕獲用戶的輸入并傳遞給主程序,下面來介紹整個游戲的事件流程,之后會看到其余的事件

程序成功運行,并裝載好游戲后,用戶首先會進入標題視圖。單擊開始按鈕后`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.rzrgm.cn/hangxin1940/archive/2013/04/13/3017640.html
浙公網安備 33010602011771號