html5 MMORPG Game研究 三 地图
关于地图的思考,怎么来做游戏中的地图?
在现有的游戏中,地图的制作即有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