本次來講一說如何利用lufylegend.js引擎製做一款html5遊戲後將其經過Qt轉換成EXE程序。步驟其實很是簡單,接下來就一步步地作一下解釋和說明。javascript
首先咱們來開發一個有點相似於太空大戰的遊戲,遊戲截圖以下:html
遊戲介紹:這個遊戲本來是七十一霧央前輩用Cocos2d-x開發的android小遊戲。因爲我看到這個遊戲實現起來比較簡單,所以就把apk下載下來,而且在霧央的指導下,把它當rar壓縮文件解開了,把素材偷走了……嘿嘿。因爲我最近的開發涉及html5領域,所以就用html5+lufylegend.js把這個遊戲移植到瀏覽器平臺上了。固然,效率不能和霧央的原版遊戲比,由於html5的效率衆所周知是很低的。html5
操做說明:用鼠標點擊界面,發出子彈攻擊迎面飛來的敵人。java
遊戲目標:不放過任何一個迎面飛來的敵人。android
遊戲測試地址:c++
http://www.cnblogs.com/yorhom/articles/3274940.html
api
注:演示地址中沒有背景音樂,是由於我以爲音樂太佔空間了,因此去掉了。下載包裏含有音樂,各位能夠欣賞一下。另外也感謝一下霧央兄弟,感謝他給我提供那麼好,那麼多的素材。瀏覽器
接下來就來講說這個遊戲的製做步驟。dom
首先你須要下載lufylegend.js遊戲引擎。這個引擎是一個html5開源庫件,利用他能夠仿照了As 3.0的語法進行html5開發,使用起來很是方便。固然,你說你不是flasher,不懂As 3.0,那也無妨,能夠參照官方API文檔進行學習。具體的介紹仍是去官方網站看看吧,省得lufy說我亂介紹他的引擎,嘿嘿~函數
引擎官方網站:
http://lufylegend.com/lufylegend
引擎API文檔:
http://lufylegend.com/lufylegend/api
由於本次開發要用到這個引擎,因此各位先看看這個引擎的一些API介紹吧,避免文中用到的一些API大夥看不懂。
首先要讀取一下游戲中的數據。本次開發要用到的數據以下:
/**加載變量*/ var loadData = [ {path:"./js/Bullet.js",type:"js"}, {path:"./js/Plain.js",type:"js"}, {path:"./js/Background.js",type:"js"}, {name:"bullet",path:"./images/bullet.png"}, {name:"sky.1",path:"./images/gamebg0.png"}, {name:"sky.2",path:"./images/gamebg1.png"}, {name:"over_text",path:"./images/gameover.png"}, {name:"player",path:"./images/hero.png"}, {name:"monster",path:"./images/monster.png"}, {name:"over_bg",path:"./images/overbg.png"}, {name:"start_bg",path:"./images/startbg.png"}, {name:"start_button_normal",path:"./images/startNormal.png"}, {name:"start_button_selected",path:"./images/startSelected.png"} ];
因爲加載完成後要保存這些加載好的數據,因此還要用一個變量:
var datalist;
接下來把一些定義的變量放在下面,都寫了註釋,你們慢慢看喔~
/**層變量*/ var backLayer, plainLayer, enemyLayer, bulletLayer, textLayer, loadingLayer; /**分數變量*/ var score; /**頻率變量*/ var maxFrame = 30; var frameIndex = 0; /**遊戲進行時間*/ var gameTime; /**對象變量*/ //顯示分數對象 var scoreText; //玩家 var player; //音樂對象 var startMusic, overMusic, playingMusic, dieMusic;
而後用到init初始化遊戲,由於遊戲是要跨平臺的,因此要在手機上全屏顯示,爲了實現這些,咱們在Main.js頂部加入以下的代碼:
//設置全屏 LSystem.screen(LStage.FULL_SCREEN); //初始化遊戲 init(30,"mylegend",800,480,main);
init的用法和LSystem.screen的用法均可以參照API文檔。
接下來咱們來看看main函數,這個函數是用來加載圖片和設置一些信息用的,好比開啓debug模式等,代碼以下:
function main(){ //設置debug模式 LStage.setDebug(true); //若是是移動端,就將body標籤margin調爲0px 0px 0px 0px if(LStage.canTouch == true){ document.body.style.margin = "0px 0px 0px 0px"; } //初始化加載層 loadingLayer = new LoadingSample3(); addChild(loadingLayer); //加載遊戲數據 LLoadManage.load( loadData, function(progress){ //顯示加載進度 loadingLayer.setProgress(progress); }, gameInit ); }
上面的代碼中,用了庫件中的LLoadManage類讀取,這個類能夠讀取js文件和圖片、音頻文件等,你們能夠本身去看看API文檔。
接下來看看gameInit裏的代碼,這個函數是用來保存加載數據,加入音樂,加入開始場景用的。代碼以下:
function gameInit(result){ //保存加載的數據 datalist = result; //清空加載層 removeChild(loadingLayer); //加入地板層 backLayer = new LSprite(); addChild(backLayer); //初始化音樂 initMusic(); //加入開始界面 addStartPage(); }
其中調用到的函數代碼分別以下:
function initMusic(){ //開場音樂 startMusic = new LSound("./music/startbg.mp3"); //結束音樂 overMusic = new LSound("./music/overbg.mp3"); //遊戲開始後的音樂 playingMusic = new LSound("./music/gamebg.wav"); //死亡後的音樂 dieMusic = new LSound("./music/die.wav"); } function addStartPage(){ //播放音樂 startMusic.play(0,100000000000000000000000000000000); //加入背景 var bitmapData = new LBitmapData(datalist["start_bg"]); var bitmap = new LBitmap(bitmapData); backLayer.addChild(bitmap); //按鈕普通時的樣式 var normalBtnStyleData = new LBitmapData(datalist["start_button_normal"]); var normalBtnStyle = new LBitmap(normalBtnStyleData); //按鈕盤旋時的樣式 var selectedBtnStyleData = new LBitmapData(datalist["start_button_selected"]); var selectedBtnStyle = new LBitmap(selectedBtnStyleData); //加入開始按鈕 var startBtn = new LButton(normalBtnStyle,selectedBtnStyle); startBtn.x = (LStage.width-startBtn.getWidth())*0.5; startBtn.y = (LStage.height-startBtn.getHeight())*0.5; backLayer.addChild(startBtn); //加入開始事件 startBtn.addEventListener(LMouseEvent.MOUSE_DOWN,startGame); }
代碼都加上了註釋,能夠參照API文檔看看。在上面的addStartPage代碼中,加入一個開始按鈕後,咱們給這個按鈕加了一個鼠標事件,這個事件是用來觸發遊戲開始用的。開始遊戲咱們用的是startGame函數,代碼以下:
function startGame(event){ //清空界面 backLayer.removeAllChild(); //分數調零 score = 0; //遊戲時間調零 gameTime = 0; //中止開始界面的音樂音樂 startMusic.close(); //播放遊戲進行中的音樂 playingMusic.play(0,100000000000000000000000000000000); //加入滾動背景 var background = new Background(); backLayer.addChild(background); //初始化層變量 initLayer(); //加入玩家飛機 player = new LBitmap(new LBitmapData(datalist["player"])); player.x = 20; player.y = (LStage.height-player.getHeight())*0.5 plainLayer.addChild(player); //加入分數文字 addText(); //加入事件 backLayer.addEventListener(LMouseEvent.MOUSE_DOWN,onmousedown); backLayer.addEventListener(LEvent.ENTER_FRAME,onframe); }
這個函數中咱們首先初始化一些值,好比分數和遊戲時間。在這裏順便說說這個遊戲時間有什麼用:由於咱們的遊戲隨着時間的推動,難度應該愈來愈難,因此咱們要保存下這個時間,方便之後的計算。
在startGame中,咱們爲了加入一個滾動的背景,咱們用到了一個Background類。這個類代碼以下:
/** * Background.js * @author Yorhom * @date 2013/8/10/23:42 */ function Background(){ var s = this; base(s,LSprite,[]); //設置移動速度 s.speed = 10; var skyBitmapData1 = new LBitmapData(datalist["sky.1"]); var skyBitmapData2 = new LBitmapData(datalist["sky.2"]); //記錄每塊背景的長度 s.lastObjX = skyBitmapData1.image.width; //實例化第一塊背景 s.skyBitmap1 = new LBitmap(skyBitmapData1); //實例化第二塊背景 s.skyBitmap2 = new LBitmap(skyBitmapData2); //將第二塊背景移動到第一塊的後面 s.skyBitmap2.x = s.lastObjX; //加入兩塊背景 s.addChild(s.skyBitmap1); s.addChild(s.skyBitmap2); //加入時間軸事件 s.addEventListener(LEvent.ENTER_FRAME,s.run); } Background.prototype.run = function(s){ //將背景向前移動 s.skyBitmap1.x -= s.speed; s.skyBitmap2.x -= s.speed; //若是第一塊背景移除到屏幕以外就移到另外一塊的後面 if(s.skyBitmap1.x < -1 * s.lastObjX){ s.skyBitmap1.x = s.skyBitmap2.x + s.lastObjX; } //若是第二塊背景移除到屏幕以外就移到另外一塊的後面 if(s.skyBitmap2.x < -1 * s.lastObjX){ s.skyBitmap2.x = s.skyBitmap1.x + s.lastObjX; } };
原理很簡單,就是先將第二塊背景接到第一塊背景的後面,若是第一塊背景已經移除屏幕了,那就把第一塊背景加到第二塊後面,以此類推,就實現了畫面不段移動的感受。
在startGame函數中,咱們還用到了實例化層的函數initLayer(),這個函數代碼以下:
function initLayer(){ //加入飛機層 plainLayer = new LSprite(); backLayer.addChild(plainLayer); //加入敵機層 enemyLayer = new LSprite(); backLayer.addChild(enemyLayer); //加入子彈 bulletLayer = new LSprite(); backLayer.addChild(bulletLayer); //加入文字層 textLayer = new LSprite(); backLayer.addChild(textLayer); }
另外還有個addText函數,負責顯示分數文字用的。
function addText(){ //實例化LTextField對象 scoreText = new LTextField(); scoreText.font = "Tekton Pro"; scoreText.size = 20; scoreText.text = "Score: " + score; scoreText.x = LStage.width - scoreText.getWidth() - 20; scoreText.y = 20; //加到顯示層中 textLayer.addChild(scoreText); }
還有就是在startGame中加的兩個事件:時間軸事件,鼠標點擊事件。鼠標事件觸發的函數代碼以下:
function onmousedown(event){ //計算子彈飛出的角度 var height = (player.y + player.getHeight()*0.5) - event.offsetY; var width = event.offsetX - (player.x + player.getWidth()*0.5); var angle = Math.atan2(height,width); //實例化一個子彈 var bullet = new Bullet(angle); bulletLayer.addChild(bullet); }
代碼很簡單,就是先取出當前點擊位置離人物的寬度與高度,而後經過Math.atan2算出這個點與玩家飛機的直線距離和經過玩家飛機的水平直線的夾角度數,並將這個值當參數傳入Bullet類中。Bullet類代碼以下:
/** * Bullet.js * @author Yorhom * @date 2013/8/12/21:14 */ function Bullet(angle){ var s = this; base(s,LSprite,[]); //計算子彈角度 s._angle = angle * 180 / Math.PI; //保存子彈移動速度 s._speed = 10; //保存當前子彈到玩家飛機的距離 s._r = 0; //計算出初始位置 s.x = player.x + player.getWidth()*0.5; s.y = player.y + player.getHeight()*0.5; //保存初始位置 s._startX = s.x; s._startY = s.y; //添加子彈對象 var bitmapData = new LBitmapData(datalist["bullet"]); s.bitmap = new LBitmap(bitmapData); s.addChild(s.bitmap); //添加射擊時的音頻對象 var attackMusic = new LSound("./music/attack.wav"); attackMusic.play(); } Bullet.prototype.onframe = function(){ var s = this; //更改當前子彈到玩家飛機的距離 s._r += s._speed; //計算y軸移動距離 var speedy = Math.sin(s._angle * Math.PI / 180) * s._r; //計算x軸移動距離 var speedx = Math.cos(s._angle * Math.PI / 180) * s._r; //更改子彈位置 s.x = s._startX + speedx; s.y = s._startY - speedy; };
這個類主要負責顯示一個子彈,而且讓子彈往點擊的方向飛去。顯示一個子彈就是用一個LBitmap來實現。移動子彈的原理就是先把子彈到玩家飛機的直線距離設置爲0,而後每當要移動子彈時,就將這個距離先加上移動速度,找到要到的位置,而後經過傳進來的那個角度參數配合Math.cos和Math.sin算出要到的位置的x,y座標,而後讓子彈移動到那個位置上去。這個對於大夥兒應該很簡單,但對於我這個只有初二水平的學生來講,連cos和sin都沒學過,查了不少資料才搞出來的。
上面還提到了時間軸事件,觸發的函數以下:
function onframe(){ //增長遊戲時間 gameTime ++; //添加敵人 if(frameIndex > maxFrame){ var enemy = new Plain(); enemyLayer.addChild(enemy); frameIndex = 0; }else{ frameIndex ++; } //移除敵人 for(var key in enemyLayer.childList){ if(enemyLayer.childList[key].mode == "die"){ enemyLayer.removeChild(enemyLayer.childList[key]); //增長分數 score += 10; //顯示新分數 changeText(); return; } if(enemyLayer.childList[key].mode == "complete"){ gameOver(); enemyLayer.removeChild(enemyLayer.childList[key]); } } //移除飛出屏幕的子彈 for(var key in bulletLayer.childList){ bulletLayer.childList[key].onframe(); if( bulletLayer.childList[key].x > LStage.width || bulletLayer.childList[key].x < 0 || bulletLayer.childList[key].y < 0 || bulletLayer.childList[key].y > LStage.height ){ bulletLayer.removeChild(bulletLayer.childList[key]); } } }
每段代碼都加了註釋,結合API文檔看一些就能明白的。其中有個Plain類,這個是一個用來實現敵機的類,包括敵機移動,檢測碰撞等,代碼以下:
/** * Plain.js * @author Yorhom * @date 2013/8/15/12:10 */ function Plain(){ var s = this; base(s,LSprite,[]); //設置飛機移動速度 s.speed = Math.floor(gameTime/100) + 7; s.mode = ""; //添加敵人的圖片 var bitmapData = new LBitmapData(datalist["monster"]); s._bitmap = new LBitmap(bitmapData); s.x = LStage.width + s._bitmap.getWidth(); s.y = Math.floor(Math.random()*(LStage.height-s._bitmap.getHeight())); s.addChild(s._bitmap); //經過時間軸事件實現不斷移動 s.addEventListener(LEvent.ENTER_FRAME,s.run); } Plain.prototype.run = function(s){ //移動飛機對象 s.x -= s.speed; //檢測碰撞 s.checkHit(); //判斷是否移除屏幕。若是是,就將mode屬性設置爲"complete" if(s.x < -1 * s.getWidth()){ s.mode = "complete"; } }; Plain.prototype.checkHit = function(){ var s = this; //判斷碰撞 for(var key in bulletLayer.childList){ if(LStage.hitTestArc(s,bulletLayer.childList[key])){ //將mode屬性改成"die" s.mode = "die"; //移除碰撞子彈 bulletLayer.removeChild(bulletLayer.childList[key]); } } };
能夠在上面的構造器代碼中看到,咱們經過遊戲時間變量gameTime計算了飛機移動的速度,達到改變遊戲的難度。其餘的代碼就直接看註釋和API文檔就能看懂。
實現了這個類,咱們的遊戲基本上就搞定了。不過還有些細節部分不可忽視。
爲了移除一些對象避免效率低下,咱們在onframe中加入了移除對象的功能。爲了實現這個功能,咱們遍歷了每一個飛機對象,而後判斷遍歷到的飛機對象的mode屬性是否爲die,若是是,就移除掉。在onframe中,實現這個效果的代碼以下:
//移除敵人 for(var key in enemyLayer.childList){ if(enemyLayer.childList[key].mode == "die"){ enemyLayer.removeChild(enemyLayer.childList[key]); //增長分數 score += 10; //顯示新分數 changeText(); return; } if(enemyLayer.childList[key].mode == "complete"){ gameOver(); enemyLayer.removeChild(enemyLayer.childList[key]); } } //移除飛出屏幕的子彈 for(var key in bulletLayer.childList){ bulletLayer.childList[key].onframe(); if( bulletLayer.childList[key].x > LStage.width || bulletLayer.childList[key].x < 0 || bulletLayer.childList[key].y < 0 || bulletLayer.childList[key].y > LStage.height ){ bulletLayer.removeChild(bulletLayer.childList[key]); } }
爲了及時更改分數,咱們在時間軸事件中還加入了調用changeText函數。代碼以下:
function changeText(){ //更改顯示文字 scoreText.text = "Score: " + score; //更改文字座標 scoreText.x = LStage.width - scoreText.getWidth() - 20; }
還有就是遊戲結束時調用的代碼,以下:
function gameOver(){ //消除事件 backLayer.die(); var bitmap; //加入遊戲結束層 var gameOverLayer = new LSprite(); backLayer.addChild(gameOverLayer); //加入背景 bitmap = new LBitmap(new LBitmapData(datalist["over_bg"])); gameOverLayer.addChild(bitmap); //加入文字 bitmap = new LBitmap(new LBitmapData(datalist["over_text"])); bitmap.x = (LStage.width - bitmap.getWidth()) * 0.5; bitmap.y = (LStage.height - bitmap.getHeight()) * 0.5; gameOverLayer.addChild(bitmap); //將遊戲結束層移除屏幕 gameOverLayer.y = -1 * gameOverLayer.getHeight(); //經過緩動將遊戲結束層移到屏幕上 LTweenLite.to(gameOverLayer,0.7,{ y:0, ease:Quad.easeInOut, onComplete:function(){ //加入鼠標事件,來應對遊戲重開 backLayer.addEventListener(LMouseEvent.MOUSE_DOWN,restart); } }); //關掉遊戲進行中的音樂 playingMusic.close(); //播放遊戲結束時候的音樂 dieMusic.play(0,1); overMusic.play(0,100000000000000000000000000000000); }
最後就是遊戲重開函數:
function restart(){ //清除界面 backLayer.die(); backLayer.removeAllChild(); //關掉遊戲結束時候的音樂 overMusic.close(); //開始遊戲 startGame(); }
上面基本上把整個遊戲製做過程簡略地講了一遍,代碼講解有點不詳細,你們能夠結合註釋看看。另外若是有感興趣的朋友,能夠到下面連接裏下載。下載包裏還有我打包好的apk文件,你們能夠在手機上玩玩。打包apk的話,能夠看看這篇文章:《用HTML5來開發一款android本地化App遊戲-寶石碰碰》
源代碼下載地址:http://files.cnblogs.com/yorhom/SpaceWar.rar
下載和安裝Qt等一些基礎的東西這裏就很少說了,Google一下或者百度一下就能夠了。接下來就直接講方法。
首先你須要在Qt Creater中建立一個Qt項目,建立項目的方法以下。
首先點開File->New File or Project,出現如下對話框,選擇如圖所示的幾個選項:
點擊Choose...按鈕,進入以下界面:
上面的信息隨便添就能夠。點擊Next,出現以下界面:
山面是在選擇配置,根據本身下載的選擇一些就ok,點擊Next繼續。出現以下界面
按照上面的添法填寫好後,再繼續按下Next,進入下一個界面。
而後按下Finish就已經建立好項目了。獲得如下的目錄樹,你們能夠看看操做對沒有:
而後把裝有html5遊戲的文件夾複製到執行文件目錄下。以下圖:
注意:貌似遊戲加了音樂就會運行不出來了,把有用到音樂的地方所有刪掉就ok沒事了
打開mainwindow.h,寫入如下代碼:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); }; #endif // MAINWINDOW_H
接着在mianwindow.cpp加入如下代碼:
#include <QtWebKit/QWebView> #include "mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { setWindowTitle(QString("")); setMaximumSize(QSize(800, 480)); setMinimumSize(QSize(800, 480)); showMaximized(); setWindowIcon(QIcon("./SpaceWar/images/logo.jpg")); QWebView *pWebView = new QWebView(this); setCentralWidget(pWebView); pWebView->load(QUrl("./SpaceWar/index.html")); } MainWindow::~MainWindow() { }
再打開main.cpp,加入如下代碼:
#include <QApplication> #include "mainwindow.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
按下如圖所示的按鈕運行程序:
運行效果以下:
最後要發佈的時候,要找到幾個dll,以下:
----------------------------------------------------------------
歡迎你們轉載個人文章。
轉載請註明:轉自Yorhom's Game Box
http://blog.csdn.net/yorhomwang
歡迎繼續關注個人博客