地图拖动效果实现.doc_第1页
地图拖动效果实现.doc_第2页
地图拖动效果实现.doc_第3页
地图拖动效果实现.doc_第4页
地图拖动效果实现.doc_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

一个实现思路1.第一次显示,以某坐标为中心(例如玩家城池坐标),读取周围特定菱形范围内的坐标。读取要求:至少要超出屏幕范围2.鼠标按下,记录拖动开始位置3.鼠标移动,拖动整个地图4.鼠标抬起,根据当前位置和拖动开始位置,计算拖动经过了多少个单元。并查找当前显示的地图块中,符合这个条件的地图块,记录世界坐标和其XY坐标5.以新的世界坐标为中心,重新获取地图数据,并把中心地图块显示到刚才的XY位置。给玩家在视觉上是平滑拖动的效果onMouseDown map.startDrag();onMouseup map.stopDrag();!-var p = new Array();var speed = 0.05; /速度var picWidth = 1280; / 大图的宽高var picHeight = 971;var x,y / 鼠标点下去时背景的坐标var x_new,y_new /位移var haveclick = false;function getmouseposition(event)if(document.all) x = document.body.scrollLeft+event.clientX; y = document.body.scrollTop+event.clientY;else x = event.layerX; y = event.layerY; haveclick = true;function movestop()haveclick = false;function movestart(event)if(haveclick)if(document.getElementById(pic).style.backgroundPosition.length=0) document.getElementById(pic).style.backgroundPosition=0px 0px;p = document.getElementById(pic).style.backgroundPosition.split( )if(document.all) x_new = document.body.scrollLeft+event.clientX; y_new = document.body.scrollTop+event.clientY;else x_new = event.layerX; y_new = event.layerY; x2 = (speed*(x_new-x)+parseInt(p0).toString(10); / 计算位移量y2 = (speed*(y_new-y)+parseInt(p1).toString(10);if (x20) y2=0;if (x20) x2=0;if (y2天地培训案例17Flash地图应用i=s 本帖最后由 S_eVent 于 2011-11-17 00:40 编辑 /i列位仙家许久不见,现在咱啥也别说了,眼泪唰啦唰啦地不,哥没哭,哥这是高兴(旁白:“高兴你个头兴你个头你个头个头头”)我*,你*掺和什么掺和,哥跟仙家们见面关你*事,你再*我就*,我*,怎么说出来的话都是星号?!还伴随着一声清脆的“滴”声?!(旁白:“神经病”)切说我发神经就是你没幽默感的表现了 算了,不扯了,伤和气,这和气一伤,啪容易扯着蛋!相信列位仙家都看过我的url=/thread-66174-1-1.html案例5视角移动/url吧?那时讲了一些关于视角移动的原理,当时提到视角的移动时列位不知会不会联想到一些大型游戏的地图或者是一些常用的地图应用呢?不管你们想没想到,反正寡人是想到了,既然想到了,就当是拓展训练,为列位揭秘flash地图应用的制作方法吧。 flash地图应用大致可分两分静态地图和动态地图两类,所谓静态地图就是地图始终是一张固定的图片,此图片会很大,长宽都可能是舞台的数倍,而动态地图就是一次只初始化用户可视范围内的地图数据,当拖动地图时会向服务器发起请求拿到新的一块地图区域的信息然后再渲染出来,像百度,谷歌,E都市的地图均是如此。 我们今天先讲静态地图。看看我静态地图的最终结果先吧:url=/upload/ASMap.swf/upload/ASMap.swf/url) 它的制作要点主要分两点:一,拖拽移动视角;二,地图缩放。第一点对于列位来说应该没有什么问题,若不明白再去翻翻案例五吧老湿至于第二点呢,简单地说就是图片坐标以及scaleX,scaleY的改变,难点就在于保持以当前视野中心点为中心等比缩放上面。我们知道,当你对一个Sprite或是其他显示对象通过改变scaleX,scaleY进行缩放的时候,显示对象会以注册点(坐标零点)为中心进行缩放,那么当你把一张图片addChild到一个Sprite中后,改变此Sprite的scaleX,scaleY进行缩放时会发现图片会向右下角延伸,要想让图片等比缩放必须把图片中心点放在Sprite原点才行。不信你试试?flash/upload/ASMapScaleTest.swf/flash测试过之后你一定能体会到,只有注册点在中心时才可能做到在改变scale时等比缩放。不过我们的地图在按下鼠标后会被拖动,不停地在改变其坐标位置,所以你一开始把注册点设置在地图图片中心位置是不够的,注册点还得跟着鼠标的拖动而不停地改变。有人就问了,啊?注册点也能动态改变吗?额其实这个“注册点”得加引号啦,其实质还是x,y的变化,让我们看看它的原理所在。首先,我们一个地图对象(可以为bitmap也可以把bitmap放至一个Sprite中)的默认原点位置 (0,0) 依然在左上角,若不更改地图addChild时的位置为负的一半地图长、宽,则对地图进行缩放时地图将会向右下方扩散。我们看下图attach59273/attach假设黑色区域为地图的父容器(可以看作是舞台,这也是我们用户的可视区域),白色十字为原注册点,在父容器中心,当淡蓝色的原地图放大时,我们能够想象注册点将会向右下方跑。此时用户会明显地看到刚才父容器中心点位置往右移动了,这自然不能达到我们想要的目的,我们是希望用户视野中心点位置保持不变然后等比缩放它的四周区域。那怎么办呢?自然是把随着scale的放大而往右下方偏移的注册点移动回来。那么到底需要移动多少呢?从图上我们其实可以看出,注册点在地图放大过程中右移的位置为它缩放前后与父容器(黑色区域)左边缘的距离之差,同理,我们也可以得到下移了的位置:offsetX = X0 - X0 * scaleX;offsetY = Y0 - Y0 * scaleY;上面的公式中的X0,Y0是注册点相对于父容器的横纵坐标,而不是相对于地图本身,因为地图本身会进行缩放,在不停地变动,那么就不能以它自身作为参照物,就像你想测一测你跑步有多快,你肯定是以一样静止的物体,比如一棵大树为参照物,不可能以一个运动的物体,如一个和你并行奔跑的人为参照物,若你以运动的物体作为参照物,那你只会感觉你跑的很慢甚至没有移动过,但实际上你已经跑得跟狗一样快了。要计算地图相对于父容器的横纵坐标,使用localToGlobal以及globalToLocal方法是再简单不过的了:var A : Point=mc.parent.globalToLocal(mc.localToGlobal(regpoint)/先把注册点转换为全局坐标再转换为它父容器的坐标mc.scaleX = mc.scaleY = value;/执行放大后,再重新计算全局坐标var B : Point=mc.parent.globalToLocal(mc.localToGlobal(regpoint)var offsetX:Number = A.x - B.x;var offsetY:Number = A.y - B.y;如此便可求得我们注册点在放大/缩小后移动了的距离。为了让缩放后改变了的注册点回到父容器中心,我们把地图的横纵坐标进行如下调整:mc.x+=offsetX;mc.y+=offsetY验证一下有没有错误。若地图放大,则offsetX, offsetY将是负值,若缩小,则为正值,地图放大后注册点往右下方偏离(见上图所示),所以为了让注册点回到父容器中心,需把地图往左上移动,那么让地图的横纵坐标加上一个负值就等于让地图的横纵坐标进行了减小,确实能够使地图往左上移动。OK,证明这样做没错后就可以把这个原理用到我们的实际开发中去了。需要注意的是,我们的这个所谓的“注册点”不过只是一个参照点而已,它的用途就是在缩放前后计算用户所关注的点偏移了的位置,然后用这个位置来让地图x,y进行移动以保证用户关注点保持在原来的“位置”。不过如果我们要对地图进行拖动的话就必须改变地图的x,y坐标,当地图坐标改变时我们的“注册点”位置也必须跟着移动以保证它在地图中所处的位置不变才行。不然当你拖动了地图,注册点还留在原地的话它在地图中所处位置就会变掉了,缩放后计算出来的偏移位置就会出现错误。看看源码:假设我们在水平以及竖直方向拖动地图的距离分别为distX和distY,那么拖动后地图坐标为map.x += distX;map.y += distY;假设我们起初设置的地图位置为map.x = 0;map.y = 0;注册点位置为舞台中心点regPoint = new Point(stage.stageWidth/2, stage.stageHeight/2);那么移动了地图后它的位置依然要和map的左上角保持距离:regpoint.x = -map.x + stage.stageWidth/2;regpoint.y = -map.y + stage.stageHeight/2;若地图进行过缩放,那么就需要把地图坐标换算为1倍大小时候的坐标再进行计算:regpoint.x = -map.x / map.scaleX + stage.stageWidth/2;regpoint.y = -map.y / map.scaleY + stage.stageHeight/2;切记,每当地图坐标改变时,注册点坐标也得跟着改变。 阅读完了理论知识的一些要点之后看看全部源码,其中还不乏一些需要注意的地方:codeSWF(width=600, height=500)public class ASMap2 extends Sprite Embed(source=assets/wowmap.jpg) private var MapResource:Class; private var map:Sprite = new Sprite();/地图 private var oldX:Number;/拖动前X private var oldY:Number;/拖动后Y private var currentMapZoom:Number = 1;/当前地图缩放比例 private var maxZoomLevel:int = 5;/最大可放大次数 private var minZoomLevel:int = -3;/最大可缩小次数 private var currentZoomLevel:int = 1;/当前缩放次数 private var zoomValue:Number = 0.1;/每次放大或缩小的比例 private var bmp:Bitmap; private var regpoint:Point/地图注册点 public function ASMap2() initView(); initBtn(); private function initView():void bmp = new MapResource(); map.addChild( bmp ); addChild( map ); map.x = 0;/地图真实注册点在0,0位置 map.y = 0; regpoint = new Point( stage.stageWidth/2, stage.stageHeight/2 );/虚拟注册点位置为舞台中心 map.doubleClickEnabled = true;/设置地图容器能接受双击事件 map.addEventListener( MouseEvent.MOUSE_DOWN, onMouseDown ); map.addEventListener( MouseEvent.MOUSE_UP, onMouseUp ); map.addEventListener( MouseEvent.DOUBLE_CLICK, onDoubleClick ); map.cacheAsBitmap = true;/缓存为bitmap,提高拖动效率 private function initBtn():void var zoomIn:MyButton = new MyButton(放大, 50, 25); var zoomOut:MyButton = new MyButton(缩小, 50, 25); zoomIn.x = 240; zoomOut.x = 300; zoomIn.y = zoomOut.y = 15; addChild( zoomIn ); addChild( zoomOut ); zoomIn.addEventListener(MouseEvent.CLICK, zoomInHandler); zoomOut.addEventListener(MouseEvent.CLICK, zoomOutHandler); /* * 设置镜头 * */ private function setCamera( x:Number, y:Number ):void var desX:Number =map.x + stage.stageWidth / 2 - x; var desY:Number =map.y + stage.stageHeight / 2 - y; /使用TweenLite缓动改变地图x,y值时,x,y是缓缓改变而不是立马改变的(切记),所以需要在缓动结束后再改变注册点 TweenLite.to( map, 0.6, x:Math.min( Math.max(desX, stage.stageWidth - map.width), 0 ), y:Math.min( Math.max(desY, stage.stageHeight - map.height), 0 ), onComplete:offsetRegpoint );/ offsetRegpoint();/注意不能在这里改变注册点,因为此时map的x,y还未变到预期值 private function onMouseDown( event:MouseEvent ):void addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); stage.addEventListener(Event.MOUSE_LEAVE, onMouseUp);/防止按住鼠标拖动过程中鼠标滑出flash player窗口后再回来造成的鼠标跟随现象 oldX = stage.mouseX; oldY = stage.mouseY; private function onMouseMove( event:MouseEvent ):void /地图拖动(视角移动) var distX:Number = stage.mouseX - oldX; var distY:Number = stage.mouseY - oldY; map.x += distX; map.y += distY; /限制地图移动范围,防止露出舞台背景 map.x = Math.min(0, map.x); map.x = Math.max(stage.stageWidth - map.width), map.x); map.y = Math.min(0, map.y); map.y = Math.max(stage.stageHeight - map.height), map.y); /地图位置一变,注册点必须跟着变 offsetRegpoint(); oldX = stage.mouseX; oldY = stage.mouseY; private function onMouseUp( event:Event ):void removeEventListener( MouseEvent.MOUSE_MOVE, onMouseMove ); private function onDoubleClick( event:MouseEvent ):void setCamera( event.stageX, event.stageY ); private function zoomInHandler(event:MouseEvent):void if( currentZoomLevel + 1 = minZoomLevel ) zoom( 1 ); /* * 根据地图位置移动注册点 * */ private function offsetRegpoint():void regpoint.x = -map.x / map.scaleX + stage.stageWidth/2; regpoint.y = -map.y / map.scaleY + stage.stageHeight/2; /* * 缩放 * param action 操作方式:0为放大,1为缩小 * */ private function zoom( action:int ):void if( action = 0 ) currentZoomLevel+; currentMapZoom += zoomValue;/缩放比例加zoomValue else if( action = 1 ) currentZoomLevel-; currentMapZoom -= zoomValue;/缩放比例减zoomValue var A:Point = this.globalToLocal( map.localToGlobal(regpoint) ); var temp:Number = map.scaleX;/临时记录scale值,由于scaleX和scaleY值一样,所以我就随便取一个赋值 map.scaleX = map.scaleY = currentMapZoom;/暂时缩放以计算缩放后的注册点位置以及地图宽高 /执行放大,再重新计算全局坐标,重新记录宽高 var B:Point=this.globalToLocal( map.localToGlobal(regpoint) ); var newW:Number = map.width; var newH:Number = map.height; /把注册点从B点移到A点 /计算缩放前后舞台中心点所对应地图位置所移动的距离 var offsetX:Number = A.x-B.x; var offsetY:Number = A.y-B.y; /将缩放前视野中心点对应地区位置移回舞台中心,让人感觉到地图是等比缩放了的 /当然,你还得限制住map不要移动太多以致露出舞台背景,最小值需用放大后的长宽进行计算 var newX:Number = Math.min( Math.max(map.x + offsetX, stage.stageWidth - newW), 0 ); var newY:Number = Math.min( Math.max(map.y + offsetY, stage.stageHeight - newH), 0 ); map.scaleX = map.scaleY = temp;/恢复到原来缩放大小 /播放舞台缩放动画,这才是真正的缩放开始 TweenLite.to( map, 0.6, scaleX:currentMapZoom, scaleY:currentMapZoom, x:newX, y:newY ); /code其中,关于地图缩放时为了保证在地图缩放过程中视野固定,确实花了我不少功夫去研究算法,详细的一些思想在我的博文中也都图文解释过了,虽然如此,我想大多数应该还是无法一时半会能够领悟进去的,若是你领悟了,就差不多可以去解答一些论坛里有关地图中心点缩放算法的提问帖子了,没准还能弄个AQ小生的徽章来戴戴。上面这段代码虽然就100来行,但是我估计很多人会停留在这个页面半小时甚至更长的时间,边看代码边画图能够帮助你更好地思考算法,就算有些公式你看不懂原理,但是你如果知道怎么使用也是不错的,可以直接到处copy我的代码来使用,也能够正常地为你所用。 除了地图缩放算法外,以上代码中还需要注意以下几点:1,cacheAsBitmap属性的使用:将此属性设置为true后能让一个显示对象缓存为位图,它就不需要flash player在每一帧都进行渲染,可以有效地增加拖拽地图流畅度,在显示元素很多的视图中灵活运用之能够大大提高运行效率,不过若你对一个每一帧都要改变外形的动画对象使用此属性没有任何意义,因为它每一帧都需要flash player重绘之,不可能停止渲染,因此此属性只对静态显示对象有明显效果。更多知识请看url=/viewthread.php?tid=44285&highlight=cacheAsBitmap这里/url2,切换镜头:双击地图时能够切换镜头,镜头切换原理见下图:attach39600/attach我们双击了白色十字点位置欲把此位置移至舞台中心点红色十字位置处,那么地图图片就会向左上移动至深蓝色框框标志的位置,不难计算出红色与白色十字点间的横坐标位置应该是x - stage.stageWidth/2,y方向位置同理可得。 S_eVent 发表于 2011-1-17 10:24i=s 本帖最后由 S_eVent 于 2011-11-17 00:46 编辑 /i3.使用TweenLite的尴尬之处: 想让地图缩放有个动态效果就想到使用TweenLite(不知道怎么使用TweenLite?看上一篇教程吧),但是用TweenLite.to方法使指定的参数是属性变化的目标值,但是这个目标值还需要事先把scale放大后才能计算得知,因此我不得不先把scale放大之后计算得到我们需要的地图位置变化目标值之后再把scale恢复为原来的值,然后再次使用TweenLite.to方法来进行放大,此方法有点烂,但是我暂时想不出什么好的方法,若有兄弟有好的建议可以提出来哈。4.代码中用到的类MyButton.as在上一案例中有提到,以后我将会多次使用它来作为负责交互的按钮。color=RoyalBluesize=4动态地图/size/color 动态地图和静态地图有着很大的不同,因为它所需要显示的不再是一个概览图,而是需要呈现给用户看到地图上每个元素的信息,比如一张城市地图,用户不再满足于看看这个城市的建筑物,地形分布情况,而是期望知道每个建筑物的详细信息。此时将会由动态地图来支持我们实现此需求,先看看总体思路与效果演示吧(url=/upload/ASMapDynamic.swf跪求一点/url) 光看思路会感觉十分抽象,因此我们将结合代码来看看。 首先自然是为每一个建筑物创建视图(View)和数据对象(VO),为了增强可拓展性,任何时候都应该记得把视图与数据分离。TileVO.as:code public class TileVO public var type:int; public var row:int; public var col:int; public function TileVO( type:int=0, row:int=0, col:int=0 ) this.type = type; this.row = row; this.col = col; /codeTileView.as:codepublic class TileView extends Sprite public var Position:Point;/记录地块行列位置 private var _tileVO:TileVO; private var view:Bitmap = new Bitmap(); public function TileView() addChild( view ); public function get tileVO():TileVO return _tileVO; public function set tileVO(value:TileVO):void /若原先没有tileVO或者两次类型不同则换图片外形 var isDifferent:Boolean = _tileVO != null & _tileVO.type != value.type; if( _tileVO = null | isDifferent ) _tileVO = value; switch( _tileVO.type ) case 0: view.bitmapData = AssetsManager.getInstance().getResourceByName( building1 ); break; case 1: default: break; /code接下来我们需要思考一下地图上所有元素的存放问题了,在上面给出的博文中讲到,全部地图元素数据一般会存在一个二维数组中(在数据库中就会是一张表格,表格中记录元素行列号),每个元素在地图上所处的行列号就是它在二维数组中的索引位置。一看到这种格子型的数据分布就想到了在url=/thread-70656-1-1.html案例15/url中提到的Grid类,我们再复习一下此类的结构:Grid.as:code/* * 格子类 * author S_eVent * */ public class Grid public var cellsInfo:Array; private var _rowNum:int = 0; private var _colNum:int = 0; private var _maxCells:int; public function Grid( rowNum:int, colNum:int ) cellsInfo = new Array( rowNum ); for( var i:int=0; i 0 ) var temp:Object = cellsInforowcol; cellsInforowcol = value; cellsInfopos0pos1 = temp; else cellsInforowcol = value; /* * 查询某个物品对象所在格子 * param view 欲查询对象 * return 位置数组,格式如row, col,若查询不到,则返回空数组 * */ public function getCell( value:Object ):Array for(var i:int=0; icellsInfo.length; i+) for(var j:int=0; jcellsInfoi.length; j+) if( cellsInfoij = value ) return i,j; return ; /* * 将某格子数组中某位置处元素删去 * */ public function removeItemAt( row:int, col:int ):void cellsInforowc

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论