qml demo分析(samegame-拼图游戏)
一、效果展示
相信大家都玩儿过连连看游戏,而且此款游戏也是闲时一款打发时间的趣事,那么接下来我将分析一款类似的游戏,完全使用qml编写界面,复杂逻辑使用js完成。由于此游戏包含4种游戏模式,因此本篇文章可能会比较长,本篇文章我主要是分析该游戏的主题思路和一些比较难理解的模块,文章末尾我会把示例代码的下载链接附上,示例代码是qt5.7示例代码,位于Qt5.7.0_vs2013\Examples\Qt-5.7\quick\demos\samegame目录下,个人知识添加了大量注释在程序中,逻辑几乎没有修改。
如图1所示,是这个游戏简单的示意,要想展示完全合格游戏的所有细节,仅仅靠这一个小的gif图是完全不够的,因此本篇文章不会和以往的示例分析那样,在源码分析过程中没有程序截图,在后续代码分析中我会适当的加入一些游戏过程中的截图。
图1 samgegame简单试玩
二、源码分析
细心的同学可能会发现在1 player模式下,程序出现了最高分的显示,呵呵呵。。。因为那是我玩儿的,这个游戏也使用了简单的本地数据库存储功能,在后续代码分析过程当中这一部分我会单独拉出来讲解。
还是同上一篇文章一样,我们还是先来整体分析下工程目录,文件看起来真是多啊,起初我看到这么多文件的时候自己也是挺难以下手,不过当我正真的理解这个程序的设计意图之后,想想就没有那么难了。
图2 工程目录
既然我已经把这个示例代码做了整体的理解,那么我就不会那么无脑的一个一个文件讲解,首先我要做的就是把这么写qml进行分类,分类完以后的工程目录就没有图2那么恐怖了,如图3所示是按功能点整理后的工程目录
图3 整理后工程目录
接下来我按文件夹分别说明该模块下的qml文件功能
- data:主要负责puzzle模式下提供关卡数据
- emitter:重写了一些例子发射器,每个例子发射器所属组不同,并且设置有不同的参数,例如:例子发射速率、例子生命周期等
- block:色块,分别存储了4种游戏模式下的色块
- control:封装了该示例代码公有的一部分组件,例如Button是封装过后的按钮,点击时可以发出clicked信号
- setting:程序设置,包括初始大小,一些基础布局信息
- 其余:剩下3个qml文件,也是游戏画面中比较关键的积分qml
1、页面布局
一般情况下qml主文件的名字都和工程名字相同,那么本篇示例程序的主程序文件就是samegame.qml文件无疑。由于该文件代码量过大,我就不整篇粘贴了,大家可以在自己安装的qt库的example目录下自行查找,如下代码所示是我对主程序文件进行了大量删减得出的主界面布局,程序中几乎每一行代码都有注释,包括删减的代码依然有大量注释,感兴趣的同学可到文章最后提供的链接进行下载
import QtQuick 2.0
import QtQuick.Particles 2.0
import "content/samegame.js" as Logic
import "content"//导入一个目录 目录下的所有组件可以直接被使用 Rectangle {
Image {//主窗口背景色 底部菜单栏和顶部信息面板都在该背景图之上
source: "content/gfx/background.png"
anchors.fill: parent
}
GameArea {//游戏画布
}
Item {//游戏启动界面 主菜单 除过底部工具栏以外部分
//G和S动画
LogoAnimation {
x:
y: Settings.headerHeight
particleSystem: gameCanvas.ps
running: root.state == ""//游戏未开始时运行
}
//Game和Same剩余的3个字母
Row {
x:
y:
Image { source: "content/gfx/logo-a.png" }//A
Image { source: "content/gfx/logo-m.png" }//M
Image { source: "content/gfx/logo-e.png" }//E
}
//四种游戏模式
Column {
spacing: Settings.menuButtonSpacing//在context目录下Settings组件中定义的属性
width: parent.width
height: parent.height - ( + Settings.footerHeight)// Button {//1 player
width: root.width
rotatedButton: true//按钮1支持旋转
imgSrc: "content/gfx/but-game-1.png"//根据导出属性指定图片地址(相对路径指定方式)
onClicked: {//自定义信号对于槽
if (root.state == "in-game")//如果游戏中则什么事情都不干
return //Prevent double clicking
root.state = "in-game"//游戏开始后 将游戏状态置位in-game字样
gameCanvas.blockFile = "Block.qml"//指定游戏中每个格子实例化时所引用的组件
gameCanvas.background = "gfx/background.png"//指定游戏背景色
arcadeTimer.start();
}
//Emitted particles don't fade out, because ImageParticle is on the GameArea
system: gameCanvas.ps//按钮中粒子发射器所属粒子系统
group: "green"//按钮中粒子发射器所属组
Timer {
id: arcadeTimer
interval: Settings.menuDelay
running : false
repeat : false
onTriggered: Logic.startNewGame(gameCanvas)//启动一次新游戏
}
} Button {//2 players
}
Button {//zen
}
Button {//puzzle
}
}
}
Image {//顶部游戏信息面盘
id: scoreBar
source: "content/gfx/bar.png"
width: parent.width
z:
y: -Settings.headerHeight//默认不显示 在界面以外
height: Settings.headerHeight
Behavior on opacity { NumberAnimation {} }
SamegameText {//当前游戏分数 arcade下显示
id: arcadeScore
anchors {
right: parent.right; //从界面右侧开始布局
topMargin: ;
rightMargin: ;
top: parent.top
}
text: '<font color="#f7d303">P1:</font> ' + gameCanvas.score//黄色的P1字样+白色字样的游戏分数
font.pixelSize: Settings.fontPixelSize
textFormat: Text.StyledText//字体格式
color: "white"//分数颜色为白色
opacity: gameCanvas.mode == "arcade" ? : //游戏模式为arcade时 显示
Behavior on opacity { NumberAnimation {} }//透明度使用渐变
}
SamegameText {//最高分 arcade下显示
}
SamegameText {//玩家一 得分 多人下显示
}
SamegameText {//玩家二 得分 多人下显示
}
SamegameText {//移动步数 puzzle下显示
}
SamegameText {//当前游戏时长 puzzle下显示
}
SamegameText {//游戏得分 puzzle下显示
}
} Image {//底部工具条
id: bottomBar
width: parent.width
height: Settings.footerHeight
source: "content/gfx/bar.png"
y: parent.height - Settings.footerHeight;
z:
Button {//退出按钮
id: quitButton
height: Settings.toolButtonHeight
imgSrc: "content/gfx/but-quit.png"
onClicked: {Qt.quit(); }//点击退出应用程序
anchors { left: parent.left; verticalCenter: parent.verticalCenter; leftMargin: }
}
Button {//菜单按钮 返回主页
}
Button {//开始新的一局
}
Button {//puzzle模式下 下一关
}
}
}
程序根节点是一个Rectangle组件,该组件是整个程序的根,他含有一个总的背景色。然后是游戏画布,游戏画布即除过顶部信息面板和底部工具栏以外的区域,游戏画布顶端紧接着顶部信息面板,底部紧接着底部工具栏顶端。从代码顺序往下分析,然后紧接着的两个大的Item分别就是顶部信息面板和底部工具条,他们两个的id分别是scoreBar与bottomBar。
GameArea组件几乎包含了游戏过程中所有的ui交互操作,在该组件中包含有一个粒子系统,游戏当中所有的烟花效果都是由该粒子系统进行实现,粒子系统默认有一种粒子图片,但是其使用Loader还加载了8种粒子图片,以备不同的粒子发射器使用,值得注意的是粒子发射器可以发射同属一个组的粒子图片。
//游戏窗口
import QtQuick 2.0
import QtQuick.Particles 2.0
import "samegame.js" as Logic
import "." Item {
Image {//游戏中背景色
id: bg
z: -
anchors.fill: parent
source: background;
fillMode: Image.PreserveAspectCrop
} MouseArea {
anchors.fill: parent; onClicked: {
if (puzzleTextBubble.opacity == ) {
puzzleTextBubble.opacity = ;//隐藏关卡提示窗口
Logic.finishLoadingMap();//根据本地文件数据 加载puzzle游戏数据
} else if (!swapping) {
Logic.handleClick(mouse.x,mouse.y);//处理点击事件
}
}
} Image {//历史最高分提示窗口
id: highScoreTextBubble
opacity: mode == "arcade" && gameOver && gameCanvas.score == gameCanvas.highScore ? :
Behavior on opacity { NumberAnimation {} }
anchors.centerIn: parent
z:
source: "gfx/bubble-highscore.png"
Image {
anchors.centerIn: parent
source: "gfx/text-highscore-new.png"
rotation: -
}
} Image {//过关提示 蓝色背景提示
id: puzzleTextBubble
anchors.centerIn: parent
opacity:
Behavior on opacity { NumberAnimation {} }
z:
source: "gfx/bubble-puzzle.png"
Connections {
target: gameCanvas
onModeChanged: if (mode != "puzzle" && puzzleTextBubble.opacity > ) puzzleTextBubble.opacity = ;
}
Text {//文本信息
id: puzzleTextLabel
width: parent.width -
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
color: "white"
font.pixelSize:
font.bold: true
wrapMode: Text.WordWrap
}
}
onModeChanged: {//当前游戏模式发送变化 隐藏双人模式下获胜窗口
p1WonImg.opacity = ;
p2WonImg.opacity = ;
}
SmokeText { id: puzzleWin; source: "gfx/icon-ok.png"; system: particleSystem }//puzzle模式闯关胜利
SmokeText { id: puzzleFail; source: "gfx/icon-fail.png"; system: particleSystem }//puzzle模式闯关失败 onSwapPlayers: {//双人模式下 鼠标点击后 交换玩家
smokeParticle.color = "yellow"
Logic.turnChange();//交换玩家
if (curTurn == ) {
p1Text.play();
} else {
p2Text.play();
}
clickDelay.running = true;
}
SequentialAnimation {
id: clickDelay//点击延迟 防止点击速度过快
ScriptAction { script: gameCanvas.swapping = true; }//
PauseAnimation { duration: }
ScriptAction { script: gameCanvas.swapping = false; }
} SmokeText {//提示玩家1 可以操作
id: p1Text; source: "gfx/text-p1-go.png";
system: particleSystem; playerNum:
opacity: p1WonImg.opacity + p2WonImg.opacity > ? :
} SmokeText {//提示玩家2 可以操作
id: p2Text; source: "gfx/text-p2-go.png";
system: particleSystem; playerNum:
opacity: p1WonImg.opacity + p2WonImg.opacity > ? :
} onGameOverChanged: {//游戏结束
if (gameCanvas.mode == "multiplayer") {
if (gameCanvas.score >= gameCanvas.score2) {
p1WonImg.opacity = ;
} else {
p2WonImg.opacity = ;
}
}
}
Image {//提示玩家1获胜
id: p1WonImg
source: "gfx/text-p1-won.png"
anchors.centerIn: parent
opacity:
Behavior on opacity { NumberAnimation {} }
z:
}
Image {//提示玩家2获胜
id: p2WonImg
source: "gfx/text-p2-won.png"
anchors.centerIn: parent
opacity:
Behavior on opacity { NumberAnimation {} }
z:
} ParticleSystem{//粒子系统
id: particleSystem;
anchors.fill: parent
z:
ImageParticle {
id: smokeParticle
groups: ["smoke"]
source: "gfx/particle-smoke.png"
alpha: 0.1
alphaVariation: 0.1
color: "yellow"
}
Loader {
id: auxLoader
anchors.fill: parent
source: "PrimaryPack.qml"
onItemChanged: {
if (item && "particleSystem" in item)
item.particleSystem = particleSystem
if (item && "gameArea" in item)
item.gameArea = gameCanvas
}
}
}
}
程序中包含有大量SmokeText组件对象和SmokeText组件对象,这些组件都被封装了起来,可以实现特定功能,例如SmokeText是烟花文本,首先提供文本显示功能,随后文本隐藏,并伴随烟花效果。该模块有一个MouseArea组件,铺满了整个GameArea区域,他主要是为了处理游戏过程中的鼠标点击事件。
ui展示的最后一个东西就是LogoAnimatin文件了,该文件代码量不大,因此我没有做删减,这个组件实现了一个G和S滚动的效果,如图1中的gis所展示的效果那样。
//程序启动窗口 game和same字样中 g/s切换动画
import QtQuick 2.0
import QtQuick.Particles 2.0 Item {
id: container //Positioned where the 48x48 S/G should be
property alias running: mainAnim.running
property ParticleSystem particleSystem
property int dur:
signal boomTime
Image {//S字样
id: s1
source: "gfx/logo-s.png"
y:
}
Image {//G字样
id: g1
source: "gfx/logo-g.png"
y: -
}
Column {//垂直布局2个元素 以便产生粒子效果
Repeater {
model:
Item {
width:
height:
BlockEmitter {
id: emitter
anchors.fill: parent
group: "red"
system: particleSystem//粒子发射器所述粒子系统
Connections {//链接container对象的信号
target: container
onBoomTime: emitter.pulse();
}
}
}
}
}
SequentialAnimation {
id: mainAnim
running: true
loops: -//无限循环
PropertyAction { target: g1; property: "y"; value: -}//界面以外
PropertyAction { target: g1; property: "opacity"; value: }//透明度变为1
PropertyAction { target: s1; property: "y"; value: }
PropertyAction { target: s1; property: "opacity"; value: }
NumberAnimation { target: g1; property: "y"; from: -; to: -; duration: dur}//G字样移动到S字样顶部
ParallelAnimation {//G字样和S字样同时移动
NumberAnimation { target: g1; property: "y"; from: -; to: ; duration: dur}
NumberAnimation { target: s1; property: "y"; from: ; to: ; duration: dur }
}
PauseAnimation { duration: dur }//暂停500ms
ScriptAction { script: container.boomTime(); }//执行js脚本 发送boomTime信号 id为emitter的BlockEmitter发射器 处理该信号
ParallelAnimation {//G字样和S字样同时淡出
NumberAnimation { target: g1; property: "opacity"; to: ; duration: dur }
NumberAnimation { target: s1; property: "opacity"; to: ; duration: dur }
}
PropertyAction { target: s1; property: "y"; value: -}//S移动到界面外
PropertyAction { target: s1; property: "opacity"; value: }//可见
NumberAnimation { target: s1; property: "y"; from: -; to: ; duration: dur * }//将S字样移动到A字母平齐位置
}
}
代码中有大量注释,相信大家应该都看得懂,值得注意的是32行的Connections连接,他将该组件的boomTime信号进行了处理,让emitter粒子发射器发生了100ms粒子,并进行关闭。代码最后的mainAnim序列动画,将G和S窗口进行了简单的动画处理,其中PropertyAction是属性动作,可以重置属性值,NumberAnimation时属性动画,将指定属性从from态变为to态,并使用duration指定的时间,如果需要指定js代码,则使用ScriptAction脚本动作。
2、data目录
data目录包含的qml文件是最多的,但也缺是最简单的,该目录下的所有文件都是服务于puzzle游戏模式,TemplateBase.qml组件中封装了puzzle游戏模式中的关卡过关评判标准,level*.qml文件都继承自TemplateBase.qml组件,新增了startingGrid属性,用于存储关卡数据,关于其他属性TemplateBase.qml文件中都有具体注释,如图4所示puzzle过关评判标准。
图4 puzzle过关评判标准
3、emitter目录
该目录下包含4个文件,除过PrimaryPack文件外都是重写了粒子发射器,PrimaryPack.qml文件主要是提供了粒子发射器发射的粒子图片,在GameArea组件中通过Loader加载器进行了所有粒子图片的加载处理。
- BlockEmitter.qml组件是块粒子发射器,主要用于启动页G和S色块消失时产生烟花效果
- MenuEmitter.qml组件是块粒子发射器,主要用于启动页菜单项被点击消失时产生烟花效果
- PaintEmitter.qml组件是粒子发射器,主要用于游戏过程中色块消失时提供烟花效果
重写的粒子发射器主要是针对粒子发射器属性进行了重新赋值。如下BlockEmitter粒子发射器所示
import QtQuick 2.0
import QtQuick.Particles 2.0
// Needed for singletons QTBUG-34418
import "." Emitter {
property Item block: parent//父组件
velocity: TargetDirection{targetX: block.width/; targetY: block.height/; magnitude: -; magnitudeVariation: }
acceleration: TargetDirection{targetX: block.width/; targetY: block.height/; magnitude: -;}
shape: EllipseShape{fill:true}
enabled: false;
lifeSpan: ; //生命周期周期
lifeSpanVariation: //生命周期振幅
emitRate: //速率 每秒钟产生1000个粒子
maximumEmitted: //only fires 0.1s bursts (still 2x old number)
size: Settings.blockSize * 0.85//粒子初始大小
endSize: Settings.blockSize * 0.85 ///最终大小
}
4、block目录
连连看游戏总共包含4种游戏模式,其实1 player和2 players模式使用的色块文件是同一个qml文件。
- block.qml:使用与1 player和2 players游戏模式,该色块文件比SimpleBlock.qml色块文件多了一个PaintEmitter粒子发射器,在游戏过重当中主要表现在色块消失后会在背景色上留下一片阴影,如图5所示色块消失时,在界面上留下了红色的效果,随着时间推进该红色残留会逐渐消失。
- PuzzleBlock.qml:puzzle模式下色块,主要是加载色块图片不一样
- SimpleBlock.qml:类似于block.qml色块,只是色块消失时没有颜色残留,烟花效果依然存在
图5 Block消失演示
5、control目录
单纯的组件封装
- Button.qml:实现了按钮的基本功能,例如主界面上的游戏菜单选项,底部工具栏的按钮均是该组件对象
- SamegameText.qml:Text封装,主要针对Text的一些属性进行了设置
- SmokeText.qml:带有烟花消失效果的文本窗口
6、setting目录
如下代码所示,进行了程序基础值定义
//游戏 属性定义
import QtQml 2.0 QtObject {
// This height/width is here for desktop testing, otherwise
// we could just use Screen.width/Screen.height.
property int screenHeight:
property int screenWidth: property int menuDelay: property int headerHeight:
property int footerHeight: property int fontPixelSize: property int blockSize: property int toolButtonHeight: property int menuButtonSpacing:
}
7、js文件分析
除过游戏ui部分,js文件就是该示例代码的灵魂所在,完成了qml不容易控制的逻辑代码
7.1动态加载组件
function changeBlock(src)
{
blockSrc = src;
component = Qt.createComponent(blockSrc);
}
7.2创建组件对象
function createBlock(column,row,type)
{
// Note that we don't wait for the component to become ready. This will
// only work if the block QML is a local file. Otherwise the component will
// not be ready immediately. There is a statusChanged signal on the
// component you could use if you want to wait to load remote files.
if (component.status == ){//组件加载完毕Component.Ready
if (type == undefined)
type = Math.floor(Math.random() * types);
if (type < || type > ) {
console.log("Invalid type requested");//TODO: Is this triggered by custom levels much?
return;
}
//通过组件创建对象
var dynamicObject = component.createObject(gameCanvas,//父类
{"type": type,//导出属性type
"x": column*gameCanvas.blockSize,
"y": -*gameCanvas.blockSize,
"width": gameCanvas.blockSize,
"height": gameCanvas.blockSize,
"particleSystem": gameCanvas.ps});//导出属性particleSystem
if (dynamicObject == null){
console.log("error creating block");
console.log(component.errorString());
return false;
}
dynamicObject.y = row*gameCanvas.blockSize;
dynamicObject.spawned = true; board[index(column,row)] = dynamicObject;
}else{
console.log("error loading block component");
console.log(component.errorString());
return false;
}
return true;
}
7.3启动新的一局游戏
//开始一场新游戏 gc变量类型决定gameCanvas变量类型
function startNewGame(gc, mode, map)
{
gameCanvas = gc;//初始化当前游戏对象
if (mode == undefined)
gameMode = "arcade";//默认为arcade游戏模式
else
gameMode = mode;
gameOver = false;//游戏未结束 cleanUp(); gc.gameOver = false;
gc.mode = gameMode;
// Calculate board size
maxColumn = Math.floor(gameCanvas.width/gameCanvas.blockSize);//计算最大列数
maxRow = Math.floor(gameCanvas.height/gameCanvas.blockSize);//计算最大行数
maxIndex = maxRow * maxColumn;//计算格子个数
if (gameMode == "arcade") //Needs to be after board sizing
getHighScore();//从本地sqlite数据库获取最佳得分 // Initialize Board
board = new Array(maxIndex);//申请游戏格子内存
gameCanvas.score = ;//初始化游戏参数
gameCanvas.score2 = ;
gameCanvas.moves = ;
gameCanvas.curTurn = ;
if (gameMode == "puzzle")//如果是puzzle模式 则需要加载关卡数据
loadMap(map);
else//Note that we load them in reverse order for correct visual stacking
for (var column = maxColumn - ; column >= ; column--)//循环创建每一个格子上的块 即红色圆形、蓝色圆形或者黄色圆形等
for (var row = maxRow - ; row >= ; row--)
createBlock(column, row);
if (gameMode == "puzzle")//如果是puzzle模式 则需要加载历史闯关等级
getLevelHistory();//Needs to be after map load
gameDuration = new Date();//游戏开始 开始计时
}
7.4鼠标点击处理游戏进度
function handleClick(x,y)
{
if (betweenTurns || gameOver || gameCanvas == undefined)
return;
var column = Math.floor(x/gameCanvas.blockSize);
var row = Math.floor(y/gameCanvas.blockSize);
if (column >= maxColumn || column < || row >= maxRow || row < )
return;
if (board[index(column, row)] == null)//判断当前点击的块是否为空
return;
// If it's a valid block, remove it and all connected (does nothing if it's not connected)
floodFill(column,row, -);
if (fillFound <= )
return;
if (gameMode == "multiplayer" && gameCanvas.curTurn == )
gameCanvas.score2 += (fillFound - ) * (fillFound - );//两个玩家时 给玩家2加分
else
gameCanvas.score += (fillFound - ) * (fillFound - );
if (gameMode == "multiplayer" && gameCanvas.curTurn == )
shuffleUp();//该玩家2时 向上洗牌
else
shuffleDown();//向下洗牌
gameCanvas.moves += ;//移动次数加一
if (gameMode == "endless")
refill();
else if (gameMode != "multiplayer")
victoryCheck();
if (gameMode == "multiplayer" && !gc.gameOver){
betweenTurns = true;
gameCanvas.swapPlayers();//signal, animate and call turnChange() when ready
}
}
7.5检测游戏是否结束
//检测游戏是否结束
function victoryCheck()
{
// Awards bonuses for no blocks left
var deservesBonus = true;//额外奖励
if (board[index(,maxRow - )] != null || board[index(,)] != null)//坐上角和左下角如果有色块 则说明窗口上还有色块
deservesBonus = false;
// Checks for game over
if (deservesBonus){//无色块
if (gameCanvas.curTurn = )//该哪个玩家 给那个玩家加1000分
gameCanvas.score += ;
else
gameCanvas.score2 += ;
}
gameOver = deservesBonus;
if (gameCanvas.curTurn == ){//如果是玩家1 操作
if (!(floodMoveCheck(, maxRow - , -)))
gameOver = true;
}else{
if (!(floodMoveCheck(, , -, true)))
gameOver = true;
}
if (gameMode == "puzzle"){
puzzleVictoryCheck(deservesBonus);//Takes it from here
return;
}
if (gameOver) {
var winnerScore = Math.max(gameCanvas.score, gameCanvas.score2);
if (gameMode == "multiplayer"){
gameCanvas.score = winnerScore;//更新最高分
saveHighScore(gameCanvas.score2);
}
saveHighScore(gameCanvas.score);//保存历史最高分
gameDuration = new Date() - gameDuration;//计算游戏耗时
gameCanvas.gameOver = true;//游戏结束
}
}
7.6从本地sqlite数据库读取历史数据
//从本地sqlite数据库读取最高分
function getHighScore()
{
var db = Sql.LocalStorage.openDatabaseSync(
"SameGame",
"2.0",
"SameGame Local Data", );
db.transaction(
function(tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(game TEXT, score NUMBER, gridSize TEXT, time NUMBER)');
// Only show results for the current grid size
var rs = tx.executeSql('SELECT * FROM Scores WHERE gridSize = "'
+ maxColumn + "x" + maxRow + '" AND game = "' + gameMode + '" ORDER BY score desc');
if (rs.rows.length > )
gameCanvas.highScore = rs.rows.item().score;
else
gameCanvas.highScore = ;
}
);
}
7.7保存游戏数据到本地sqlite数据库
//保存最高分到本地sqlite数据库
function saveHighScore(score)
{
// Offline storage
var db = Sql.LocalStorage.openDatabaseSync(
"SameGame",
"2.0",
"SameGame Local Data", );
var dataStr = "INSERT INTO Scores VALUES(?, ?, ?, ?)";
var data = [
gameMode,
score,
maxColumn + "x" + maxRow,
Math.floor(gameDuration / )
];
if (score >= gameCanvas.highScore)//Update UI field
gameCanvas.highScore = score; db.transaction(
function(tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(game TEXT, score NUMBER, gridSize TEXT, time NUMBER)');
tx.executeSql(dataStr, data);
}
);
}
三、下载链接
qml demo分析(samegame-拼图游戏)的更多相关文章
- qml demo分析(maskedmousearea-异形窗口)
一.效果展示 如本文的标题所示,这篇文章分析的demo是一个异形窗口,主要展示鼠标在和异形区域交互的使用,效果如图1所示,当鼠标移动到白云或者月亮上时,相应的物体会高亮,当鼠标按下时,物体会有一个放大 ...
- qml demo分析(threadedanimation-线程动画)
一.效果预览 使用过qml的同学都知道,使用qml做动画效果是非常简单的,再也不需要像QWidget那样,自己模拟一个动画,费时又费力,往往还达不到效果.今天我们就来分析下qml的两种动画实现方式,如 ...
- qml demo分析(maroon-小游戏)
1.效果展示 这篇文章我还是分析一个qt源码中的qml程序,程序运行效果如下图所示. 图1 游戏开始 图2 游戏中 2.源码分析 这个游戏的源码文件比较多,为了能更清楚的了解整个代码,我先整体分析 ...
- qml demo分析(threading-线程任务)
一.关键类说明 qml内置了WorkerScript组件,该组件有一个source属性,可以加载js文件,含有一个名为message的信号,意味着他有一个默认的onMessage槽函数,除此之外他还有 ...
- qml demo分析(text-字体展示)
上一篇文章分析了一个小游戏,使用qml编写界面+js进行复杂逻辑控制,算是一个比较完整的qml示例代码了,今天就不那么继续变态啦,来看一个简单的字体示例程序吧,该示例代码比较简单,主要是展示了几个简单 ...
- qml demo分析(abstractitemmodel-数据分离)
一.概述 qt5之后qml也可以被用于桌面程序开发,今天我就拿出qt demo中的一个qml示例程序进行分析.这个demo主要是展示了qml数据和展示分离的使用方式,qml只专注于快速高效的绘制界面, ...
- qml demo分析(externaldraganddrop-拖拽)
一.效果展示 客户端程序拖拽是一个很常见的需求,对于QWidget程序来说,需要重写如图1这么几个方法,通过重写这几个方法的逻辑,我们就可以控制鼠标拖拽的逻辑,糟糕的是QDrag执行exec后是一个阻 ...
- qml demo分析(rssnews-常见新闻布局)
一.效果展示 今儿来分析一篇常见的ui布局,完全使用qml编写,ui交互效果友好,如图1所示,是一个常见的客户端新闻展示效果,左侧是一个列表,右侧是新闻详情. 图1 新闻效果图 二.源码分析 首先先来 ...
- qml demo分析(photosurface-图片涅拉)
阅读qml示例代码已有一小段时间,也陆续的写了一些自己关于qml示例代码的理解,可能由于自己没有大量的qml开发经验,总感觉复杂的ui交互qml处理起来可能会比较棘手,但事实总是会出人意料,今天我们就 ...
随机推荐
- Java 读书笔记 (十二) Java Character 类
在实际开发过程中, 我们经常会遇到需要使用对象,而不是内置数据类型的情况. 为了解决这个问题, Java语言为内置数据类型char提供了包装类Character类. 可以使用Character的构造方 ...
- http.go
) } if name != cfgName { continue } return val.FieldByNa ...
- C++类中静态变量和普通变量的区别
静态变量: 1.静态变量会被编到程序的exe里面,从程序启动到结束,它一直存在: 2.静态变量的初始化值为0: 3.全局变量默认是静态变量: 4.在类中的函数变量前面加了static的也是静态变量,只 ...
- BZOJ_2622_[2012国家集训队测试]深入虎穴_最短路
BZOJ_2622_[2012国家集训队测试]深入虎穴_最短路 Description 虎是中国传统文化中一个独特的意象.我们既会把老虎的形象用到喜庆的节日装饰画上,也可能把它视作一种邪恶的可怕的动物 ...
- 权限系统与RBAC模型概述[绝对经典]
0. 前言 一年前,我负责的一个项目中需要权限管理.当时凭着自己的逻辑设计出了一套权限管理模型,基本原理与RBAC非常相似,只是过于简陋.当时google了一些权限管理的资料,从中了解到早就有了RBA ...
- 操作系统--进程管理(Processing management)
一.进程的组成 进程通常由程序.数据和进程控制块(Process Control Block,PCB)组成. 二. 进程的状态以及状态切换 进程执行时的间断性决定了进程可能具有多种状态,最基本的三种状 ...
- 使用FileUpload实现Servlet的文件上传
简介 FileUpload 是 Apache commons下面的一个子项目,用来实现Java环境下的文件上传功能. FileUpload链接 FileUpload 是基于Apache的Commons ...
- nginx日志模块及日志定时切割
一:作用 Ngx_http_log_module:定义日志格式,并且以指定的格式保存. 二:示例配置 log_format compression '$remote_addr - $remote_us ...
- docker环境部署
docker环境部署 1 查看当前系统版本 只支持CentOS7版本的系统,如果不是的话,可以让项目方进行重装或者系统内核升级. [root@bogon bin]# cat /etc/redhat-r ...
- appium-desktop录制脚本二次开发,生成我司自动化脚本
目的 通过对appium-desktop脚本录制功能进行二次开发,使录制的java脚本符合我司自动化框架要求. 实现步骤 1.增加元素名称的输入框 由于ATK(我司自动化测试框架)脚本中元素是以“ap ...