• 用html+js写一个简单的游戏


    入驻博客园后的第一篇技术博客,这次来聊聊前端js技术应用。

     

    最近接触一款手机游戏:PopStar(消灭星星),第一次在手机上玩就被深深吸引了。玩了几局后发现,想要一路过关斩将,就要想方设法得到更高的分数,所以要么一次消灭更多的星星,要么每一局剩下更少的星星(10个以下有奖励分数,挺可观的)(具体信息上网找攻略,这里不做详述)。

    到此,技术宅有了一个自己写PopStar的计划,想到就开始动手 —— 这次选择html页面+js来实现。

    GO ...

     

    首先,需要知道这款游戏的各种规则,最基本的就是,消灭x个星星,得到(x2 * 5)分数,最后剩下星星数和得分:10(0)、9(380)、8(720)、7(1020) ... 1(1980)、0(2000)[神!星星眼仰望]

    好了,首先需要有一个界面,这里就直接参照PopStar的游戏界面,简化了一番,用html写出:

      1 <style type="text/css">
      2 body {
      3     margin: 0;
      4     padding: 0;
      5     font-size: 14px;
      6     font-family: Arial;
      7 }
      8 
      9 .container {
     10     width: 600px;
     11     margin: 10px auto;
     12     border: 1px solid #aaaaaa;
     13     padding: 10px;
     14 }
     15 
     16 .label {
     17     display: inline-block;
     18     padding: 2px 9px;
     19     background-color: rgba(58, 135, 173, 0.75);
     20     color: #ffffff;
     21     text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
     22     white-space: nowrap;
     23     vertical-align: baseline;
     24     -webkit-border-radius: 3px;
     25     -moz-border-radius: 3px;
     26     border-radius: 3px;
     27 }
     28 
     29 .row:after,
     30 .box:after {
     31     display: table;
     32     content: "";
     33     line-height: 0;
     34     clear: both;
     35 }
     36 
     37 .stage,
     38 .difficulty,
     39 .target {
     40     float: left;
     41     width: 33.33333333%;
     42     padding: 5px 0 15px;
     43 }
     44 
     45 .score {
     46     text-align: center;
     47 }
     48 
     49 .message {
     50     height: 30px;
     51     line-height: 30px;
     52     margin: 15px 0 20px;
     53     text-align: center;
     54 }
     55 
     56     .message .selected {
     57         font-size: 10px;
     58         color: #999999;
     59     }
     60 
     61     .message .encourage {
     62         font-size: 16px;
     63         font-weight: bold;
     64     }
     65 
     66 .box {
     67     position: relative;
     68 }
     69 
     70     .box div {
     71         /*position: absolute;*/
     72         cursor: pointer;
     73         -webkit-border-radius: 3px;
     74         -moz-border-radius: 3px;
     75         border-radius: 3px;
     76 
     77         /* for text */
     78         float: left;
     79         width: 58px;
     80         height: 58px;
     81         margin: 1px;
     82     }
     83 
     84 .star-red {
     85     background-color: rgba(255, 0, 0, 0.5);
     86 }
     87 
     88 .star-green {
     89     background-color: rgba(0, 255, 0, 0.5);
     90 }
     91 
     92 .star-blue {
     93     background-color: rgba(0, 0, 255, 0.5);
     94 }
     95 
     96 .star-yellow {
     97     background-color: rgba(255, 255, 0, 0.5);
     98 }
     99 
    100 .star-lightblue {
    101     background-color: rgba(0, 255, 255, 0.5);
    102 }
    103 </style>
    104 
    105 <div class="container">
    106     <div class="row">
    107         <div class="stage">
    108             级数
    109             <label id="stage" class="label">1</label>
    110         </div>
    111         <div class="difficulty">
    112             难度
    113             <label id="difficulty" class="label">1</label>
    114         </div>
    115         <div class="target">
    116             目标分数
    117             <label id="target" class="label">1</label>
    118         </div>
    119     </div>
    120     <div class="score">
    121         分数
    122         <label id="score" class="label">1</label>
    123     </div>
    124     <div class="message">
    125         <label id="message_selected" class="selected"></label>
    126         <label id="message_encourage" class="encourage"></label>
    127         <label id="message_failed" class="encourage"></label>
    128     </div>
    129 
    130     <div id="box" class="box">
    131         <!-- 100个div标签 -->
    132     </div>
    133 </div>

    这里仅聊聊实现逻辑,所以只是用代码简单的设置了5种颜色的星星,也可以用图片做得再漂亮一点。

     

    有了界面接下来开始用js来操作。

    引入jquery.js文件(比较懒,直接在jq官网down了最新版的,没去比较更新了什么内容),接着设置几个值:

     1 var maps = {
     2     xItemCount: 10, // 横排个数
     3     yItemCount: 10, // 竖排个数
     4     itemMargin: 1,
     5     difficulty: 5,  // 难度
     6     braveScore: { value: 125, message: 'Brave!' },   // 超过125分,鼓励
     7     failedMessage: 'Sorry, you are failed!'
     8 }
     9 var starClass = [
    10     'star-red',
    11     'star-blue',
    12     'star-yellow',
    13     'star-green',
    14     'star-lightblue'
    15 ];
    16 
    17 var Stage = 0, Target, Score = 0;
    18 var $selecteditems = [];
    19 
    20 var $box = $('#box');
    21 $box.css({ height: $box[0].clientWidth });
    22 var boxWidth = $box[0].clientWidth;
    23 var itemWidth = boxWidth / maps.xItemCount - maps.itemMargin * 2;

    (因为css里只设置了5种星星样式,所以最高难度只到5)

     

    接着,需要1个Array的自定义方法exists,用来判断星星Item是否在选择列表中;还需要3个计算分数的方法:_getTarget(获取目标级别的目标分数,原版中目标分数并非以一个相同的数字递增,这里为了方便就采取这种计算方法)、_getScore(根据选择、消灭的星星个数计算得分)、_getAdditionalScore(每局最后计算奖励分数)

     1 Array.prototype.exists = function ($item) {
     2     var isIn = false;
     3     this.forEach(function ($i) {
     4         if ($i.attr('id') === $item.attr('id')) { isIn = true; }
     5     });
     6     return isIn;
     7 };
     8 
     9 
    10 /* 获取目标分数 */
    11 var _getTarget = function (stage) {
    12     if (stage === 1) { return 1000; }
    13     else if (stage === 2) { return 2500; }
    14     else { return (stage - 1) * 2500 }
    15 };
    16 
    17 /* 计算得分 */
    18 var _getScore = function () {
    19     return $selecteditems.length * $selecteditems.length * 5;
    20 }
    21 
    22 /* 计算奖励分数 */
    23 var _getAdditionalScore = function (count) {
    24     if (count >= 10) return 0;
    25     else return _getAdditionalScore(count + 1) + (380 - (9 - count) * 40);
    26     /* 利用高中数列的计算方法可以得出正确的公式,偷个小懒直接用最简单递归计算得分 */
    27 }

     

    然后,需要一个自动生成星星的方法:

    随机获取一个大于等于0,小于maps.difficulty的整数

    var val = Math.floor(Math.random() * maps.difficulty);

    该数值作为每颗星星的值,并获取对应的星星样式(starClass),接着计算星星位置(top、left),这里排列方式为:

    从右下角开始,往上堆入一列,再往右堆入第2列 ... ...

     1 var _newStage = function () {
     2     $('#stage').html(++Stage);
     3     Target = _getTarget(Stage);
     4     $('#target').html(Target);
     5 
     6     for (var x = 0; x < maps.xItemCount; ++x) {
     7         for (var y = 0; y < maps.yItemCount; ++y) {
     8             var val = Math.floor(Math.random() * maps.difficulty);
     9             var top = boxWidth - (itemWidth + maps.itemMargin * 2) * (y + 1);
    10             var left = (itemWidth + maps.itemMargin * 2) * x;
    11             $box.append(
    12                 $('<div>', { id: x + '_' + y, 'class': starClass[val], css: { top: top, left: left,  itemWidth, height: itemWidth } })
    13                     .data({ x: x, y: y, value: val })
    14                     .click(function () {
    15                         var $item = $(this);
    16 
    17                         if ($selecteditems.length) {
    18                             if ($selecteditems.exists($item)) {
    19                                 // 点击的方块在选中的方块列表里面
    20                                 _clearSelectItems();
    21                                 return;
    22                             } else {
    23                                 // 点击的方块不在选中的方块列表里面
    24                                 _clearItemStatus();
    25                             }
    26                         }
    27 
    28                         _selectItems($item);
    29                         _setSelectedItemsStatus();
    30                     })
    31             );
    32         }
    33     }
    34 }

     

    试试效果!

     

    当点击某一颗星星时,有3种情况:

    1、原本什么都么有选择($selecteditems.length == 0)

    则选择与之相邻(上下左右)的,数值一样的星星。若是数量为1,则取消该次选择;若数量大于等于2,则全部显示选择,并计算出可得分数。

    这里通过当前星星Item的ID,依次检查左、右、上、下4颗星星的数值,用递归的方法,一层层寻找,最终取得所有相连且数值相同的星星Item,保存在$selecteditems中。

     1 /* 寻找相邻方块 */
     2 var _selectItems = function ($item) {
     3     $selecteditems.push($item);
     4 
     5     var x = $item.data('x');
     6     var y = $item.data('y');
     7     var val = $item.data('value');
     8 
     9     if (x - 1 >= 0) {
    10         var $newItem = $('#' + (x - 1) + '_' + y, $box);
    11         if ($newItem.length && $newItem.data('value') === val && !$selecteditems.exists($newItem)) {
    12             _selectItems($newItem);
    13         }
    14     }
    15     if (x + 1 < maps.xItemCount) {
    16         var $newItem = $('#' + (x + 1) + '_' + y, $box);
    17         if ($newItem.length && $newItem.data('value') === val && !$selecteditems.exists($newItem)) {
    18             _selectItems($newItem);
    19         }
    20     }
    21     if (y - 1 >= 0) {
    22         var $newItem = $('#' + x + '_' + (y - 1), $box);
    23         if ($newItem.length && $newItem.data('value') === val && !$selecteditems.exists($newItem)) {
    24             _selectItems($newItem);
    25         }
    26     }
    27     if (y + 1 < maps.yItemCount) {
    28         var $newItem = $('#' + x + '_' + (y + 1), $box);
    29         if ($newItem.length && $newItem.data('value') === val && !$selecteditems.exists($newItem)) {
    30             _selectItems($newItem);
    31         }
    32     }
    33 }
    34 
    35 /* 设置选中方块状态 */
    36 var _setSelectedItemsStatus = function () {
    37     if ($selecteditems.length <= 1) {
    38         $selecteditems = [];
    39         $('#message_selected').empty();
    40     } else {
    41         $.each($selecteditems, function () {
    42             this.css({  itemWidth - 4, height: itemWidth - 4, border: '2px solid rgba(0, 0, 0, 0.6)' });
    43         });
    44 
    45         $('#message_selected').html('个数:' + $selecteditems.length + ',分数:' + _getScore());
    46         $('#message_encourage').empty();
    47     }
    48 }

     

    2、原本已有选择($selecteditems.length >= 2),但本次点击的星星不在已选择的星星列表里

    则清空已选择的星星状态、列表,并依照1重新检查、计算、选择。

    1 /* 清除方块状态 */
    2 var _clearItemStatus = function () {
    3     $box.children().css({  itemWidth, height: itemWidth, border: 'none' });
    4     $selecteditems = [];
    5 }

     

    3、原本已有选择($selecteditems.length >= 2),且本次点击的星星在已选择的星星列表里

    则需要删除已选择的星星,并重新排列剩余的星星。

     1 /* 移除选中方块 */
     2 var _clearSelectItems = function () {
     3     var score = _getScore();
     4     Score += score;
     5     $('#score').html(Score);
     6     $('#message_selected').empty();
     7 
     8     if (score >= maps.braveScore.value) {
     9         $('#message_encourage').html(maps.braveScore.message);
    10 
    11         setTimeout(function () {
    12             $('#message_encourage').empty()
    13         }, 2000);
    14     }
    15 
    16     $selecteditems.forEach(function ($i) {
    17         $i.fadeOut(0, function () {
    18             $i.remove();
    19         });
    20     });
    21     $selecteditems = [];
    22 
    23     var setX = 0, setY = 0;
    24     for (var x = 0; x < maps.xItemCount; ++x) {
    25         for (var y = 0; y < maps.yItemCount;) {
    26             var $item = $('#' + x + '_' + y, $box);
    27 
    28             if ($item.length) {
    29                 if (x != setX || y != setY) {
    30                     var top = boxWidth - (itemWidth + maps.itemMargin * 2) * (setY + 1);
    31                     var left = (itemWidth + maps.itemMargin * 2) * setX;
    32                     $item
    33                         .attr({ id: setX + '_' + setY })
    34                         .data({ x: setX, y: setY })
    35                         .animate({ top: top, left: left }, 300);
    36                 }
    37 
    38                 ++setY;
    39                 ++y;
    40                 if (setY >= maps.yItemCount || y >= maps.yItemCount) {
    41                     ++setX;
    42                     setY = 0;
    43                 }
    44             } else {
    45                 ++y;
    46                 if (y >= maps.yItemCount) {
    47                     if (setY > 0) {
    48                         ++setX;
    49                     }
    50                     setY = 0;
    51                 }
    52             }
    53         }
    54     }
    55 
    56     _checkSingleItems();
    57 }

     

    已经有效果了!(自己先得瑟一下 :D 玩多一会)

     

    最后,在消灭了选择的星星后,需要判断剩余的星星是否是单独的。若不是单独的,则不做任何操作;若是,则需要计算奖励分为多少,算总分后,若不足目标分数,显示失败,否则,清除所有剩余星星,并重新开始下一级别。

     1 /* 检查单个的方块 */
     2 var _checkSingleItems = function () {
     3     var $items = $box.children();
     4     var isSingle = true;
     5     for (var i = 0, length = $items.length; i < length; ++i) {
     6         _selectItems($($items[i]));
     7         var count = $selecteditems.length;
     8         $selecteditems = [];
     9 
    10         if (count > 1) {
    11             isSingle = false;
    12             break;
    13         }
    14     }
    15 
    16     if (isSingle) {
    17         setTimeout(function () {
    18             var additionalScore = _getAdditionalScore($items.length);
    19             Score += additionalScore;
    20             $('#score').html(Score);
    21 
    22             if (additionalScore) {
    23                 $('#message_encourage').html('Additional Score ' + additionalScore);
    24             }
    25 
    26             if (Score < Target) {
    27                 $('#message_encourage').empty();
    28                 $('#message_failed').html(maps.failedMessage);
    29             } else {
    30                 setTimeout(function () {
    31                     $('#message_encourage').empty()
    32                 }, 2000);
    33 
    34                 if ($items.length) {
    35                     $items.fadeOut(300, function () {
    36                         $items.remove();
    37                         _newStage();
    38                     });
    39                 } else {
    40                     _newStage();
    41                 }
    42             }
    43         }, 500);
    44     }
    45 }

     

    完成!

    基本可以玩了,当然,没有经过系统的测试,肯定会存在许多bug。

    (截至博主发布时,已无意点到一个bug了  T-T  不过这里只是技术逻辑的讨论,不再做深层的检查修复)

     

    附上下载路径:https://files.cnblogs.com/SugarLSG/PopStar.zip

     


    [ 本次探讨只做技术研究,不用于任何商业目的;代码并非出自网络,许多不成熟的地方请各位多多见谅,虚心请教; ]

  • 相关阅读:
    Java 处理 XML
    Spring MVC全局异常处理
    ThreadLocal与Synchronized区别
    office xml 方式
    Nginx ssl证书部署方法
    利用nginx来屏蔽指定的user_agent的访问以及根据user_agent做跳转
    Nginx使用GZIP来压缩网页
    Kibana安装与基本用法(ELK)
    用Linux自带的Logrotate来管理日志
    Net accounts命令
  • 原文地址:https://www.cnblogs.com/SugarLSG/p/3134210.html
Copyright © 2020-2023  润新知