用Html5结合Qt制作一款本地化EXE游戏-太空大战(Space War)
本次来说一说如何利用lufylegend.js引擎制作一款html5游戏后将其通过Qt转换成EXE程序。步骤其实非常简单,接下来就一步步地做一下解释和说明。
首先我们来开发一个有点类似于太空大战的游戏,游戏截图如下:
游戏介绍:这个游戏原本是七十一雾央前辈用Cocos2d-x开发的android小游戏。由于我看到这个游戏实现起来比较简单,因此就把apk下载下来,并且在雾央的指导下,把它当rar压缩文件解开了,把素材偷走了……嘿嘿。由于我最近的开发涉及html5领域,因此就用html5+lufylegend.js把这个游戏移植到浏览器平台上了。当然,效率不能和雾央的原版游戏比,因为html5的效率众所周知是很低的。
操作说明:用鼠标点击界面,发出子弹攻击迎面飞来的敌人。
游戏目标:不放过任何一个迎面飞来的敌人。
游戏测试地址:
http://www.cnblogs.com/yorhom/articles/3274940.html
注:演示地址中没有背景音乐,是因为我觉得音乐太占空间了,所以去掉了。下载包里含有音乐,各位可以欣赏一下。另外也感谢一下雾央兄弟,感谢他给我提供那么好,那么多的素材。
接下来就来说说这个游戏的制作步骤。
准备工作
首先你需要下载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 0pxif(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.5plainLayer.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实现本地化EXE游戏
下载和安装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_OBJECTpublic: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("file:///"+QUrl(QFileInfo("./SpaceWar/index.html").absoluteFilePath()));
}MainWindow::~MainWindow()
{}
注意,QUrl里面的路径要是绝对路径,并且要在取出的绝对路径前面加上file:///。
再打开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,如下:
- libgcc_s_dw2-1.dll
- libstdc++-6.dll
- QtCore4.dll
- QtGui4.dll
- QtNetwork4.dll
- QtWebKit4.dll
----------------------------------------------------------------
欢迎大家转载我的文章。
转载请注明:转自Yorhom's Game Box
http://blog.csdn.net/yorhomwang
欢迎继续关注我的博客
用Html5结合Qt制作一款本地化EXE游戏-太空大战(Space War)相关推荐
- 如何零基础制作一款自己的游戏!(一)
如何零基础制作一款游戏(一) 文章目录 如何零基础制作一款游戏(一) 前言 一.软件下载以及创建工程 二.使用步骤 1.进入工程 2.设置更改 3.更改界面 4.脚本更改 5.下载插件 6.如何设置障 ...
- 零基础学CocosCreator·第七季-制作一款塔防游戏
第七季-制作一款塔防游戏 01.塔防前言 为什么是塔防? 准备 02.使用TileMap创建地图 新建地图 获取地图 编辑地图 代码操控 运行 03-16.实战中 04.状态机 代码 08.事件分发器 ...
- 用html做个猜字游戏,HTML5 Canvas API制作简单的猜字游戏
这篇文章主要介绍了借助HTML5 Canvas API制作一个简单的猜字游戏的实例分享,游戏中每局会自动生成一个字母,玩家按键盘来猜测该字母是哪一个,需要的朋友可以参考下 二话不说,先上效果图以及源代 ...
- 如何使用Python制作一款国际象棋小游戏
如果你想使用 Python 制作国际象棋游戏,可以使用如下步骤: 设计棋盘:可以使用一个二维数组来存储棋盘上每一个位置的棋子. 实现棋子移动:可以使用一个函数来处理棋子的移动,并在棋子移动过程中进行限 ...
- html5的canvas制作口红机闯关游戏(一)
目的 最近实践课让随便做小项目,准备学习下h5的canvas画布功能,参考canvas实现的见缝插针游戏,来制作一个口红机闯关小游戏,学习实践过程问题记录和canvas功能学习整理. 什么是canva ...
- 如何制作一款盗墓类游戏
我非常喜欢鬼吹灯系列小说,不仅是看完了全部的原著,各种有声小说和电影电视都是看过了的.从文字到影视,都给了我们不一样的鬼吹灯视角,但是唯独没有在游戏上得以体现.于是乎,我就有了一个想法,能不能做一个纯 ...
- 制作一款3D炸弹超人游戏
说起炸弹超人,相信很多朋友都玩过类似的游戏,其中最为人熟知的莫过于<泡泡堂>.该类型游戏需要玩家在地图中一边跑动一边放置炸弹,同时还要躲避敌方炸弹保护自己.最初的炸弹超人游戏都是2D的,今 ...
- 制作一款Jframe窗口游戏原来这么简单,我抚摸着光头禁不住惊叹!!开始、暂停、重新开始事件必须有,线程也加入下充充面子,背景更换和移动也需要,一首背景音乐当然更适合游戏。
Jframe制作打字游戏 设计思路和流程 为什么设计这个玩意 一.初始设计分析 窗口的建立和实现显示字母 二.进阶设计分析 1.打字游戏基本实现流程的设计 2.键盘注册实现消除字母和重新生成 三.优化 ...
- unity制作一款塔防游戏
文章目录 介绍 寻路系统 怪物生成器 制作3种初级炮台.3种升级炮台 设置炮台属性 选择炮台,添加监听事件 炮弹追踪攻击敌人 拖动鼠标实现相机视角转换 鼠标光标放在cube上变色 文字动画 介绍 关键 ...
最新文章
- java 反射 注解 运用_Java注解与反射的使用
- java创建node类型数据类型_[Java教程]js DOM Node类型
- 如何“主动出击”提升网站的收录速度?
- 下载并导出数据到execl中
- OpenCV DIS光流OpticalFlow的实例(附完整代码
- 设计一个类代表二维空间的一个圆。_平面设计基础——点、线、面
- Java并发编程之线程池ThreadPoolExecutor解析
- 论信息化投标低于1元中标值吗?
- 教你如何开发一款实用的完整Android App
- android 动态权限推送图标问题,「JPush」通知栏相关问题
- mysql数据库修改排序规则
- 003 python 注释/数据类型/运算符/输入输出/格式化输出
- WPF 视频教程+笔记
- 易语言单窗口单ip软件源码_想要挣钱创收 那就用脚本操作手机群控软件啊
- 干货丨面向新基建,安全厂商都准备了哪些前瞻性的“武器”?
- 聚美优品广告词和经典分析
- 【树链剖分】月下毛景树
- CVPR 2016 摘要阅读
- Flink实操 : 算子操作
- Pytorch实战——基于RNN的新闻分类