• JS开发HTML5游戏《神奇的六边形》(四)


    近期出现一款魔性的消除类HTML5游戏《神奇的六边形》,今天我们一起来看看如何通过开源免费的青瓷引擎(www.zuoyouxi.com)来实现这款游戏。

    (点击图片可进入游戏体验)

    因内容太多,为方便大家阅读,所以分成部分来讲解。

    本文为第四部分,主要包括:

    16.分数往上飘动画

    17.形状飞入动画

    18.其他动画表现添加

    19.游戏结束界面

    20. 添加LOGO

    21. 渲染优化

    若要一次性查看所有文档,也可点击这里

    十六. 分数往上飘的动画

    在表现加分时,分数会有个缩放的效果,然后往上移动并淡出。这些效果可以通过Tween Group来组合实现。

    1. 在board节点下,创建UIText节点,取名为score,属性设置如下:

    • 文本居中显示
    • 文本颜色为白色,大小40,外加6像素描边

    2. 为score节点,添加TweenScale组件,控制缩放动画,属性设置如下图(注意设置Tween Group=1):

    3. 为score节点,添加TweenPosition组件,控制节点向上移动,属性设置如下图(注意设置Tween Group=1):

    4. 为score节点,添加TweenAlpha组件,控制节点淡出,属性设置如下图(注意设置Tween Group=1):

    5. 将score节点拖拽到Assets/prefab目录,创建预制。然后从场景中删除。

    6. 在Scripts/ui下新建脚本:FlyScore.js

     1 /**
     2   * 分数飘出来的特效
     3   */
     4  var FlyScore = qc.defineBehaviour('qc.tetris.FlyScore', qc.Behaviour, function() {
     5      var self = this;
     6  }, {
     7      scorePrefab: qc.Serializer.PREFAB
     8  });
     9 
    10  /**
    11   * 开始播放冒分数动画
    12   */
    13  FlyScore.prototype.play = function(pos, score) {
    14      var self = this;
    15 
    16      var scoreOb = self.game.add.clone(self.scorePrefab, self.gameObject);
    17      scoreOb.text = '' + score;
    18      var tp = scoreOb.getScript('qc.TweenPosition');
    19      scoreOb.anchoredX = qc.Tetris.board.data[pos].x;
    20      scoreOb.anchoredY = qc.Tetris.board.data[pos].y;
    21      tp.from = new qc.Point(scoreOb.x, scoreOb.y);
    22      tp.to = new qc.Point(tp.from.x, tp.from.y - 80);
    23      tp.resetGroupToBeginning();
    24      tp.playGroupForward();
    25      self.game.timer.add(600, function() {
    26          scoreOb.destroy();
    27      });
    28  };

    7. 将FlyScore脚本挂载到board节点,设置scorePrefab属性为Assets/prefab/score.bin。保存场景。

     

    十七. 形状飞入动画

    1. 双击Assets/prefab/Blocks.bin,编辑预制

    2. 选中Blocks节点,添加TweenPosition组件,属性设置如下:

    3. 保存预置

    4. 打开Scripts/ui/Pool.js,添加flyIn接口,处理飞入动画的播放

     1 /**
     2   * 播放飞入的动画
     3   */
     4  Pool.prototype.flyIn = function(index) {
     5      var self = this, o = self.gameObject, children = o.children;
     6      var offset = o.width * (0.5 - 0.165);
     7 
     8      // 先确保位置都正确
     9      self.resize();
    10 
    11      if (index === 0) {
    12          var o = children[0], c = o.getScript('qc.tetris.BlocksUI');
    13          c.flyIn(offset);
    14      }
    15      if (index === 0 || index === 1) {
    16          var o = children[1], c = o.getScript('qc.tetris.BlocksUI');
    17          c.flyIn(offset);
    18      }
    19 
    20      var o = children[2], c = o.getScript('qc.tetris.BlocksUI');
    21      c.flyIn(offset);
    22  };

    5. 打开Scripts/ui/BlocksUI.js,添加flyIn接口,处理单个形状飞入动画

     1  /**
     2   * 飞入动画
     3   */
     4  BlocksUI.prototype.flyIn = function(offset) {
     5      var self = this,
     6          tp = self.getScript('qc.TweenPosition');
     7 
     8      tp.delay = 0.5;
     9      tp.to = new qc.Point(self.gameObject.x, self.gameObject.y);
    10      tp.from = new qc.Point(tp.to.x + offset, tp.to.y);
    11      tp.resetToBeginning();
    12      tp.playForward();
    13  };

    6. 运行游戏,查看下效果:已经可以正常游戏了不是?

     

    十八. 其他动画表现添加

    目前还缺少两个表现:加分动画(数字跳动)、形状回弹效果。其方法和之前讲述的大致一样,这里简略做个说明。

    加分动画

    1. 修改CurrentScore.js代码,添加动画播放代码:

     1 var CurrentScore = qc.defineBehaviour('qc.tetris.CurrentScore', qc.Behaviour, function() {
     2      var self = this;
     3      self.runInEditor = true;
     4  }, {
     5  });
     6 
     7  /**
     8   * 初始化处理
     9   */
    10  CurrentScore.prototype.awake = function() {
    11      var self = this, div = self.gameObject.div;
    12 
    13      div.className = 'score_current';
    14      self.setScore(qc.Tetris.score.current);
    15  };
    16 
    17  /**
    18   * 更新最新的高分
    19   */
    20  CurrentScore.prototype.setScore = function(best) {
    21      best = best || qc.Tetris.score.current;
    22 
    23      // 做动画表现,从old -> best
    24      var old = this.gameObject.div.innerHTML * 1;
    25      this.delta = best - old;
    26      if (this.delta <= 0)
    27          this.gameObject.div.innerHTML = '' + best;
    28 
    29      // 0.2s内需要播放完毕,计算每秒增加的数量
    30      this.step = this.delta / 0.2;
    31 
    32      // 播放缩放动画
    33      var ts = this.getScript('qc.TweenScale');
    34      ts.resetToBeginning();
    35      ts.playForward();
    36  };
    37 
    38  /**
    39   * 动画表现
    40   */
    41  CurrentScore.prototype.update = function() {
    42      if (this.delta <= 0) {
    43          // 动画表现完毕了
    44          return;
    45      }
    46 
    47      var step = Math.round(this.step * this.game.time.deltaTime / 1000);
    48      this.delta -= step;
    49      var old = this.gameObject.div.innerHTML * 1 + step;
    50      if (old > qc.Tetris.score.current) {
    51          old = qc.Tetris.score.current;
    52          this.delta = 0;
    53      }
    54      this.gameObject.div.innerHTML = '' + old;
    55  };

    2. 为场景节点UIRoot/score添加TweenScale组件,属性设置如下:

    形状回弹效果

    修改BlocksUI.js代码,添加backAni方法:

     1 /**
     2      * 退回到原来位置的动画表现
     3      */
     4     BlocksUI.prototype.backAni = function(x, y) {
     5         var self = this, o = self.gameObject,
     6             tp = self.getScript('qc.TweenPosition');
     7         if (tp.enable) return;
     8         tp.delay = 0;
     9         tp.from = new qc.Point(x, y);
    10         tp.to = new qc.Point(self.gameObject.x, self.gameObject.y);
    11         tp.stop();
    12         tp.resetToBeginning();
    13         self.gameObject.interactive = false;
    14         tp.onFinished.addOnce(function() {
    15             self.gameObject.interactive = true;
    16             o.parent.getScript('qc.tetris.Pool').resize();
    17         });
    18         tp.playForward();
    19     };
    

     在onDragEnd方法中,当无法放入棋盘的分支中,加入backAni的调用:

     1 BlocksUI.prototype.onDragEnd = function(e) {
     2         var self = this,
     3             o = self.gameObject;
     4         self.drag = false;
     5 
     6         if (self.flagBlocks.visible && self.lastPos) {
     7             // 放到这个位置中去
     8             self.drop = true;
     9             qc.Tetris.operation.putIn(self.index, self.lastPos, self.data);
    10         }
    11         else {
    12             // !!!!!!
    13             // 在这个分支中修改为如下代码
    14             var x = o.x, y = o.y;
    15             self.reset();
    16             o.parent.getScript('qc.tetris.Pool').resize();
    17             self.backAni(x, y);
    18         }
    19 
    20         // 显示标记可以干掉了
    21         self.flagBlocks.destroy();
    22         delete self.flagBlocks;
    23     };
       

    十九. 游戏结束界面

    游戏界面包含2个页面:

     

    这两个页面使用html+css元素快速搭建(DOM节点)。步骤如下:

    1. 在UIRoot下创建Dom节点,取名GameOver

           

    • 居中显示,大小为340*441
    • 缩放1.5倍
    • 设置节点可以交互(碰撞范围非常大,这样底部游戏所有的元素都无法接收事件了)
    • 设置className=gameover

    2. 在Scripts/ui下新建脚本:GameOverUI.js

     1 /**
     2   * 游戏结束界面
     3   */
     4  var GameOverUI = qc.defineBehaviour('qc.tetris.GameOverUI', qc.Behaviour, function() {
     5      var self = this;
     6      qc.Tetris.gameOver = self;
     7      self.runInEditor = true;
     8  }, {
     9      shareClue: qc.Serializer.PREFAB
    10  });
    11 
    12  GameOverUI.prototype.awake = function() {
    13      var div = this.gameObject.div;
    14      var score = qc.Tetris.score.current;
    15      var percent = 40;
    16 
    17      this.rawHtml =
    18          '<div class="gameover_title">Game Over</div>' +
    19          '<div class="gameover_score">__SCORE__</div>' +
    20          '<div class="gameover_pos">你击败了全球__PERCENT__%的玩家</div>' +
    21          '<div class="gameover_desc">让朋友们来膜拜大神吧!</div>' +
    22          '<div class="gameover_share" onclick="qc.Tetris.gameOver.share()" ontouchstart="qc.Tetris.gameOver.share()">马上告诉他们</div>' +
    23          '<div class="gameover_restart" onclick="qc.Tetris.gameOver.restart()" ontouchstart="qc.Tetris.gameOver.restart()">再玩一次</div>' +
    24          '<div class="gameover_act">' +
    25          '    <div class="gameover_logo"></div><div class="gameover_act_desc">点击关注送好礼</div> ' +
    26          '</div>' +
    27          '<div class="clear"></div>';
    28      this.rawHtml = this.rawHtml.replace('__SCORE__', '' + score);
    29      this.rawHtml = this.rawHtml.replace('__PERCENT__', '' + percent);
    30      div.innerHTML = this.rawHtml;
    31  };
    32 
    33  GameOverUI.prototype.onDestroy = function() {
    34      delete qc.Tetris.gameOver;
    35  };
    36 
    37  GameOverUI.prototype.share = function() {
    38      // 打开share界面
    39      this.game.add.clone(this.shareClue, this.gameObject.parent);
    40  };
    41 
    42  GameOverUI.prototype.restart = function() {
    43      this.gameObject.destroy();
    44      qc.Tetris.operation.restart();
    45  };

    本界面主要通过内置的DOM来进行处理,具体不多作解释(您需要有一定的web前端开发基础)

    1. 打开Assets/css/style.css,添加如下样式表:

     1 /* Game Over */
     2  .gameover {
     3      text-align: center;
     4      width: 100%;
     5      font-family: arial, sans serif;
     6      background: url("../raw/bg.png") no-repeat;
     7      color: #000000;
     8  }
     9  .gameover_title {
    10      font-size: 40px;
    11      margin-top: 10px;
    12      height: 50px;
    13      text-align: center;
    14  }
    15  .gameover_score {
    16      font-size: 90px;
    17      margin-top: -15px;
    18      height: 98px;
    19      text-align: center;
    20  }
    21  .gameover_pos {
    22      text-align: center; font-size: 28px;
    23      height: 40px;
    24  }
    25  .gameover_desc {
    26      text-align: center; color: #ffffff;
    27      height: 30px; font-size: 20px; line-height: 100%;
    28  }
    29  .gameover_share {
    30      background: url("../raw/btn_blue.png") center no-repeat;
    31      height: 76px;
    32      line-height: 76px;
    33      font-size: 30px;
    34      color: #ffffff;
    35      text-align: center;
    36  }
    37  .gameover_restart {
    38      background: url("../raw/btn_yellow.png") center no-repeat;
    39      text-align: center; color: #ffffff;
    40      height: 76px;
    41      line-height: 76px;
    42      font-size: 30px;
    43      margin-top: 10px;
    44  }
    45 
    46  .gameover_logo {
    47      float: left;
    48      background: url("../raw/logo.png") no-repeat;
    49      width: 64px;
    50      height: 62px;
    51      margin: 8px 0px 0px 2px;
    52  }
    53  .gameover_act_desc {
    54      color: #ffffff;
    55      float: right;
    56      width: 250px;
    57      text-align: left;
    58      height: 62px;
    59      line-height: 62px;
    60      margin-top: 8px;
    61      font-size: 28px;
    62  }
    63  .clear { clear: both; }

    2. GameOverUI.js脚本挂载到GameOver对象,刷新查看下效果。

    3. 将GameOver节点拖拽到Assets/prefab下,创建预制。然后从场景中删除。游戏结束界面就完成了。下面构建分享页面

    4. 在UIRoot下新建Dom节点:shareClue,参数设置如下:

    大小设置为铺满整个屏幕,className=share

    5.在shareClue新建Dom节点:arraw,用来显示箭头,定位为右上角,参数设置如下:

     

    6. 在shareClue新建Dom节点:desc,用来显示提示语内容。参数设置如下:

    7. 在Scripts/ui新建文件ShareClue.js

     1 /**
     2   * 分享提示页面
     3   */
     4  var ShareClue = qc.defineBehaviour('qc.tetris.ShareClue', qc.Behaviour, function() {
     5      var self = this;
     6      self.runInEditor = true;
     7  }, {
     8      descNode: qc.Serializer.NODE
     9  });
    10 
    11  /**
    12   * 初始化
    13   */
    14  ShareClue.prototype.awake = function() {
    15      this.descNode.div.innerHTML = '请点击右上角<br/>分享给您的好友吧<br/>看下他们能取得多少分';
    16  };
    17 
    18  /**
    19   * 点击时干掉本页面
    20   */
    21  ShareClue.prototype.onClick = function() {
    22      this.gameObject.destroy();
    23  };

    8. 将ShareClue脚本挂载到shareClue节点上,设置Desc Node为下面的desc节点

    9. 编辑Assets/css/style.css,添加样式表:

     1 /* 分享提示 */
     2 .share {
     3     background-color: #000000;
     4     opacity:0.5;
     5     filter:alpha(opacity=50);
     6     text-align: center; color: #ffffff;
     7 }
     8 .share_clue {
     9     background-image: url("../raw/arrows.png");
    10 }
    11 .share_desc {
    12     color: #ffffff; font-size: 60px; text-align: center;
    13     line-height: 70px;
    14 }

    10. 保存场景并刷新之,查看效果。

    11. 将shareClue拖拽到Assets/prefab,创建预置,然后从场景中删除。

    12. 选中UIRoot节点,设置UIManager组件的gameOverPrefab=Assets/prefab/GameOver.bin

    13. 双击Assets/prefab/GameOver.bin编辑预置,设置shareClue = Assets/prefab/shareClue.bin

    14. 打开Scripts/logic/Board.js,补齐死亡逻辑:

     1 Object.defineProperties(Board.prototype, {
     2     /**
     3      * @property {boolean} die - 当前是否已经死亡了
     4      * @readonly
     5      */ 
     6     die: {
     7         get: function() {
     8             // 如果有单个点形状的,一定死不了
     9             var pool = qc.Tetris.Shapes.pool;
    10             for (var i = 0; i < pool.length; i++) {
    11                 if (pool[i].list.length === 1) return false;
    12             }
    13 
    14             // 逐一检查,各形状能否扔来进来
    15             for (var pos in this.data) {
    16                 for (var i = 0; i < pool.length; i++) {
    17                     if (this.checkPutIn(pos, pool[i].list)) return false;
    18                 }
    19             }
    20             return true;
    21         }
    22     }
    23 });

    15. 在Scripts/operation创建脚本Restart.js,处理重新开始游戏的逻辑:

     1 /**
     2  * 请求重新开始游戏
     3  */
     4 qc.Tetris.operation.restart = function() {
     5     var game = qc.Tetris.game,
     6         ui = game.ui;
     7 
     8     // 清空棋盘信息
     9     qc.Tetris.board.restart();
    10 
    11     // 当前分数清0
    12     qc.Tetris.score.current = 0;
    13 
    14     // 3个形状重新替换掉
    15     qc.Tetris.Shapes.restart();
    16 
    17     // 界面初始化
    18     ui.restart();
    19 };

    16. 游戏的整体逻辑已经完成,测试看看

     

    二十. 添加Logo

    具体请自行参考工程理解,涉及的场景节点有:UIRoot/logo和UIRoot/company
    涉及的脚本有:Scripts/ui/Company.js
    涉及的样式表:

    1    /* logo */
    2     .logo {
    3         background-image: url("../raw/qici.png");
    4     }
    5     .company {
    6         color:#ffffff;font-size: 24px;
    7     }

    二十一. 渲染优化

    本游戏,使用图片比较少,并且大部分都采用DOM的方式渲染,非常高效。
    唯一可以做优化的是3个形状:每个形状下面挂载了多个格子节点,并且大部分时间是保持不变的,因此可以将这些格子节点缓存到canvas以提升渲染效率(多耗了点内存)。步骤如下:

    1. 双击Assets/Prefab/Blocks.bin编辑预置,在预置根节点添加内置组件:CacheAsBitmap,设置属性如下图:

    这个组件的用途是将节点渲染到独立的canvas上并缓存起来。

    2. 保存预制。当内部的格子发生变化时,需要“设脏”(确保缓存能被刷新)。打开BlocksUI.js,在reset方法最后面加入:

     1 self.getScript('qc.CacheAsBitmap').dirty = true; 

    添加帧率查看

    在UIRoot挂载Dom节点:debug,属性设置如下:

    2. 编辑style.css,添加样式表:

     1 .debug { color:green; font-size:18px; } 

    3. 为debug挂载内置脚本:DebugViewer,刷新下页面,就可以查看帧率情况了:

    其中,FPS为游戏实际运行帧率;Total为游戏一次主循环所使用的时间;Logic为游戏逻辑损耗的时间(即Preupdate、Update等),Render为渲染时间。

    4. 在最终发布时,需要将debug隐藏掉

     

        《神奇的六边形》 到此就分享完毕了,感谢各位的耐心阅读,若发现问题,还请大家及时指出,帮助我们不断完善。以后还将陆续分享其他好游戏的开发经验,望大家继续关注,谢谢!

     

    上一篇:JS开发HTML5游戏《神奇的六边形》(三) 

     

     

     

     

  • 相关阅读:
    MapXtreme 2005
    QQ在线源码
    Oralce rowid
    MOSS2007 安装与部署(下)
    MapXtreme 2005新增内容
    MOSS 2007安装与部署(上)
    PL/SQL中的where子句比较
    Oracle中插入日期型数据
    在HTML语言网页中加载视频的代码
    HTTP 错误 500.24 Internal Server Error 检测到在集成的托管管道模式下不适用的 ASP.NET 设置。
  • 原文地址:https://www.cnblogs.com/qici/p/4956577.html
Copyright © 2020-2023  润新知