zoukankan      html  css  js  c++  java
  • 地图游戏初尝

    《迷宫游戏》

      今天第一次做移动端地图游戏,游戏虽简,获益匪浅。

      以前没做过地图游戏,一直不懂怎么构造地图和检测路径,我想这就是做web地图游戏的关键吧。慢慢地有了思路,有了常用方案,那么就算换了一个很复杂的图也不怕实现不了了。不过web app一直离不开性能问题,尤其在移动端。假如使用标签元素来构图的话(像本例),当操作的数量达到一定后,流畅度就好大打折扣。因为web app一般是无法得到硬件加速的,所以性能远不如native app。除非开启3D加速(GPU),如移动大图时使用translate3D,否则就会有明显的卡顿感。

      还好,由于本例只用了几百个块,操作简单,所以暂时不会有卡顿感。下面我就分部讲讲如何实现一个web地图小游戏吧,有了这个基础,就算换上复杂的图也知道怎么实现了。

      1、建图拿数据

      首先介绍一款软件——Tiled Map Editor,这是一个地图编辑器工具,它可以辅助我们更快更准确地编辑web游戏地图。具体的操作我就不讲了,可以自行百度教程。

      下载地址:http://www.mapeditor.org/

      打开Tiled—>创建新图—>划分块大小与数量—>添加图块—>利用图块拼图—>导出JS文件

       

      打开导出的js文件,我们可以看到很代码和参数,我这里就只需要layouts里面的data数组。它是地图小块的标注,0表示没图,其他数字表示相对应的图。

      2、编写界面

        (1)首先创建一个外部包裹容器wrap,宽高100%等于可视区域大小。设置超出隐藏相对定位于body,根据情况设置背景;

        (2)接着创建地图容器map,设置绝对定位,宽高等于地图大小,如3200X640px;

        (3)然后创建地图小块,绝对定位固定大小,如32X32px;

        (4)最后编写其他样式,如移动块、按钮与加载等。

      HTML&CSS代码如下:

      1 <!DOCTYPE html>
      2 <html>
      3     <head>
      4         <meta charset="utf-8" />
      5         <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
      6         <title>迷宫游戏</title>
      7         <style type="text/css">
      8             body,h1,h2,h3,h4,p,dl,dd,ul,ol,form,input,textarea,th,td,select{margin: 0;padding: 0;}
      9             em{font-style: normal;}
     10             li{list-style: none;}
     11             a{text-decoration: none;}
     12             img{border: none;vertical-align: top;margin: 0;}
     13             table{border-collapse: collapse;}
     14             input,textarea{outline: none;}
     15             textarea{resize:none;overflow: auto;}
     16             body{font-size:12px;font-family: arial;}
     17             
     18             html,body,#wrap{
     19                 height: 100%;
     20                 width: 100%;
     21             }
     22             #loading{
     23                 position: absolute;
     24                 width: 32px;
     25                 height: 32px;
     26                 top: 50%;
     27                 left: 50%;
     28                 margin-left: -16px;
     29                 margin-top: -16px;
     30                 background: url(img/loading.gif) no-repeat;
     31                 z-index: 100;
     32             }
     33             #wrap{
     34                 overflow: hidden;
     35                 position: relative;
     36                 background: url(img/bg.jpg) no-repeat;    
     37                 z-index: 1;
     38             }
     39             #map{
     40                 position: absolute;
     41                 left: 0;
     42                 top: 0;
     43                 width: 3200px;
     44                 height: 640px;
     45             }
     46             .wall{
     47                 position: absolute;
     48                 width: 32px;
     49                 height: 32px;
     50                 background: url(img/stick.png) no-repeat;
     51             }
     52             #move{
     53                 width: 96px;
     54                 height: 96px;
     55                 position: absolute;
     56                 background: url(img/dog.png) no-repeat;
     57                 top: 96px;
     58                 left: 64px;
     59             }
     60             #timer{
     61                 height: 32px;
     62                 width: 30%;
     63                 position: absolute;
     64                 top: 0;
     65                 right: 0;
     66                 color: #bdb632;
     67                 font-size: 1em;
     68                 line-height: 32px;
     69             }
     70             .treasure{
     71                 width: 96px;
     72                 height: 96px;
     73                 background: url(img/treasure.png) no-repeat;
     74                 position: absolute;
     75             }
     76             #start{
     77                 display: none;
     78                 position: absolute;
     79                 width: 30%;
     80                 height: 32px;
     81                 color: #BDB632;
     82                 font-size: 1.5em;
     83                 text-align: center;
     84                 line-height: 32px;
     85                 left: 35%;
     86                 top: 50%;
     87                 margin-top: -17px;
     88                 border: #BDB632 1px solid;
     89                 z-index: 80;            
     90             }
     91             #end{
     92                 display: none;
     93                 position: absolute;
     94                 width: 40%;
     95                 height: 10%;
     96                 font-size: 2em;
     97                 left: 30%;
     98                 top: 40%;
     99                 text-align: center;
    100                 line-height: 2;
    101                 color: #BDB632;
    102                 font-weight: bold;
    103                 z-index: 50;
    104             }
    105             #cover{
    106                 /*display: none;*/
    107                 position: fixed;
    108                 left: 0;
    109                 width: 100%;
    110                 height: 100%;
    111                 background: #000;
    112                 -webkit-opacity: 0.6;
    113                 opacity: 0.6;
    114                 z-index: 10;
    115             }
    116         </style>
    117         <script src="js/touch.js" type="text/javascript" charset="utf-8"></script>
    118     </head>
    119     <body>
    120         <div id="wrap">
    121             <!--地图-->
    122             <div id="map">
    123                 <div id="move"></div>
    124             </div>
    125             <!--计时器-->
    126             <div id="timer">
    127                 <span>剩余时间:</span>
    128                 <span id="seconds">90</span>
    129                 <span>S</span>
    130             </div>
    131             <!--加载中-->
    132             <div id="loading"></div>
    133             <!--开始-->
    134             <div id="start">开始游戏</div>
    135             <!--结束-->
    136             <div id="end">游戏结束!</div>
    137             <!--覆盖层-->
    138             <div id="cover"></div>
    139         </div>
    140     </body>
    141 </html>
    展开代码

      3、功能实现

        Javascript代码模块:    

        (1)获取页面元素、设置地图和移动块的位置变量、地图与移动块的高;   

     1      var Map = document.getElementById("map");
     2         var wrap = document.getElementById("wrap");
     3         var seconds = document.getElementById("seconds");//秒计时
     4         var start = document.getElementById("start");//开始
     5         var loading = document.getElementById("loading");//加载
     6         var end = document.getElementById("end");//结束
     7         var cover = document.getElementById("cover");//覆盖层
     8         var move = document.getElementById("move");//滑块
     9         var t  = move.offsetTop;//滑块位置
    10         var l = move.offsetLeft;
    11         var mapL = Map.offsetLeft;//地图位置
    12         var mapT = Map.offsetTop;
    13         var mapH = Map.offsetHeight;//地图高

        (2)根据tiled生成的数组创建地图元素,如本例是迷宫的墙体;

     1 //Tiled生成的墙体数组
     2         var sticks =[0, 0, 0, 0, 0, 0, ………………1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0];//数组太长,中间省略
     3         var r = -1;//
     4         var c = 0;//
     5         //生成墙
     6         for (var i = 0; i < sticks.length; i++) {
     7             if(i%100==0){
     8                 r++;
     9             }
    10             c = i%100;
    11             //0为空,1为有
    12             if(sticks[i]==1){
    13                 var div = document.createElement("div");
    14                 div.className = "wall";
    15                 Map.appendChild(div);
    16                 div.style.left = c*32 + "px";//小块宽高为32
    17                 div.style.top = r*32 + "px";
    18             }        
    19         }

        (3)预加载图片,将本例中用到的图片进行预加载;

     1 //预加载图片
     2         var imgArr = ["bg.jpg","dog.png","treasure.png"];//图片数组
     3         (function loadImg (){
     4             var index = 0;
     5             for(var i=0;i<imgArr.length ;i++){
     6                 var img = new Image();
     7                 img.src = "img/" + imgArr[i];
     8                 img.onload = function (){
     9                     index++;
    10                     if(index == imgArr.length){
    11                         loading.style.display = "none";
    12                         start.style.display = "block";                
    13                     }
    14                 };
    15             }
    16         })();

        (4)绑定触屏事件,检测碰撞与移动,引入touch.js;

     1 //触屏事件
     2         touch.on('#Map', 'touchstart', function(ev){
     3                 ev.preventDefault();
     4         });    
     5         touch.on(Map,"touchstart",function(ev){
     6                 var halfW = wrap.clientWidth;//可视区宽高
     7                 var halfH = wrap.clientHeight;
     8                 var x = ev.touches[0].pageX;//1手指坐标
     9                 var y = ev.touches[0].pageY;
    10                 //
    11                 if( x > halfW/2 && (y > halfH/3) && y < 2*halfH/3 && sticks[100*t/32+(l/32+3)]==0 && sticks[100*t/32+(l/32+3)+100]==0 && sticks[100*t/32+(l/32+3)+200]==0){
    12                     l+=32;
    13                     move.style.left = l +"px";
    14                     //假如物块到达边沿,右移地图
    15                     if(l+mapL>=halfW-96){
    16                         Map.style.left = mapL - 192 + "px";
    17                         mapL-=192;
    18                     }
    19                 }
    20                 //
    21                 else if (x < halfW/2 && (y > halfH/3) && y < 2*halfH/3 && sticks[100*t/32+(l/32-1)]==0 && sticks[100*t/32+(l/32-1)+100]==0 && sticks[100*t/32+(l/32-1)+200]==0){
    22                     l-=32;
    23                     move.style.left = l +"px";
    24                     //物块到达屏幕左边沿,左移地图
    25                     if(l<=96-mapL){
    26                         if(mapL<=-192){
    27                             Map.style.left = mapL + 192 + "px";
    28                             mapL+=192;
    29                         }else{
    30                             Map.style.left = 0 + "px";
    31                             mapL=0;
    32                         }                    
    33                     }
    34                 }
    35                 //
    36                 else if (y < halfH/3 && sticks[(t/32-1)*100+l/32]==0 && sticks[(t/32-1)*100+l/32+1]==0 && sticks[(t/32-1)*100+l/32+2]==0){
    37                     t-=32;
    38                     move.style.top = t + "px";
    39                     //接近上边,上移大图
    40                     if( t<= -Map.offsetTop + 32){
    41                         Map.style.top = 0 + "px";
    42                     }                
    43                 }
    44                 //
    45                 else if (y > 2*halfH/3 && sticks[(t/32+3)*100+l/32]==0 && sticks[(t/32+3)*100+l/32+1]==0 && sticks[(t/32+3)*100+l/32+2]==0){
    46                     t+=32;
    47                     move.style.top = t + "px";
    48                     //到达下方,移动大图
    49                     if(t>=halfH-96){
    50                         Map.style.top = halfH - mapH + "px";
    51                     }
    52                 }                
    53         });    

        (5)给其他按钮绑定事件和设定定时器等。

     1 // 开始
     2         start.onclick = function (){
     3             cover.style.display = "none";
     4             this.style.display = "none";
     5             timerFn();
     6         }
     7 //计时器
     8         var timer = null;
     9         var i = 90;// 时长
    10         function timerFn(){
    11             timer = setInterval(function(){
    12                 seconds.innerHTML = i;
    13                 i--;
    14                 if(i<0){                
    15                     cover.style.display = "block";
    16                     end.style.display = "block";
    17                     clearInterval(timer);
    18                 }
    19             },1000);
    20         }

        代码块4碰撞与移动判断条件如( x > halfW/2 && (y > halfH/3) && y < 2*halfH/3 && sticks[100*t/32+(l/32+3)]==0 && sticks[100*t/32+(l/32+3)+100]==0 && sticks[100*t/32+(l/32+3)+200]==0)

        解析:由于我把屏幕触摸区域分成了以下四个区域,上下左右。

        x>halfW/2 && y>halfH/3 即表示在屏幕水平小于1/2处,垂直1/3到2/3处,其余类同;

        sticks[100*t/32+(l/32+3)]==0表示地图被划分为许多行,每行100个单位,根据sticks数组判断移动块的右侧是否为空。如果移动块的left除以单位长度32,再加上本身宽度为3个单位,即得移动块右侧的数组对应的值。如果为0说明为空1说明有div块(即有墙)。由于移动块相当于3个长度单位,所以要检测右侧上中下三块是否为空(行数加1和2)。

     

      结束语:当地图比较大,需要创建数以千计的div时,为了游戏更加流畅,应采用大图做地图,然后同样用数组记录位置。

      游戏地址:www.chengguanhui.com/demos/maze

      请使用手机或谷歌浏览器Device Mode下访问。

  • 相关阅读:
    WebSQL的基本使用过程
    Windows下Apache2.2+PHP5安装步骤
    DNS解析过程
    MongoDBTemplate多条件查询的问题
    解决Java工程URL路径中含有中文的情况
    Maven配置默认使用的JDK版本
    Mockito when(...).thenReturn(...)和doReturn(...).when(...)的区别
    如何正确对tomcat host进行配置
    对Java动态代理的理解
    对Mybatis的理解
  • 原文地址:https://www.cnblogs.com/chengguanhui/p/4683437.html
Copyright © 2011-2022 走看看