极念网欢迎您!
html5 MMORPG Game研究 三 地图
作者:翅膀的初衷来源:本站原创发布时间:2013/10/12 11:09:30查看数:61052

关于地图的思考,怎么来做游戏中的地图?
  在现有的游戏中,地图的制作即有tile类型的,比如传奇,也有整张大图的,比如大话!开始我本人比较倾向于tile地图的,资源可以很好的重复利用,不过一番权衡之后,我还是选择了用一整张图来做,加上障碍点和遮盖物,非常完美!处理起来简单方便!不过一整张图最大的问题是怎么解决它的加载问题,一张2000*2000像素的地图,起码得有个2-3M,我在这里采用的先加载一张小的缩略图,然后等大图加载完毕再进行替换!

  实现起来非常简单

var map = new j2d.Map(); map.DrawImage=小图;//先用小图显示 var img = new Image(); img.onload=function(){ map.DrawImage=img;//大图加载完成进行替换 } img.src="大图";


  下面我们只要重点处理地图的障碍物与遮盖物即可!不过在此之前我还要解决一个小问题

  在之前的开发中,当我们控制的角色行走到屏幕的边缘后,就再也无法行走了,如果是一幅较大的图片,我们的角色永远无法行走到屏幕之外的地图上,为了解决这个问题,我们将使用地图跟随效果!

  即如果地图大于当前屏幕,且角色处理屏幕中心的时候,当角色移动一步,地图则向相反的方向移一步,直到角色移到地图的边缘为止!在这个地程中,角色应当一直处理屏幕正中央!

  为了实现这个效果,我们为地图对象:Map 添加一个方法:

this.RunTo=function(point){ var x = this.DrawPoint.X + point.X;//Map移动后的起始绘制X坐标 if(x>=0&&(x+j2d.Context.canvas.width)<=this.Width){//如果X大于0,并且小于MAP宽度,则地图X坐标移动到X this.DrawPoint.X=x; } var y = this.DrawPoint.Y + point.Y;//Y坐标同量 if(y>=0&&(y+j2d.Context.canvas.height)<=this.Height){ this.DrawPoint.Y=y; } };

  然后在角色移动时,每次移动都调用Map的RunTo方法即可!

  下面我们来处理地图的障碍物,何谓障碍物?即角色无法到达的地方,都算是障碍物!我对有种方法情有独钟!即在一张图上面涂上黑色,只要是黑色的区域都是无法到达的地方!不过这样每幅地图每次都要多加载一张图片,每次判断坐标是否可以到达时都要去取下色,无疑会降低程序的处理速度!所以我决定先做一个简单的处理程序,先将图片的障碍生成,放到地图对应的XML中!

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title>无标题文档</title> <script type="text/javascript"> function GetPath(){ var src = document.getElementById("txtSrc").value; var context = document.getElementById("scene").getContext("2d"); var image = new Image(); image.onload=function(){ document.getElementById("scene").width=image.width; document.getElementById("scene").height=image.height; context.drawImage(image,0,0,image.width,image.height,0,0,image.width,image.height); var rgb=context.getImageData(0,0,image.width,image.height).data; var n=rgb.length; var array = new Array(); for(var i=0;i<n;i+=4){ array.push(new Array(rgb[i+0],rgb[i+1],rgb[i+2])); }; var html=""; for(var i=0;i<image.height;i++){ html+="["; for(var j=0;j<image.width;j++){ var temp = array[image.width * i+j]; if(Math.sqrt(temp[0]-0)<10&&Math.sqrt(temp[1]-0)<10&&Math.sqrt(temp[2]-0)<10){//判断是否黑色 允许10的误差 html+="1"; }else if (Math.sqrt(temp[0]-255)<10&&Math.sqrt(temp[1]-0)<10&&Math.sqrt(temp[2]-0)<10){//判断是否红色 允许10的误差 html+="9"; } else{ html+="0"; } if(j<image.width-1){ html+=','; } }; html+="]"; if(i<image.height-1){ html+=","; } } Debug("<br /><br /><br /><br />"); Debug(html); }; image.src=src; } function Debug(msg){ document.getElementById("Debug").innerHTML+=msg+'<br />'; } </script> </head> <body> <div> <input type="text" id="txtSrc" value="test.jpg" /><input type="button" value="GET" onclick="GetPath()" /> </div> <canvas id="scene" width="0" height="0"></canvas> <div id="Debug"> </div> </body> </html>

注意的是:上面的代码不能直接打开使用,必须放到IIS或者其它网页服务器中,且图片必须与网页在同一个域名下面,不然会产生跨域问题!


我这里使用的图片是:

 

按比例缩小10倍的障碍图:

 

  然后我们将这些信息保存到XML中,方便JS读取!(注:这里缩略图与障碍图的比例请保持为1:1)

<?xml version="1.0" encoding="utf-8"?> <Map id="1" Name="地图名" Src="地图URL" Thumbnail="地图缩略图" Width="地图宽" Height="地图高" Proportion="比例" Barrier="..."> </Map>

下面我们再来写一个方法来读取XML,其实就是一个简单的Ajax方法

function LoadXml(src,async,callback){ var xmlDoc; var XmlHttpRequest; try{ XmlHttpRequest = new XMLHttpRequest(); }catch(e){ try{ XmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP"); }catch(e){ try{ XmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP"); }catch(e){ XmlHttpRequest=null; } } } if(XmlHttpRequest){ if(async){ XmlHttpRequest.open("GET", src, true); XmlHttpRequest.onreadystatechange = function(){ if (XmlHttpRequest.readyState == 4){ if (XmlHttpRequest.status == 200){ if(callback){ callback(XmlHttpRequest.responseXML); } }else{ alert('http error code:'+XmlHttpRequest.status); } } }; XmlHttpRequest.send(null); }else{ XmlHttpRequest.open("GET", src, false); XmlHttpRequest.send(null); return XmlHttpRequest.responseXML; } } }

好,下面我们就可以来看看我们的地图效果了:

LoadMap:function(id,callBack){ LoadXml("Data/Map/"+id+".xml",true,function(xml){ var img = new Image(); var node = xml.getElementsByTagName("Map")[0]; img.onload=function(){ var mapObject= new j2d.Map(); mapObject.DrawObject=img; mapObject.ID=node.getAttribute("ID");//XML操作 mapObject.Name=node.getAttribute("Name"); mapObject.Width=parseInt(node.getAttribute("Width")); mapObject.Height=parseInt(node.getAttribute("Height")); mapObject.BarrierProportion=parseInt(node.getAttribute("Proportion")); mapObject.Size=10; mapObject.Barrier=eval(node.getAttribute("Barrier")); mapObject.Zoom={"X":mapObject.Width/img.width,"Y":mapObject.Height/img.height}; var Pic = new Image(); Pic.onload=function(){ mapObject.IsComplete=true; mapObject.DrawObject=Pic; mapObject.Zoom={"X":1,"Y":1}; }; Pic.src = "Data/Map/"+node.getAttribute("Src"); if(callBack){ callBack(mapObject); } } img.src="Data/Map/"+node.getAttribute("Thumbnail"); }); }

  那我们怎么判断角色是否经过障碍呢?

var notPass=false;//是否是障碍 var tempPoint = j2d.Game.Map.Barrier[parseInt(y/j2d.Game.Map.BarrierProportion)]; if(tempPoint){ tempPoint=tempPoint[parseInt(x/j2d.Game.Map.BarrierProportion)]; if(tempPoint==0){ notPass=true; me.Transparent=1;//不是 }else if (tempPoint==9){ notPass=true;//不是 me.Transparent=0.4; }else{ notPass=false;// //me.Transparent=1; } }

  非常简单了!下面将介绍怎么处理遮盖物!假如有一棵事,当精灵从树叶下面经过时,如果是之前,你会发现,我们的精灵正在“轻功树上飞”! 当然,我们可以把这棵树抠图抠了来,让它遮盖在精灵上面,就不会出现这种情况了!不过我这里将采用一种更加简洁的方法!在之前的步骤中,我已经将遮盖物对应的区域已经规划出来了,只要精灵进入这片区域,让精灵半透明,这样即实精灵还是在“树上飞”,但是看起来好像是在树叶下面行走一样!同时,这也可以说是2.5d的效果了!一举多得,不过也有缺点,就是哪怕精灵只是一个小脚进入遮盖区域也会变透明。不过为了前面的诸多优点,这一点点牺牲绝对是值的!
另外偷偷说句:现在的页游都是这么处理!哪怕是互联网大佬腾讯开发的九仙也是如此!

  如果要将精灵透明,我们调整下Sprite方法,如下:

Sprite:function(){ j2d.BaseEntity.call(this); this.DrawObject = document.createElement("canvas"); this.DrawImage = null;//图像 this.Transparent = 1;//透明度 this.Target=null;//目标 this.Speed=100; var action="",//当前动作 list,//动作列表 index=0,//当前动作索引 timeStep=0, context, drawPoint = j2d.Point(0,0); this.AddAnimation=function(key,value){ if(!list){ list=new Object(); } list[key]=value; return true; }; this.SetAnimation=function(key){ if(action!=key){ index=0; action=key; timeStep=3600000; } }; this.Update=function(gameTime){ if(action&&action!=""&&list[action]){ timeStep+=gameTime; if(timeStep>=list[action].Interval){ timeStep=0; if(this.IsComplete==true){ drawPoint=j2d.Point(list[action].Frames[index]*this.Width,this.Direction*this.Height); if(list[action].Callback){ list[action].Callback(index,action); } if(index>=list[action].Frames.length-1){ index=0; }else{ index++; } } //-------------------------- draw -------------------------------------- if(!context){ this.DrawObject.width=this.Width; this.DrawObject.height=this.Height; context=this.DrawObject.getContext("2d"); } if(this.DrawImage){//Debug('dddddddddddddddddd'+this.DrawObject.width); context.clearRect(0, 0, context.canvas.width, context.canvas.height);//alert(gameTime); var w = context.canvas.width > this.DrawImage.width ? this.DrawImage.width :context.canvas.width ; var h = context.canvas.height > this.DrawImage.height ? this.DrawImage.height :context.canvas.height; context.drawImage(this.DrawImage,//绘制 drawPoint.X,// drawPoint.Y,// w,//绘制的尺寸 h,//绘制的尺寸 0,//绘制的区域的左上角 0,//绘制的区域的左上角 w,//绘制区域的大小。 h //绘制区域的大小。 ); if(this.Transparent>=0 || this.Transparent<1){ var mii = context.getImageData(0,0,context.canvas.width,context.canvas.height); var pix = mii.data; for(var i=0,n=pix.length;i<n;i+=4) { pix[i+3] = pix[i+3]* this.Transparent; } context.putImageData(mii,0,0); } } //------------------------------------------------------------------ } } for(var n=0;n<this.Containet.length;n++){ if(this.Containet[n]){ if(this.Containet[n].IsLose&&this.Containet[n].IsLose==true){ this.Containet.splice(n,1); n--; }else{ if(this.Containet[n].Update){ this.Containet[n].Update(gameTime); } } } } }; }

好,再来看看实际效果

 

同文同时发布于cnblog与www.jiniannet.com!作者:翅膀的初衷 出处:http://www.jiniannet.com 转载请保留本信息

 

演示地址:http://www.jiniannet.com/html5

注:原域名iis0已弃用,启用新域名www.jiniannet.com