html5 MMORPG Game研究 三 地图

作者:翅膀的初衷 来源:本站原创 发布时间:2013-10-12 查看数:61608

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