• qml demo分析(maroon-小游戏)


    1、效果展示

      这篇文章我还是分析一个qt源码中的qml程序,程序运行效果如下图所示。

     图1  游戏开始

    图2  游戏中

    2、源码分析

      这个游戏的源码文件比较多,为了能更清楚的了解整个代码,我先整体分析代码,然后再局部分析。

    1、源码目录结构

    图3  源码目录

      如图3所示,是小游戏的源码目录,下边我分别按文件名称来介绍该文件的功能

    • TowerBase.qml:模型父类,定义了一些共有的属性,比如血量,攻击距离和攻击伤害等
    • Bomb.qml:海藻,父类为TowerBase.qml
    • Factory.qml:星星,父类为TowerBase.qml
    • Ranged.qml:章鱼,父类为TowerBase.qml
    • Melee.qml:螃蟹,父类为TowerBase.qml
    • BuildButton.qml:左键菜单中的一项
    • GameCanvas.qml:游戏中画布
    • GameOverScreen.qml:游戏结束画布
    • InfoBar.qml:游戏信息,包含当前血量显示,当前救下的鱼和金币数量
    • maroon.qml:程序主文件
    • MobBase.qml:带有鱼的气泡
    • NewGameScreen.qml:程序启动时画布,和GameCanvas、GameOverScreen组成了一张竖直的大画布
    • SoundEffect.qml:声音文件,可以播放音频文件
    • logic.js:js文件,完成一些具体的逻辑操作,比如新游戏清空内存,游戏推进更新内存等。

    2、交互分析

      该游戏为一个塔防类游戏,类似于植物大战僵尸,但是游戏丰富程度和游戏流程度却要差上很多,不过既然是demo,我们就只学习它的方式方法。首先启动游戏,游戏界面使用NewGameScreen组件展示ui,启动页只包含游戏标题、带气泡的鱼和开始按钮,在动态图1的第一帧就可以看到,点击开始游戏,整个界面下滑,然后出现倒数3秒,并开始游戏,开始游戏画面用GameCanvas组件展示,但不包括游戏当前血量和金币数值等信息。随着游戏的推进,游戏的推进速度也会随之变快,这个时候如果自身血量小于等于0那么游戏就会结束,整个界面再次下滑,出现游戏结束后所得分数界面GameOverScreen,在这个界面我们还可以再一次启动程序,当点击新游戏时,整个界面上滑,又出现开始游戏画面GameCanvas。这个时候关于界面上滑和下滑就出现了一个循环,即GameCanvas和GameOverScreen相互转化。其实整个游戏画面是一个高度为当前游戏窗口高度不到3倍大的画布(游戏信息数据在游戏总和游戏结束均有)。

    3、模型分析

      在这个小游戏中,总共有5个模型,分别是:海藻、星星、带鱼的泡泡、章鱼和螃蟹。除过带鱼的泡泡其余4个模型都有一个公有的基类TowerBase,因此这4个模型我就重点解释其中一个模型,其他模型的代码中也有大量的注释。

      Bomb是海藻组件,其中有一个关键对象SpriteSequence,他可以控制多个动画的渲染,在这个海藻组件中默认使用name为idle的Sprite,这是一个可以将png分段展示的对象。游戏中游戏推进时,fire接口会被调用,播放海藻爆炸前音频文件,当前动画改为shoot,并开启一个定时器,用于调用finishFire接口,完成击杀气泡拯救小鱼的操作,海藻组件代码如下,代码中亦有大量注释

     1 //海藻爆炸  
     2 import QtQuick 2.0
     3 import "../logic.js" as Logic
     4 import ".."
     5 
     6 TowerBase {
     7     id: container
     8     hp: 10
     9     range: 0.4
    10     rof: 10
    11     property real detonationRange: 2.5
    12 
    13     function fire() {//开始爆炸
    14         sound.play()//首先播放声音
    15         sprite.jumpTo("shoot")//
    16         animDelay.start()//启动定时器
    17     }
    18 
    19     function finishFire() {//爆炸结束
    20         var sCol = Math.max(0, col - 1)
    21         var eCol = Math.min(Logic.gameState.cols - 1, col + 1)
    22         var killList = new Array()
    23         for (var i = sCol; i <= eCol; i++) {
    24             for (var j = 0; j < Logic.gameState.mobs[i].length; j++)
    25                 if (Math.abs(Logic.gameState.mobs[i][j].y - container.y) < Logic.gameState.squareSize * detonationRange)
    26                     killList.push(Logic.gameState.mobs[i][j])//满足爆炸距离的都加入到击杀列表
    27             while (killList.length > 0)
    28                 Logic.killMob(i, killList.pop())//调用js函数击杀指定列所有目标
    29         }
    30         Logic.killTower(row, col);//移除海藻
    31     }
    32 
    33     Timer {
    34         id: animDelay
    35         running: false
    36         interval: shootState.frameCount * shootState.frameDuration//动画播放总时长
    37         onTriggered: finishFire()
    38     }
    39 
    40     function die()//销毁对象本身
    41     {
    42         destroy() // No blink, because we usually meant to die
    43     }
    44 
    45     SoundEffect {//播放音频文件
    46         id: sound
    47         source: "../audio/bomb-action.wav"//海藻爆炸音频文件
    48     }
    49 
    50     SpriteSequence {//动画序列
    51         id: sprite
    52          64
    53         height: 64
    54         interpolate: false
    55         goalSprite: ""
    56 
    57         Sprite {//海藻转向动画
    58             name: "idle"
    59             source: "../gfx/bomb-idle.png"
    60             frameCount: 4
    61             frameDuration: 800//每帧持续时长
    62         }
    63 
    64         Sprite {//海藻爆炸动画
    65             id: shootState
    66             name: "shoot"
    67             source: "../gfx/bomb-action.png"
    68             frameCount: 6
    69             frameDuration: 155
    70             to: { "dying" : 1 } //动画结束后  跳转到dying动画
    71         }
    72 
    73         Sprite {//海藻爆炸动画
    74             name: "dying"
    75             source: "../gfx/bomb-action.png"//资源地址
    76             frameCount: 1//只包含一帧
    77             frameX: 64 * 5
    78             frameWidth: 64
    79             frameHeight: 64
    80             frameDuration: 155
    81         }
    82 
    83         SequentialAnimation on x {//动画作用于x坐标
    84             loops: Animation.Infinite
    85             NumberAnimation { from: x; to: x + 4; duration: 900; easing.type: Easing.InOutQuad }
    86             NumberAnimation { from: x + 4; to: x; duration: 900; easing.type: Easing.InOutQuad }
    87         }
    88         SequentialAnimation on y {//动画作用于y坐标
    89             loops: Animation.Infinite
    90             NumberAnimation { from: y; to: y - 4; duration: 900; easing.type: Easing.InOutQuad }
    91             NumberAnimation { from: y - 4; to: y; duration: 900; easing.type: Easing.InOutQuad }
    92         }
    93     }
    94 }

     4、js文件分析

      js文件用于控制qml程序的逻辑实现部分,简单的js代码可以内联到qml组件代码里,如果是复杂的或者一些工具函数,那么最好还是写到一个单独的js文件,然后通过import导入到qml文件中。

      关于这个游戏的一些具体细节我个人没有仔细研究,比如游戏的速度控制。写此分析文章的原因主要是为了学习qml的语法和代码习惯,因此不尽如人意的地方可能会比较多,大神勿喷,仅供初学者借鉴。

      这个游戏的js文件主要是提供了一系列的工具函数,包括游戏进度控制的参数,具体接口含义可直接看如下代码中的注释

      1 .pragma library // 共享库  该文件只会被加载一次
      2 .import QtQuick 2.0 as QQ
      3 
      4 // Game Stuff
      5 var gameState // Local reference
      6 function getGameState() { return gameState; }
      7 
      8 var towerData = [ // Name and cost, stats are in the delegate per instance
      9     { "name": "Melee", "cost": 20 },
     10     { "name": "Ranged", "cost": 50 },
     11     { "name": "Bomb", "cost": 75 },
     12     { "name": "Factory", "cost": 25 }
     13 ]
     14 
     15 var waveBaseData = [300, 290, 280, 270, 220, 180, 160, 80, 80, 80, 30, 30, 30, 30];
     16 var waveData = [];
     17 
     18 var towerComponents = new Array(towerData.length);
     19 var mobComponent = Qt.createComponent("mobs/MobBase.qml");
     20 
     21 //游戏结束  释放动态申请的内存空间  并重置相应的标志
     22 function endGame()
     23 {
     24     gameState.gameRunning = false;//游戏未在正在运行
     25     gameState.gameOver = true;//游戏结束
     26     for (var i = 0; i < gameState.cols; i++) {
     27         for (var j = 0; j < gameState.rows; j++) {
     28             if (gameState.towers[towerIdx(i, j)]) {
     29                 gameState.towers[towerIdx(i, j)].destroy();
     30                 gameState.towers[towerIdx(i, j)] = null;
     31             }
     32         }
     33         for (var j in gameState.mobs[i])
     34             gameState.mobs[i][j].destroy();
     35         gameState.mobs[i].splice(0,gameState.mobs[i].length); //Leaves queue reusable
     36     }
     37 }
     38 
     39 function startGame(gameCanvas)
     40 {
     41     waveData = new Array();
     42     for (var i in waveBaseData)
     43         waveData[i] = waveBaseData[i];
     44     gameState.freshState();//重置游戏资料
     45     for (var i = 0; i < gameCanvas.cols; i++) {//清空游戏内存数据,主要针对每个格子存放数据
     46         for (var j = 0; j < gameCanvas.rows; j++)
     47             gameState.towers[towerIdx(i, j)] = null;
     48         gameState.mobs[i] = new Array();
     49     }
     50     gameState.towers[towerIdx(0, 0)] = newTower(3, 0, 0);//左上角生成一个星星
     51     gameState.gameRunning = true;
     52     gameState.gameOver = false;
     53 }
     54 
     55 function newGameState(gameCanvas)//开始一场新游戏
     56 {
     57     for (var i = 0; i < towerComponents.length; i++) {
     58         towerComponents[i] = Qt.createComponent("towers/" + towerData[i].name + ".qml");
     59         if (towerComponents[i].status == QQ.Component.Error) {
     60             gameCanvas.errored = true;
     61             gameCanvas.errorString += "Loading Tower " + towerData[i].name + "
    " + (towerComponents[i].errorString());
     62             console.log(towerComponents[i].errorString());
     63         }
     64     }
     65     gameState = gameCanvas;//gameState赋初值  这个时候才知道对象类型
     66     gameState.freshState();//重置游戏数据
     67     gameState.towers = new Array(gameCanvas.rows * gameCanvas.cols);//为游戏分配rows*cols个内存空间,用于存储每个格子数据
     68     gameState.mobs = new Array(gameCanvas.cols);//
     69     return gameState;
     70 }
     71 
     72 function row(y)//返回所在行
     73 {
     74     return Math.floor(y / gameState.squareSize);
     75 }
     76 
     77 function col(x)//返回所在列
     78 {
     79     return Math.floor(x / gameState.squareSize);
     80 }
     81 
     82 function towerIdx(x, y)//根据行和列计算towers位置
     83 {
     84     return y + (x * gameState.rows);
     85 }
     86 
     87 function newMob(col)//随机产生一个带鱼气泡
     88 {
     89     var ret = mobComponent.createObject(gameState.canvas,
     90         { "col" : col,
     91           "speed" : (Math.min(2.0, 0.10 * (gameState.waveNumber + 1))),
     92           "y" : gameState.canvas.height });
     93     gameState.mobs[col].push(ret);
     94     return ret;
     95 }
     96 
     97 function newTower(type, row, col)//根据类型生成模型,并设置模型所在行和列
     98 {
     99     var ret = towerComponents[type].createObject(gameState.canvas);
    100     ret.row = row;
    101     ret.col = col;
    102     ret.fireCounter = ret.rof;
    103     ret.spawn();
    104     return ret;
    105 }
    106 
    107 function buildTower(type, x, y)//根据菜单项类型、行和列  生成tower
    108 {
    109     if (gameState.towers[towerIdx(x,y)] != null) {//如果之前存在
    110         if (type <= 0) {//如果点击类型不在4个菜单项里 则清空该tower
    111             gameState.towers[towerIdx(x,y)].sell();
    112             gameState.towers[towerIdx(x,y)] = null;
    113         }
    114     } else {
    115         if (gameState.coins < towerData[type - 1].cost)//如果金额不够 直接退出
    116             return;
    117         gameState.towers[towerIdx(x, y)] = newTower(type - 1, y, x);//生成一个新的模型
    118         gameState.coins -= towerData[type - 1].cost;//减去建造模型 所需要的金币
    119     }
    120 }
    121 
    122 function killMob(col, mob)//移除指定列模型
    123 {
    124     if (!mob)
    125         return;
    126     var idx = gameState.mobs[col].indexOf(mob);
    127     if (idx == -1 || !mob.hp)
    128         return;
    129     mob.hp = 0;
    130     mob.die();
    131     gameState.mobs[col].splice(idx,1);//从列中减掉
    132 }
    133 
    134 function killTower(row, col)//销毁指定位置模型
    135 {
    136     var tower = gameState.towers[towerIdx(col, row)];
    137     if (!tower)
    138         return;
    139     tower.hp = 0;
    140     tower.die();
    141     gameState.towers[towerIdx(col, row)] = null;
    142 }
    143 
    144 function tick()//游戏推进
    145 {
    146     if (!gameState.gameRunning)//游戏不在运行时  直接返回
    147         return;
    148 
    149     // Spawn
    150     gameState.waveProgress += 1;//游戏推进
    151     var i = gameState.waveProgress;
    152     var j = 0;
    153     while (i > 0 && j < waveData.length)
    154         i -= waveData[j++];
    155     if ( i == 0 ) // Spawn a mob//生成一个气泡
    156         newMob(Math.floor(Math.random() * gameState.cols));
    157     if ( j == waveData.length ) { // Next Wave
    158         gameState.waveNumber += 1;//游戏等级+1
    159         gameState.waveProgress = 0;
    160         var waveModifier = 10; // Constant governing how much faster the next wave is to spawn (not fish speed)
    161         for (var k in waveData ) // Slightly faster
    162             if (waveData[k] > waveModifier)
    163                 waveData[k] -= waveModifier;
    164     }
    165 
    166     // 遍历所有格子 
    167     for (var j in gameState.towers) {
    168         var tower = gameState.towers[j];
    169         if (tower == null)
    170             continue;
    171         if (tower.fireCounter > 0) {
    172             tower.fireCounter -= 1;
    173             continue;
    174         }
    175         var column = tower.col;//遍历所有气泡  
    176         for (var k in gameState.mobs[column]) {
    177             var conflict = gameState.mobs[column][k];
    178             if (conflict.y <= gameState.canvas.height && conflict.y + conflict.height > tower.y
    179                 && conflict.y - ((tower.row + 1) * gameState.squareSize) < gameState.squareSize * tower.range) { // 满足伤害距离
    180                 tower.fire();//
    181                 tower.fireCounter = tower.rof;
    182                 conflict.hit(tower.damage);//气泡自行处理伤害动作
    183             }
    184         }
    185 
    186         // 只有星星模型满足此条件  新增金币  并调用星星的fire动作
    187         if (tower.income) {
    188             gameState.coins += tower.income;
    189             tower.fire();
    190             tower.fireCounter = tower.rof;
    191         }
    192     }
    193 
    194     // 气泡移动
    195     for (var i = 0; i < gameState.cols; i++) {//遍历所有列
    196         for (var j = 0; j < gameState.mobs[i].length; j++) {//遍历每一列的气泡
    197             var mob = gameState.mobs[i][j];//气泡
    198             var newPos = gameState.mobs[i][j].y - gameState.mobs[i][j].speed;
    199             if (newPos < 0) {//如果浮出水面  
    200                 gameState.lives -= 1;//生命值减1
    201                 killMob(i, mob);//移除气泡
    202                 if (gameState.lives <= 0)//当生命值小于等于0时 游戏结束
    203                     endGame();//执行此操作之后 gameRunning状态变为false  则该tick函数会被调用 但不会往下执行
    204                 continue;
    205             }
    206             var conflict = gameState.towers[towerIdx(i, row(newPos))];//拿到指定位置模型
    207             if (conflict != null) {
    208                 if (mob.y < conflict.y + gameState.squareSize)
    209                     gameState.mobs[i][j].y += gameState.mobs[i][j].speed * 10; // 气泡上浮一下
    210                 if (mob.fireCounter > 0) {
    211                     mob.fireCounter--;
    212                 } else {//气泡移动到守卫模型跟前
    213                     gameState.mobs[i][j].fire();//
    214                     conflict.hp -= mob.damage;//守卫模型受到伤害
    215                     if (conflict.hp <= 0)//当守卫模型血量小于等于0时 移除守卫模型
    216                         killTower(conflict.row, conflict.col);
    217                     mob.fireCounter = mob.rof;
    218                 }
    219             } else {
    220                 gameState.mobs[i][j].y = newPos;//气泡移动到新位置
    221             }
    222         }
    223     }
    224 }

    5、左键菜单项

     1 //游戏中左键菜单项
     2 import QtQuick 2.0
     3 import "logic.js" as Logic
     4 
     5 Item {
     6     id: container
     7      64
     8     height: 64
     9     property alias source: img.source
    10     property int index//当前点击列序
    11     property int row: 0//当前菜单项所在行
    12     property int col: 0//当前菜单项所在列
    13     property int towerType//表明菜单项类型 根据此类型 可以获取name和cost值
    14     property bool canBuild: true//菜单项是否可以被创建
    15     property Item gameCanvas: parent.parent.parent
    16     signal clicked()//自定义信号  当该控件被点击时 
    17 
    18     Image {
    19         id: img
    20         opacity: (canBuild && gameCanvas.coins >= Logic.towerData[towerType-1].cost) ? 1.0 : 0.4//当金币数不够时,该菜单项透明度变为40%
    21     }
    22     Text {//菜单项右上角数字
    23         anchors.right: parent.right
    24         font.pointSize: 14
    25         font.bold: true
    26         color: "#ffffff"
    27         text: Logic.towerData[towerType - 1].cost
    28     }
    29     MouseArea {//鼠标点击时  根据菜单项类型、行数和列数新建模型
    30         anchors.fill: parent
    31         onClicked: {
    32             Logic.buildTower(towerType, col, row)//调用js方法 生成一个新的tower
    33             container.clicked()//发出菜单项被点击信号
    34         }
    35     }
    36     Image {//下三角
    37         visible: col == index && row != 0 //当列号等于当前点击列时  并且不是第一行
    38         source: "gfx/dialog-pointer.png"
    39         anchors.top: parent.bottom
    40         anchors.topMargin: 4
    41         anchors.horizontalCenter: parent.horizontalCenter
    42     }
    43     Image {//上倒三角
    44         visible: col == index && row == 0//当列号等于当前点击列时  并且是第一行
    45         source: "gfx/dialog-pointer.png"
    46         rotation: 180
    47         anchors.bottom: parent.top//三角的底部紧接父控件顶部
    48         anchors.bottomMargin: 6
    49         anchors.horizontalCenter: parent.horizontalCenter
    50     }
    51 }

    6、主qml,用于整个程序ui布局

      1 //程序主文件,由此qml启动整个ui
      2 import QtQuick 2.0
      3 import QtQuick.Particles 2.0
      4 import "content"
      5 import "content/logic.js" as Logic
      6 
      7 Item {
      8     id: root
      9      320
     10     height: 480
     11     property var gameState//游戏状态  就是游戏场景GameCanvas 维护了大量游戏过程的信息
     12     property bool passedSplash: false//游戏开始标志  只有首次启动游戏时为false 后续游戏结束重新开始依赖于gameOver状态
     13 
     14     Image {
     15         source:"content/gfx/background.png"  //背景图高度为主窗口3倍大小   1:失败重新开始   2:游戏中   3:启动游戏 
     16         anchors.bottom: view.bottom//背景图和窗口底部对齐
     17 
     18         ParticleSystem {//粒子系统
     19             id: particles
     20             anchors.fill: parent
     21 
     22             ImageParticle {//图片粒子   表示气泡
     23                 id: bubble
     24                 anchors.fill: parent
     25                 source: "content/gfx/catch.png"
     26                 opacity: 0.25//透明度25%
     27             }
     28 
     29             Wander {
     30                 xVariance: 25;
     31                 pace: 25;
     32             }
     33 
     34             Emitter {//粒子发射器   规定发射粒子规则
     35                  parent.width
     36                 height: 150
     37                 anchors.bottom: parent.bottom
     38                 anchors.bottomMargin: 3
     39                 startTime: 15000  //每隔15s发送一次粒子
     40 
     41                 emitRate: 2//每次发送粒子数目  默认每秒钟发送10个
     42                 lifeSpan: 15000    //最多存活15s
     43 
     44                 acceleration: PointDirection{ y: -6; xVariation: 2; yVariation: 2 }//加速度    y值减小  x方向和y方向存在2个偏移
     45 
     46                 size: 24//初始大小
     47                 sizeVariation: 16//大小可上下浮动范围
     48             }
     49         }
     50     }
     51 
     52     Column {
     53         id: view
     54         y: -(height - 480)
     55          320
     56         
     57         //游戏结束   对应背景图中序号1
     58         GameOverScreen { gameCanvas: canvas }//绑定GameCanvas到gameCanvas导出对象上,主要为了获取游戏结束时,救了多少条鱼和修改游戏状态
     59 
     60         //游戏中
     61         Item {
     62             id: canvasArea
     63              320
     64             height: 480
     65 
     66             Row {//游戏中  顶部波浪1
     67                 height: childrenRect.height
     68                 Image {
     69                     id: wave
     70                     y: 30//距离顶部30像素
     71                     source:"content/gfx/wave.png"//960*70
     72                 }
     73                 Image {
     74                     y: 30//距离顶部30像素
     75                     source:"content/gfx/wave.png"
     76                 }
     77                 NumberAnimation on x { from: 0; to: -(wave.width); duration: 16000; loops: Animation.Infinite }//向左走
     78                 SequentialAnimation on y {//平方向移动的过程中,垂直方向进行序列动画
     79                     loops: Animation.Infinite
     80                     NumberAnimation { from: y - 2; to: y + 2; duration: 1600; easing.type: Easing.InOutQuad }
     81                     NumberAnimation { from: y + 2; to: y - 2; duration: 1600; easing.type: Easing.InOutQuad }
     82                 }
     83             }
     84 
     85             Row {//游戏中  顶部波浪2
     86                 opacity: 0.5
     87                 Image {
     88                     id: wave2
     89                     y: 25
     90                     source: "content/gfx/wave.png"
     91                 }
     92                 Image {
     93                     y: 25
     94                     source: "content/gfx/wave.png"
     95                 }
     96                 NumberAnimation on x { from: -(wave2.width); to: 0; duration: 32000; loops: Animation.Infinite }//向右走
     97                 SequentialAnimation on y {
     98                     loops: Animation.Infinite
     99                     NumberAnimation { from: y + 2; to: y - 2; duration: 1600; easing.type: Easing.InOutQuad }
    100                     NumberAnimation { from: y - 2; to: y + 2; duration: 1600; easing.type: Easing.InOutQuad }
    101                 }
    102             }
    103 
    104             Image {//阳光照射效果1
    105                 source: "content/gfx/sunlight.png"
    106                 opacity: 0.02
    107                 y: 0
    108                 anchors.horizontalCenter: parent.horizontalCenter
    109                 transformOrigin: Item.Top//偏移起始位置
    110                 SequentialAnimation on rotation {//在图片旋转属性上使用序列动画  即:动画依次执行
    111                     loops: Animation.Infinite//动画无限循环
    112                     NumberAnimation { from: -10; to: 10; duration: 8000; easing.type: Easing.InOutSine }
    113                     NumberAnimation { from: 10; to: -10; duration: 8000; easing.type: Easing.InOutSine }
    114                 }
    115             }
    116 
    117             Image {//阳光照射效果2
    118                 source: "content/gfx/sunlight.png"
    119                 opacity: 0.04
    120                 y: 20
    121                 anchors.horizontalCenter: parent.horizontalCenter
    122                 transformOrigin: Item.Top
    123                 SequentialAnimation on rotation {
    124                     loops: Animation.Infinite
    125                     NumberAnimation { from: 10; to: -10; duration: 8000; easing.type: Easing.InOutSine }
    126                     NumberAnimation { from: -10; to: 10; duration: 8000; easing.type: Easing.InOutSine }
    127                 }
    128             }
    129 
    130             Image {//背景网格
    131                 source: "content/gfx/grid.png"
    132                 opacity: 0.5
    133             }
    134             //游戏场景
    135             GameCanvas {
    136                 id: canvas
    137                 anchors.bottom: parent.bottom
    138                 anchors.bottomMargin: 20
    139                 x: 32
    140                 focus: true
    141             }
    142 
    143             InfoBar { anchors.bottom: canvas.top; anchors.bottomMargin: 6;  parent.width }
    144 
    145             //3..2..1..go
    146             Timer {
    147                 id: countdownTimer
    148                 interval: 1000
    149                 running: root.countdown < 5
    150                 repeat: true
    151                 onTriggered: root.countdown++//
    152             }
    153             Repeater {//倒数数据
    154                 model: ["content/gfx/text-blank.png", "content/gfx/text-3.png", "content/gfx/text-2.png", "content/gfx/text-1.png", "content/gfx/text-go.png"]
    155                 delegate: Image {
    156                     visible: root.countdown <= index
    157                     opacity: root.countdown == index ? 0.5 : 0.1
    158                     scale: root.countdown >= index ? 1.0 : 0.0
    159                     source: modelData
    160                     Behavior on opacity { NumberAnimation {} }
    161                     Behavior on scale { NumberAnimation {} }//NumberAnimation继承自PropertyAnimation,duration值默认为250ms
    162                 }
    163             }
    164         }
    165         //游戏启动页  仅仅包含游戏标题、带气泡的鱼和开始按钮   背景色和上浮的气泡是由根元素下的粒子系统(particles)完成
    166         NewGameScreen {
    167             onStartButtonClicked: root.passedSplash = true//游戏开始
    168         }
    169     }
    170 
    171     property int countdown: 10
    172     Timer {
    173         id: gameStarter
    174         interval: 4000
    175         running: false
    176         repeat: false
    177         onTriggered: Logic.startGame(canvas);//等待4s中启动新的一局  等待的过程中countdownTimer定时器 每隔1s更新倒数图片  显示3 2 1 go
    178     }
    179 
    180     states: [
    181         State {//游戏开始   当在游戏第一次启动时gameOver为false   passedSplash为false
    182             name: "gameOn"; 
    183             when: gameState.gameOver == false && passedSplash    //当游戏状态不为结束并且passedSplash为真时触发  passedSplash值由NewGameScreen信号的处理槽函数修改
    184             PropertyChanges { target: view; y: -(height - 960) }//480标示显示第二页
    185             StateChangeScript { script: root.countdown = 0; }//countdown重置为0  开始新游戏时  倒数3 2 1
    186             PropertyChanges { target: gameStarter; running: true }//调用游戏开始定时器  启动游戏
    187         },
    188         State {//对应背景图第1页
    189             name: "gameOver"; 
    190             when: gameState.gameOver == true //当游戏状态为结束时触发
    191             PropertyChanges { target: view; y: 0 }//Column定位器滚动到最顶端   也即游戏结束,请重新开始页面
    192         }
    193     ]
    194 
    195     transitions: Transition {//对指定属性进行动画过渡
    196         NumberAnimation { properties: "x,y"; duration: 1200; easing.type: Easing.OutQuad }
    197     }
    198     //组件加载完毕时,背景图定位到第三页,即游戏启动页
    199     Component.onCompleted: gameState = Logic.newGameState(canvas);
    200 }

    3、源码下载

      qml maroon小游戏

  • 相关阅读:
    js刷新
    getHibernateTemplate()为NUll
    struts2+hibernate+spring+jquery返回json List列表
    windowopen
    web配置详解
    缓存
    uuid-不好之处
    多对多转化一对多
    多对多拆成两个 多对一
    我的github地址账号和密码
  • 原文地址:https://www.cnblogs.com/swarmbees/p/6475993.html
Copyright © 2020-2023  润新知