一、效果展示

  相信大家都玩儿过连连看游戏,而且此款游戏也是闲时一款打发时间的趣事,那么接下来我将分析一款类似的游戏,完全使用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);
}
);
}

三、下载链接

  samegame示例分析

qml demo分析(samegame-拼图游戏)的更多相关文章

  1. qml demo分析(maskedmousearea-异形窗口)

    一.效果展示 如本文的标题所示,这篇文章分析的demo是一个异形窗口,主要展示鼠标在和异形区域交互的使用,效果如图1所示,当鼠标移动到白云或者月亮上时,相应的物体会高亮,当鼠标按下时,物体会有一个放大 ...

  2. qml demo分析(threadedanimation-线程动画)

    一.效果预览 使用过qml的同学都知道,使用qml做动画效果是非常简单的,再也不需要像QWidget那样,自己模拟一个动画,费时又费力,往往还达不到效果.今天我们就来分析下qml的两种动画实现方式,如 ...

  3. qml demo分析(maroon-小游戏)

    1.效果展示 这篇文章我还是分析一个qt源码中的qml程序,程序运行效果如下图所示. 图1  游戏开始 图2  游戏中 2.源码分析 这个游戏的源码文件比较多,为了能更清楚的了解整个代码,我先整体分析 ...

  4. qml demo分析(threading-线程任务)

    一.关键类说明 qml内置了WorkerScript组件,该组件有一个source属性,可以加载js文件,含有一个名为message的信号,意味着他有一个默认的onMessage槽函数,除此之外他还有 ...

  5. qml demo分析(text-字体展示)

    上一篇文章分析了一个小游戏,使用qml编写界面+js进行复杂逻辑控制,算是一个比较完整的qml示例代码了,今天就不那么继续变态啦,来看一个简单的字体示例程序吧,该示例代码比较简单,主要是展示了几个简单 ...

  6. qml demo分析(abstractitemmodel-数据分离)

    一.概述 qt5之后qml也可以被用于桌面程序开发,今天我就拿出qt demo中的一个qml示例程序进行分析.这个demo主要是展示了qml数据和展示分离的使用方式,qml只专注于快速高效的绘制界面, ...

  7. qml demo分析(externaldraganddrop-拖拽)

    一.效果展示 客户端程序拖拽是一个很常见的需求,对于QWidget程序来说,需要重写如图1这么几个方法,通过重写这几个方法的逻辑,我们就可以控制鼠标拖拽的逻辑,糟糕的是QDrag执行exec后是一个阻 ...

  8. qml demo分析(rssnews-常见新闻布局)

    一.效果展示 今儿来分析一篇常见的ui布局,完全使用qml编写,ui交互效果友好,如图1所示,是一个常见的客户端新闻展示效果,左侧是一个列表,右侧是新闻详情. 图1 新闻效果图 二.源码分析 首先先来 ...

  9. qml demo分析(photosurface-图片涅拉)

    阅读qml示例代码已有一小段时间,也陆续的写了一些自己关于qml示例代码的理解,可能由于自己没有大量的qml开发经验,总感觉复杂的ui交互qml处理起来可能会比较棘手,但事实总是会出人意料,今天我们就 ...

随机推荐

  1. php自带验证邮箱 url ip函数

    以前用PHP验证邮箱.URL.IP是否合法都是通过自己写正则来实现,但是有时候脑子发昏,可能会写出一个不是完全正确的正则,导致验证出错,今天发现原来PHP本身自带了验证邮箱.URL.IP是否合法的函数 ...

  2. Selenium库,Python精品教程!

    什么是Selenium selenium基本使用 用python写爬虫的时候,主要用的是selenium的Webdriver,我们可以通过下面的方式先看看Selenium.Webdriver支持哪些浏 ...

  3. Asp.Net WebAPI中Filter过滤器的使用以及执行顺序

    转发自:http://www.cnblogs.com/UliiAn/p/5402146.html 在WEB Api中,引入了面向切面编程(AOP)的思想,在某些特定的位置可以插入特定的Filter进行 ...

  4. Android P Beta发布!最新版本抢先体验!

    在不久前结束的谷歌I/O开发者大会上,谷歌公布了下一个版本的 Android,也就是 Android P 的 beta 版本.Android P 将 AI 定位为操作系统的核心,并侧重于提供智能且简洁 ...

  5. Postman----模拟服务器返回数据

    使用场景:在某些情况下,比如A接口还没开发好,我们需要测试B接口,刚好B接口的请求数据中需要包含A接口返回的数据,这时我们就可以模拟A接口服务器返回的数据来测试B接口 解决办法: 举例:模拟此 htt ...

  6. SQL Server事务 事务日志

    事务 (SQL Server) 一.事务概念    事务是一种机制.是一种操作序列,它包含了一组数据库操作命令,这组命令要么全部执行,要么全部不执行.因此事务是一个不可分割的工作逻辑单元.在数据库系统 ...

  7. solr6.6 导入索引数据

    1.什么是core core是solr的一个索引库,可以理解为一个数据库,core可以根据需要,创建多个. 2.创建core 例如,创建一个core,名字叫mycore,就可以用一下命令: E:\so ...

  8. CentOS7搭建本地YUM仓库,并定期同步阿里云源

    CentOS7同步阿里云镜像rpm包并自建本地yum仓库 系统环境 # cat /etc/centos-release CentOS Linux release 7.6.1810 (Core) # u ...

  9. MIP 移动网页加速器视频教程全新发布

    MIP (Mobile Instant Pages - 移动网页加速器) 是百度推出的开源项目,用于移动端页面加速.MIP 技术通过优化浏览器资源加载,前端代码执行及 CDN 缓存加速来加速页面,打造 ...

  10. 【EXCEL-折线图】百折不挠 | 用EXCEL画出与众不同的折线图(曲线图)

    很多熟悉EXLCE的朋友都知道EXCEL在生成统计图表方面的强大功能,我们在写各类总结.报告.方案等文档时常涉及到各类统计数字,将统计数字用图表的形式展示出来,既直观又美观.下面我分享一种不一样的折线 ...