unity中实现百度本地瓦片地图生成地图与坐标转换

编程入门 行业动态 更新时间:2024-10-06 18:25:33

unity中实现百度本地瓦片<a href=https://www.elefans.com/category/jswz/34/1770718.html style=地图生成地图与坐标转换"/>

unity中实现百度本地瓦片地图生成地图与坐标转换

第一步, 获取百度瓦片地图,下载到本地

网上有很多相关的工具自行查找一个使用即可

实在懒得找的我把我用的分享出来:

然后查看本地的地图目录结构. 搞清楚目录接口之后, 之后加载图片的路径问题就搞定了

如下图 大概的目录结构 1-18级地图  每一级内的文件夹名称是行数  打开进去的图名称是列数

也就是说每一片的瓦片地图层级+行数+列数 是唯一的 这就好办了 

第二步,写一个读取本地地图的类

坐标的问题后边再说

 

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;/// <summary>
/// 地图图片管理
/// </summary>
public class MapTextureManager : MonoBehaviour
{//避免重复加载  搞一个缓存private Dictionary<string, Sprite> Sprites = new Dictionary<string, Sprite>();public MapAxesTransfer mapAxesTransfer;private List<LoadTextureInfo> LoadInfos = new List<LoadTextureInfo>();/// <summary>/// 图片地址拼接/// </summary>/// <param name="zoom">层级</param>/// <param name="pieceX">行数</param>/// <param name="pieceY">列数</param>/// <returns></returns>string StitchingPath(int zoom,float pieceX,float pieceY){return @"file:///D:/TaiAnGround/maptile/" + zoom + "/" + (int)pieceX + "/" + (int)pieceY + ".png";}/// <summary>/// 预加载/// </summary>/// <param name="Pieces">加载中心的块坐标</param>public void Preload(Dictionary<int, List<Vector2>> Pieces){//先加载小层级Dictionary<int, Vector4> zoomToLRPiece = mapAxesTransfer.ZoomToLRPiece(10, 15);foreach (var key in zoomToLRPiece.Keys){Vector4 bound = zoomToLRPiece[key];Vector2 left = new Vector2(bound.x, bound.y);Vector2 right = new Vector2(bound.z, bound.w);for (int i = (int)left.x; i < right.x + 1; i++){for (int j = (int)left.y; j < right.y + 1; j++){string path = StitchingPath(key, i, j);LoadTextureInfo info = new LoadTextureInfo(path, null);LoadInfos.Add(info);}}}//加载附近层级以及大层级foreach (int zoom in Pieces.Keys){foreach (var piece in Pieces[zoom]){string path = StitchingPath(zoom, piece.x, piece.y);LoadTextureInfo info = new LoadTextureInfo(path,null);LoadInfos.Add(info);}}LoadSingle();}/// <summary>/// 队列内添加一个加载任务/// </summary>public void AddLoadTask(int zoom, float pieceX, float pieceY, Action<Sprite> setSprite){string path = StitchingPath(zoom, pieceX, pieceY);if (Sprites.ContainsKey(path)){setSprite(Sprites[path]);}else{LoadTextureInfo info = new LoadTextureInfo(path, setSprite);if (LoadInfos.Count == 0){if (!LoadInfos.Contains(info)){LoadInfos.Add(info);LoadSingle();}}else{LoadInfos.Add(info);}}}/// <summary>/// 开始加载/// </summary>void LoadSingle(){if (LoadInfos.Count > 0)StartCoroutine(LoadSpritebywww(LoadInfos[0]));elseResources.UnloadUnusedAssets();}private IEnumerator LoadSpritebywww(LoadTextureInfo info){using (WWW www = new WWW(info.path)){yield return www;if (www.isDone){if (string.IsNullOrEmpty(www.error)){var sprite = Sprite.Create(www.texture, new Rect(0, 0, 256, 256), new Vector2(0, 0), 100);if (info.setSprite != null)info.setSprite(sprite);Sprites[info.path] = sprite;}else{WD_Debug._Print("错误:  "+www.error);}if (LoadInfos.Count > 0) LoadInfos.RemoveAt(0);LoadSingle();}}}public class LoadTextureInfo{public string path;public Action<Sprite> setSprite;public LoadTextureInfo(string path,Action<Sprite> act){this.path = path;setSprite = act;}}
}

 

第三,坐标转换

这里要说到几个坐标系和他们之间的关系

经纬度坐标系

经线的定义:地球表面连接南北两极的大圆线上的半圆弧,任两根经线的长度相等
纬度的定义:地球表面某点随地球自转所形成的轨迹。任何一根纬线都是圆形而且两两平行。纬线的长度是赤道的周长乘以纬线的纬度的余弦,所以赤道最长,离赤道越远的纬线,周长越短,到了两极就缩为0

墨卡托坐标/平面坐标

百度提供了经纬度坐标和墨卡托坐标转换的方法我们拿来直接用,经纬度要先转换成墨卡托坐标 才能拿来计算转换坐标,原因是经纬度表示的是个球,直接以经纬度去计算距离是肯定有偏差的.

像素坐标

在18级地图下 像素坐标等于墨卡托坐标,17级就要除以2 16级除以4以此类推,基于此 就可以计算出经纬度对应的块坐标,

块坐标

就是下载的瓦片地图 图片的行列数坐标, 我写的方法是以中心图块为基准查找周围图块的 根据块坐标的原理 查找周围图块就很简单了

世界坐标

图块在unity 的世界坐标,以块坐标左下角的那个图块作为000点.又知道每一块图块的unity内的长度,基于此 可以计算出任意层级下任意图块的unity世界坐标

一个图块(使用的是spriterenderer组件) 的大小计算方式: 每单位像素量除以分辨率  每单位像素量就是下边截图中的属性,分辨率是已知的

米坐标

同理的,在由一个点 已知的经纬度坐标和米坐标以及偏移量 计算出的坐标 可以用来测距 两点之间实际距离

好的 搞清楚他们之间的关系一切都好办了

这是百度提供的坐标转换方法

里边有一堆乱七八糟的参数 咱也不是很懂.但能用就行了 

计算的时候大多数时候 float的精度是不够的 所以下边定义了一个double类型的vector2

using System;
using System.Collections.Generic;/// <summary>
/// 经纬度墨卡托转换
/// </summary>
public class MCObject
{public MCObject(){}//经纬度 偏移一定的像素量 取另一个经纬度public Vector2D Point_Offset(double lng, double lat, double offx, double offy, int zoom){//经纬度转墨卡托var re = convertLL2MC(lng, lat);//转换到zoom级别下re.x *= Math.Pow(2, zoom - 18);re.y *= Math.Pow(2, zoom - 18);//像素坐标re.x += offx/*/2*/;//256re.y += offy/*/2*/;//转回平面坐标//像素坐标.re.x /= Math.Pow(2, zoom - 18);re.y /= Math.Pow(2, zoom - 18);//转回经纬度convertMC2LL(re.x, re.y);re.x = Longitude;re.y = Latitude;return re;}static double DEF_PI = 3.14159265359; // PIstatic double DEF_2PI = 6.28318530712; // 2*PIstatic double DEF_PI180 = 0.01745329252; // PI/180.0static double DEF_R = 6370693.5; // radius of earth//适用于近距离 求两个经纬度之间的实际距离public static double GetShortDistance(double lon1, double lat1, double lon2, double lat2){double ew1, ns1, ew2, ns2;double dx, dy, dew;double distance;// 角度转换为弧度ew1 = lon1 * DEF_PI180;ns1 = lat1 * DEF_PI180;ew2 = lon2 * DEF_PI180;ns2 = lat2 * DEF_PI180;// 经度差dew = ew1 - ew2;// 若跨东经和西经180 度,进行调整if (dew > DEF_PI)dew = DEF_2PI - dew;else if (dew < -DEF_PI)dew = DEF_2PI + dew;dx = DEF_R * Math.Cos(ns1) * dew; // 东西方向长度(在纬度圈上的投影长度)dy = DEF_R * (ns1 - ns2); // 南北方向长度(在经度圈上的投影长度)// 勾股定理求斜边长distance = Math.Sqrt(dx * dx + dy * dy);return distance;}/// <summary>/// 经纬度坐标和墨卡托坐标互转 /// </summary>/// <param name="x"></param>/// <param name="y"></param>/// <param name="OutLongitudeAndLatitude">trueMC坐标转经纬度,false经纬度转MC坐标</param>public MCObject(double x, double y, bool OutLongitudeAndLatitude = false){if (OutLongitudeAndLatitude){_MCx = x;_MCy = y;convertMC2LL(_MCx, _MCy);}else{_Longitude = x;_Latitude = y;convertLL2MC(_Longitude, _Latitude);}}private double _MCx;private double _MCy;public double MCx{get{return _MCx;}set { _MCx = value; }}public double MCy{get{return _MCy;}set { _MCy = value; }}public double _Longitude;public double _Latitude;public double Longitude{get{if (_Longitude != null){return _Longitude;}else{return 0;}}set { _Longitude = value; }}public double Latitude{get{if (_Latitude != null){return _Latitude;}else{return 0;}}set { _Latitude = value; }}private List<List<double>> LL2MC = new List<List<double>>(){new List<double>(){-0.0015702102444, 111320.7020616939, 1704480524535203, -10338987376042340, 26112667856603880, -35149669176653700, 26595700718403920, -10725012454188240, 1800819912950474, 82.5},new List<double>() {0.0008277824516172526, 111320.7020463578, 647795574.6671607, -4082003173.641316, 10774905663.51142, -15171875531.51559, 12053065338.62167,  -5124939663.577472, 913311935.9512032, 67.5},new List<double>() {0.00337398766765, 111320.7020202162, 4481351.045890365, -23393751.19931662, 79682215.47186455, -115964993.2797253, 97236711.15602145, -43661946.33752821, 8477230.501135234, 52.5},new List<double>() {0.00220636496208, 111320.7020209128, 51751.86112841131, 3796837.749470245, 992013.7397791013, -1221952.21711287, 1340652.697009075, -620943.6990984312, 144416.9293806241, 37.5},new List<double>() {-0.0003441963504368392, 111320.7020576856, 278.2353980772752, 2485758.690035394, 6070.750963243378, 54821.18345352118, 9540.606633304236, -2710.55326746645, 1405.483844121726, 22.5},new List<double>() {-0.0003218135878613132, 111320.7020701615, 0.00369383431289, 823725.6402795718, 0.46104986909093, 2351.343141331292, 1.58060784298199, 8.77738589078284, 0.37238884252424, 7.45}};private List<List<double>> MC2LL = new List<List<double>>() { new List<double>() { 1.410526172116255e-8, 0.00000898305509648872, -1.9939833816331, 200.9824383106796, -187.2403703815547, 91.6087516669843, -23.38765649603339, 2.57121317296198, -0.03801003308653, 17337981.2 }, new List<double>() { -7.435856389565537e-9, 0.000008983055097726239, -0.78625201886289, 96.32687599759846, -1.85204757529826, -59.36935905485877, 47.40033549296737, -16.50741931063887, 2.28786674699375, 10260144.86 }, new List<double>() { -3.030883460898826e-8, 0.00000898305509983578, 0.30071316287616, 59.74293618442277, 7.357984074871, -25.38371002664745, 13.45380521110908, -3.29883767235584, 0.32710905363475, 6856817.37 }, new List<double>() { -1.981981304930552e-8, 0.000008983055099779535, 0.03278182852591, 40.31678527705744, 0.65659298677277, -4.44255534477492, 0.85341911805263, 0.12923347998204, -0.04625736007561, 4482777.06 }, new List<double>() { 3.09191371068437e-9, 0.000008983055096812155, 0.00006995724062, 23.10934304144901, -0.00023663490511, -0.6321817810242, -0.00663494467273, 0.03430082397953, -0.00466043876332, 2555164.4 }, new List<double>() { 2.890871144776878e-9, 0.000008983055095805407, -3.068298e-8, 7.47137025468032, -0.00000353937994, -0.02145144861037, -0.00001234426596, 0.00010322952773, -0.00000323890364, 826088.5 } };private Double[] MCBAND = { 12890594.86, 8362377.87, 5591021d, 3481989.83, 1678043.12, 0d };private int[] LLBAND = { 75, 60, 45, 30, 15, 0 };private double getRange(double cf, double ce, double t){if (ce != 0){cf = cf > ce ? cf : ce;}if (t != 0){cf = cf > t ? t : cf;}return cf;}private double getLoop(double cf, double ce, double t){while (cf > t){cf -= t - ce;}while (cf < ce){cf += t - ce;}return cf;}private Vector2D convertor(double longitude, double latitude, double[] cg){if (cg == null){return null;}double t = cg[0] + cg[1] * longitude;double ce = Math.Abs(latitude) / cg[9];double ch = cg[2] + cg[3] * ce + cg[4] * Math.Pow(ce, 2) + cg[5] * Math.Pow(ce, 3) + cg[6] * Math.Pow(ce, 4) + cg[7] * Math.Pow(ce, 5) + cg[8] * Math.Pow(ce, 6);t = t * (longitude < 0 ? -1 : 1);ch = ch * (latitude < 0 ? -1 : 1);var re = new Vector2D(t,ch);//re.Item1 = t;//re.Item2 = ch;return re;}//经纬度转墨卡托坐标public Vector2D convertLL2MC(double longitude, double latitude){try{longitude = getLoop(longitude, -180, 180);latitude = getRange(latitude, -74, 74);int countLLBAND = 7;double[] cg = null;for (int cf = 0; cf < countLLBAND; cf++){if (latitude >= LLBAND[cf]){cg = LL2MC[cf].ToArray();break;}}if (cg == null){for (int cf = countLLBAND - 1; cf >= 0; cf--){if (longitude <= -LLBAND[cf]){cg = LL2MC[cf].ToArray();break;}}}Vector2D result = convertor(longitude, latitude, cg);_MCx = result.x;_MCy = result.y;return result;}catch (System.Exception ex){Vector2D result = new Vector2D(); ;//result.Item1 = 0;//result.Item2 = 0;return result;}}//墨卡托坐标转经纬度坐标public Vector2D convertMC2LL(double x, double y){try{double[] cF = null;x = Math.Abs(x);y = Math.Abs(y);for (int cE = 0; cE < MCBAND.Length; cE++){if (y >= MCBAND[cE]){cF = MC2LL[cE].ToArray();break;}}Vector2D result = convertor(x, y, cF);_Longitude = result.x;_Latitude = result.y;return result;}catch (System.Exception ex){_Longitude = 0;_Latitude = 0;return new Vector2D();}}
}/// <summary>
/// double类型的一个vector2
/// </summary>
public class Vector2D
{/// <summary>/// 如果是经纬度  lat值/// </summary>public double x;/// <summary>/// 如果是经纬度  lng值/// </summary>public double y;public Vector2D(){x = 0;y = 0;}public Vector2D(double X, double Y){x = X;y = Y;}
}

然后是各种坐标之间的转换

需要拿到下载地图的时候 左下角和右上角的经纬度, 

using System;
using System.Collections.Generic;
using UnityEngine;//地图坐标轴转换
public class MapAxesTransfer : MonoBehaviour
{public static MapAxesTransfer ins;//  public MapPicData mapPicData = new MapPicData();MCObject MC = new MCObject();public int zoom = 17;/// <summary>/// 图片大小 分辨率/// </summary>public Vector2 picSize = new Vector2(256, 256);/// <summary>/// 图片的每单位像素  图片资源的设置/// </summary>public int PixelsPerUnit = 100;#region 初始坐标public Vector2D InitleftLL{get{return new Vector2D(){x = 左下角经度,y = 左下角纬度,};}}public Vector2D rightLL{get{return new Vector2D(){x = 右上角经度,y = 右上角纬度,};}}public double 左下角经度 = 117.811422d, 左下角纬度 = 36.655215d;//117.811422d, 36.655215dpublic double 右上角经度 = 118.214941d, 右上角纬度 = 36.888737d;//118.214941d, 36.888737d#endregionpublic Vector2D leftLL = new Vector2D();//左下角的MC坐标public Vector2D leftDownMC = new Vector2D();//全图左下角块坐标public Vector2 leftPiece;public Vector2 rightPiece;/// <summary>/// 单个图块unity世界坐标的长度/// </summary>public float pixel;/// <summary>/// 单块平面坐标长度/// </summary>double pieceLengh;public Vector3 GetRightPos(){return new Vector3(rightPiece.x - leftPiece.x, 20, rightPiece.y - leftPiece.y) * pixel;}public void Init(){ins = this;pixel = picSize.x / PixelsPerUnit;leftDownMC = MC.convertLL2MC(InitleftLL.x, InitleftLL.y);leftPiece = LLToPiece(InitleftLL, zoom);rightPiece = LLToPiece(rightLL, zoom);ChangeLayer(zoom);WD_Debug._Print(Math.Pow(2, zoom - 18));//MCToWorld(null);TestSet();}//更换层级 一些东西要重新计算定义public void ChangeLayer(int zoom){this.zoom = zoom;leftPiece = LLToPiece(InitleftLL, zoom);rightPiece = LLToPiece(rightLL, zoom);pieceLengh = picSize.x / Math.Pow(2, zoom - 18);WD_Debug._Print("左下角1  " + leftPiece + "  单块平面坐标的长度" + pieceLengh + "   左下角MC   " + leftDownMC.x);//下边两行先后顺序不能变 否则计算有误差  计算的时候是以leftDownMC为基准计算的   leftDownMC必须由修正后的经纬度计算ReSetLL();//修正经纬度TestSet();}//测试 更换层级之后的参考点位置GameObject g;void TestSet(){Vector2D mcpos = MC.convertLL2MC(referencePointLngLat.x, referencePointLngLat.y);Vector3 world = MCToWorld(mcpos);if (g == null)g = new GameObject("参考点");g.transform.position = world;}//修正经纬度到左下角 原因 : 在网页上取的经纬度 和实际上左下角块坐标的经纬度肯定是有偏差的, 需要重新计算private void ReSetLL(){//块坐标转平面坐标  块坐标乘以ppuVector2D mcPos = new Vector2D(){x = leftPiece.x * picSize.x + 1,y = leftPiece.y * picSize.x + 1};mcPos.x /= Math.Pow(2, zoom - 18);mcPos.y /= Math.Pow(2, zoom - 18);//MC坐标转经纬度leftLL = MC.convertMC2LL(mcPos.x, mcPos.y);}//取层级对应的所有块坐标的左下角和右上角public Dictionary<int, Vector4> ZoomToLRPiece(int startZoom, int endZoom){Dictionary<int, Vector4> zoomtoPiece = new Dictionary<int, Vector4>();for (int i = startZoom; i < endZoom; i++){zoomtoPiece[i] = new Vector4(){x = LLToPiece(InitleftLL, i).x,y = LLToPiece(InitleftLL, i).y,z = LLToPiece(rightLL, i).x,w = LLToPiece(rightLL, i).y,};}// WD_Debug._Error(zoomtoPiece.Count);return zoomtoPiece;}//取一个坐标附近各个层级的块坐标public Dictionary<int, List<Vector2>> PieceToZoom(Vector2 CenterPiece, int zoom){Dictionary<int, List<Vector2>> zoomtoPiece = new Dictionary<int, List<Vector2>>();for (int i = 15; i < 18 + 1; i++){float coe = (float)Math.Pow(2, zoom - i);Vector2 newPiece = new Vector2((int)(CenterPiece.x / coe),(int)(CenterPiece.y / coe));//  WD_Debug._Print(zoom + "   " + i + "   新中点" + newPiece + "系数?   " + (float)Math.Pow(2, zoom - i));zoomtoPiece[i] = GetDic(newPiece, new Vector2(10, 10));}return zoomtoPiece;}List<Vector2> GetDic(Vector2 CenterPiece, Vector2 colraw){List<Vector2> ListPieces = new List<Vector2>();for (int i = 1; i < ((int)(colraw.x / 2)) + 1; i++){for (int j = 1; j < ((int)(colraw.y / 2)) + 1; j++){ListPieces.Add(CenterPiece + new Vector2(i, j));ListPieces.Add(CenterPiece + new Vector2(i, -j));ListPieces.Add(CenterPiece + new Vector2(-i, -j));ListPieces.Add(CenterPiece + new Vector2(-i, j));ListPieces.Add(CenterPiece + new Vector2(-i, 0));ListPieces.Add(CenterPiece + new Vector2(i, 0));ListPieces.Add(CenterPiece + new Vector2(0, -j));ListPieces.Add(CenterPiece + new Vector2(0, j));}}return ListPieces;}/// <summary>/// 经纬度取块坐标/// </summary>public Vector2 LLToPiece(Vector2D LL, int Zoom){//先转墨卡托坐标Vector2D piece = MC.convertLL2MC(LL.x, LL.y);//转换到zoom级别下piece.x *= Math.Pow(2, Zoom - 18);piece.y *= Math.Pow(2, Zoom - 18);//墨卡托取块坐标return new Vector2((int)(piece.x / picSize.x), (int)(piece.y / picSize.x));}/// <summary>/// 世界坐标转经纬度/// </summary>/// <returns></returns>public Vector2D WorldToLL(Vector3 worldPos){//计算偏移像素量 世界坐标乘以ppu Vector2D MCOffset = new Vector2D(){x = worldPos.x * PixelsPerUnit,y = worldPos.z * PixelsPerUnit,};return MC.Point_Offset(leftLL.x, leftLL.y, MCOffset.x, MCOffset.y, zoom);}/// <summary>/// 经纬度转世界坐标/// </summary>public Vector3 LLToWorld(Vector2D LL){Vector2D TempMC = MC.convertLL2MC(LL.x, LL.y);Vector3 world = MCToWorld(TempMC);return world;}/// <summary>/// 世界坐标转墨卡托坐标  /// </summary>public Vector2D WorldToMC(Vector3 worldPos){Vector2D LL = WorldToLL(worldPos);Vector2D leftDownMCs = MC.convertLL2MC(LL.x, LL.y);return leftDownMCs;}/// <summary>/// 墨卡托转世界坐标/// </summary>public Vector3 MCToWorld(Vector2D MC){string[] X = (MC.x / pieceLengh).ToString().Split('.');string[] Y = (MC.y / pieceLengh).ToString().Split('.');//小数点之前的是块坐标long pieceX = long.Parse(X[0]);long pieceY = long.Parse(Y[0]);Vector2 piecePos = new Vector2(pieceX, pieceY);double s = MC.x / pieceLengh;//小数点之后的是偏移量float ratioX = float.Parse("0." + X[1]);float ratioY = float.Parse("0." + Y[1]);//块偏移量 Vector2 offsettt = (piecePos - leftPiece);Vector3 center = new Vector3(ratioX + offsettt.x, 0, ratioY + offsettt.y);center *= pixel;return center;}/// <summary>/// 墨卡托转块/// </summary>public Vector2 MCToPiece(Vector2D centerMC){WD_Debug._Print("zoom = " + zoom + "   " + Math.Pow(2, zoom - 18) + "    " + centerMC.x);centerMC.x *= Math.Pow(2, zoom - 18);centerMC.y *= Math.Pow(2, zoom - 18);//墨卡托取块坐标return new Vector2((int)(centerMC.x / picSize.x), (int)(centerMC.y / picSize.x));}/// <summary>/// 米坐标转世界坐标/// </summary>public Vector3 CMToWorld(float x, float y){//转经纬度Vector2D LL = CMToLatLng(x, y);//经纬度转世界坐标Vector2D MCs = MC.convertLL2MC(LL.x, LL.y);if (MCs.x == 0) return new Vector3();return MCToWorld(MCs);}//参考点private Vector2D referencePointLngLat = new Vector2D(){x = 118.050357d,y = 36.860605d};//米坐标private Vector2D referencePointXY = new Vector2D(){x = -3576280,y = -1258750,};/// <summary>/// 米坐标 转经纬度/// </summary>public Vector2D CMToLatLng(float x, float y){double Dx = x * 10.0f - referencePointXY.x / 1000.0f;double Dy = y * 10.0f - referencePointXY.y / 1000.0f;double c = 6371393f * 2f * Math.PI;double ss = c * Math.Cos(referencePointLngLat.y * Mathf.Deg2Rad);//当前纬度的周长double d_m = 360f / ss;//度每米double cd = 360f / c;//赤道度每米Vector2D LL = new Vector2D(){//经度x = Dx * d_m + referencePointLngLat.x,//纬度y = Dy * cd + referencePointLngLat.y};return LL;}
}

转换的方法写好了  终于要到了使用的时候了

上代码

using System;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.EventSystems;public class MapManager : MonoBehaviour
{public static MapManager ins;[Header("是否打开日志")]public bool isDebug = false;public Dictionary<Vector2, TempPic> tempPics = new Dictionary<Vector2, TempPic>();public int zoom = 15;private MCObject MC = new MCObject();public MapAxesTransfer mapAxesTransfer;private MapTextureManager LoadTexture;/// <summary>/// 尺寸/// </summary>private Vector2 numSize;[Header("初始化后视角是否在中心")]public bool iscenter;void Start(){WD_Debug.ins = isDebug;ins = this;mapAxesTransfer = GetComponentInChildren<MapAxesTransfer>();LoadTexture = GetComponent<MapTextureManager>();mapAxesTransfer.Init();int sizes = (int)(1920 / mapAxesTransfer.picSize.x);numSize = new Vector2(sizes + 3, sizes + 3);//决定地图几圈的地方WD_Debug._Print("计算排列  " + numSize + "   " + Screen.width);//InitMap();InitMap(iscenter);}Vector2D CameraLL;//更换层级public void ChangeLayer(int zoom){Vector3 Screensize = new Vector3(Screen.width / 2, Screen.height / 2);ray = Camera.main.ScreenPointToRay(Screensize);if (Physics.Raycast(ray, out hit)) //{//换之前先记录下当前中心位置的平面坐标   换之后 没有正确的块坐标不好找Vector2D centerMC = mapAxesTransfer.WorldToMC(hit.point);CameraLL = mapAxesTransfer.WorldToLL(hit.point);//WD_Debug._Print("换之前的MC坐标" + centerMC.x + "   " + centerMC.y);this.zoom = zoom;mapAxesTransfer.ChangeLayer(zoom);//这里已经将左下角块坐标重新计算了tempPics.Remove(centerTemppic.PieceCoordinates);//这里删除之前的中心位置  后边设置得是新的中心块centerTemppic.PieceCoordinates = mapAxesTransfer.MCToPiece(centerMC);//计算出下一个中心快的坐标之后  在计算其位置 以左下角为00点结算Vector2 offset = centerTemppic.PieceCoordinates - mapAxesTransfer.leftPiece;Vector3 InitPosition = new Vector3(offset.x, 0, offset.y);InitPosition *= mapAxesTransfer.pixel;// WD_Debug._Print("转换之后的块坐标" + centerTemppic.PieceCoordinates + " 坐标差 " + offset + "  位置" + InitPosition);centerTemppic.transform.position = InitPosition;centerTemppic.isUpdate = true;centerPos = centerTemppic.PieceCoordinates;InitMap(true,true);}}Camera cameramain;public void SetMapEnable(bool isEnable){foreach (var map in tempPics.Values)map.SetEnable(isEnable);}void Update(){TileUpdate();}/// <summary>/// 中心图块周围坐标对应图块的信息/// </summary>private Dictionary<Vector2, Vector3> Groundpoints;/// <summary>/// 中心图块/// </summary>public TempPic centerTemppic;private Ray ray;private RaycastHit hit;//刷新地图void TileUpdate(){ray = Camera.main.ScreenPointToRay(Input.mousePosition);if (!EventSystem.current.IsPointerOverGameObject()){if (Physics.Raycast(ray, out hit, 1000, 1 << 8)) //{if (Input.GetMouseButtonUp(0)){mapAxesTransfer.WorldToMC(hit.point);}}}if (Input.GetMouseButtonUp(2)|| Input.GetMouseButtonUp(1)){//找新的中心图 并刷新其周围图块 新的中心块一定是距离相机最近的一个 需要计算UpdateCamera();}}public void UpdateCamera(){Vector3 Screensize = new Vector3(Screen.width / 2, Screen.height / 2);ray = Camera.main.ScreenPointToRay(Screensize);if (Physics.Raycast(ray, out hit))//{if (hit.transform.gameObject.layer == 8){if (hit.transform != centerTemppic.transform){Groundpoints[centerTemppic.PieceCoordinates] = centerTemppic.transform.position;centerTemppic = hit.transform.GetComponent<TempPic>();centerTemppic.isUpdate = true;Groundpoints.Remove(centerTemppic.PieceCoordinates);MapUpdate();}}}}[Header("是否使用输入得到中心点位置")]public bool IsInputCenter = false;[Header("输入的中心点经纬度")]public double inputlat = 118.050357d, inputlng = 36.860605d;void GetInputCenter(){centerPos = mapAxesTransfer.LLToPiece(new Vector2D(inputlat, inputlng), zoom);}public Vector2 centerPos;//中心块的块坐标//临时存储要删掉的图 public List<GameObject> TempDeleteMaps = new List<GameObject>();/// <summary>/// 初始化地图 以平面坐标左下角的图块的世界坐标为000;/// </summary>/// <param name="Center"></param>void InitMap(bool Center,bool isRefresh = false){Vector3 InitPosition = Vector3.zero;var TempPicPre = Resources.Load<GameObject>("prefabgis/mapresources/TempPic");if (Center){if (centerPos == Vector2.zero) //调用之前 赋值中心点.则 是更新地图层级.centerPos = new Vector2(Convert.ToInt32((mapAxesTransfer.leftPiece.x + mapAxesTransfer.rightPiece.x) / 2),Convert.ToInt32((mapAxesTransfer.leftPiece.y + mapAxesTransfer.rightPiece.y) / 2));if (IsInputCenter) GetInputCenter();Vector2 offset = centerPos - mapAxesTransfer.leftPiece;// Debug.Log(offset);InitPosition = new Vector3(/*(int)numSize.x / 2 + 1 + */offset.x, 0, /*(int)numSize.y / 2 + 1 +*/ offset.y);InitPosition *= mapAxesTransfer.pixel;}else{//新的初始化 以图块左下角的图为原点  旧的是全图块中间的图作为原点//中心图块的块坐标   x是列数 y是行数centerPos = new Vector2(Convert.ToInt32((mapAxesTransfer.leftPiece.x + numSize.x / 2 + 1)), Convert.ToInt32((mapAxesTransfer.leftPiece.y + numSize.y / 2 + 1)));//初始化生成 具体数量是否要可变得 ?InitPosition = new Vector3((int)numSize.x / 2 + 1, 0, (int)numSize.x / 2 + 1);InitPosition *= mapAxesTransfer.pixel;//设置相机}//初始位置放到目标点if (isRefresh)RefreshMapLayer();elseSetMap(TempPicPre, centerPos, InitPosition);Vector3 cameraPos;if (isRefresh)cameraPos = mapAxesTransfer.LLToWorld(CameraLL);elsecameraPos = new Vector3(InitPosition.x + mapAxesTransfer.pixel / 2, 20, InitPosition.z + mapAxesTransfer.pixel / 2);Camera.main.transform.position = new Vector3(cameraPos.x, 20, cameraPos.z);}/// <summary>/// 以左下角为零点计算地图/// </summary>void SetMap(GameObject TempPicPre, Vector2 centerPos, Vector3 InitPosition){WD_Debug._Print("初始化");//新的初始化 以图块左下角的图为原点  旧的是全图块中间的图作为原点GameObject centerTemp = Instantiate(TempPicPre, InitPosition, Quaternion.Euler(90, 0, 0)) as GameObject;centerTemp.transform.SetParent(transform);centerTemp.name = centerPos.ToString();centerTemppic = centerTemp.GetComponent<TempPic>();centerTemppic.PieceCoordinates = centerPos;LoadTexture.AddLoadTask(zoom, (int)centerPos.x, (int)centerPos.y, tex =>{centerTemppic.SetSprite(tex);});tempPics[centerPos] = centerTemppic;Groundpoints = centerTemppic.BoundsMapPos(numSize, mapAxesTransfer.pixel);foreach (var pos in Groundpoints.Keys){GameObject temp = Instantiate(TempPicPre, Groundpoints[pos], Quaternion.Euler(90, 0, 0)) as GameObject;temp.transform.SetParent(transform);TempPic temppic = temp.GetComponent<TempPic>();LoadTexture.AddLoadTask(zoom, (int)pos.x, (int)pos.y, tex =>{temppic.SetSprite(tex);});// temppic.LoadPicSingleInit(GetPathZoom(pos));temppic.name = pos.ToString();temppic.PieceCoordinates = pos;tempPics[pos] = temppic;}LoadTexture.Preload(mapAxesTransfer.PieceToZoom(centerPos, zoom));}/// <summary>/// 中心图片更换之后调用 刷新周围 算法需要优化/// </summary>void MapUpdate(){Dictionary<Vector2, Vector3> newGroundpoints = centerTemppic.BoundsMapPos(numSize, mapAxesTransfer.pixel);List<Vector2> SamePoint = new List<Vector2>();//相同的点foreach(var pos in Groundpoints.Keys){foreach (var poss in newGroundpoints.Keys){if (!SamePoint.Contains(pos) && !SamePoint.Contains(poss)){if (!newGroundpoints.ContainsKey(pos) && !Groundpoints.ContainsKey(poss)){SamePoint.Add(poss);SamePoint.Add(pos);if(tempPics.ContainsKey(pos)){TempPic temp = tempPics[pos];temp.transform.position = newGroundpoints[poss];temp.PieceCoordinates = poss;temp.name = poss.ToString();LoadTexture.AddLoadTask(zoom, (int)poss.x, (int)poss.y, tex =>{temp.SetSprite(tex);});//temp.LoadPicSingleInit(GetPathZoom(poss));tempPics[poss] = temp;tempPics.Remove(pos);}}}}}SamePoint.Clear();Groundpoints = newGroundpoints;}/// <summary>/// 刷新地图层级/// </summary>void RefreshMapLayer(){Dictionary<Vector2, TempPic> Vec_Temps = new Dictionary<Vector2, TempPic>();Vec_Temps[centerTemppic.PieceCoordinates] = centerTemppic;centerTemppic.name = centerTemppic.PieceCoordinates.ToString();LoadTexture.AddLoadTask(zoom, (int)centerTemppic.PieceCoordinates.x, (int)centerTemppic.PieceCoordinates.y, tex =>{centerTemppic.SetSprite(tex);});Dictionary<Vector2, Vector3> newGroundpoints = centerTemppic.BoundsMapPos(numSize, mapAxesTransfer.pixel);int index = 0;foreach (var poss in newGroundpoints.Keys){var obj = tempPics.ElementAt(index);index++;TempPic temp = obj.Value;if (temp.transform.position != newGroundpoints[poss])//刷层级的时候其实,15级以下不会有重复  temp.transform.position = newGroundpoints[poss];temp.PieceCoordinates = poss;temp.name = poss.ToString();LoadTexture.AddLoadTask(zoom, (int)poss.x, (int)poss.y, tex =>{temp.SetSprite(tex);});Vec_Temps[poss] = temp;}tempPics = Vec_Temps;}}

还有一个类 图片预制体上需要挂的

主要用来计算周围图块的坐标

using UnityEngine;
using System.Collections.Generic;public class TempPic : MonoBehaviour 
{public bool isUpdate = true;//是否需要刷新位置/// <summary>/// 块坐标/// </summary>public Vector2 PieceCoordinates;/// <summary>/// 四周八个相邻图片的位置  图位置 对应块坐标/// </summary>/// <returns></returns>public Dictionary<Vector2, Vector3> BoundsMapPos(Vector2 pieceNum,float size){if (isUpdate || _boundsMapPos.Count == 0){isUpdate = false;_boundsMapPos.Clear();GetDic(pieceNum, size);}return _boundsMapPos;}private readonly Dictionary<Vector2, Vector3> _boundsMapPos = new Dictionary<Vector2, Vector3>();public void SetColor(Color col){if (spriterender == null)spriterender = GetComponent<SpriteRenderer>();spriterender.color = col;}void GetDic(Vector2 colraw, float size){for (int i = 1; i < ((int)(colraw.x / 2))+1; i++){for (int j = 1; j < ((int)(colraw.y / 2))+1; j++){_boundsMapPos[PieceCoordinates + new Vector2(i, j)] = transform.position + new Vector3(size*i, 0, size*j);_boundsMapPos[PieceCoordinates + new Vector2(i, -j)] = transform.position + new Vector3(size * i, 0, -size * j);_boundsMapPos[PieceCoordinates + new Vector2(-i, -j)] = transform.position + new Vector3(-size * i, 0, -size * j);_boundsMapPos[PieceCoordinates + new Vector2(-i, j)] = transform.position + new Vector3(-size * i, 0, size * j);_boundsMapPos[PieceCoordinates + new Vector2(-i, 0)] = transform.position + new Vector3(-size * i, 0, 0);_boundsMapPos[PieceCoordinates + new Vector2(i, 0)] = transform.position + new Vector3(size * i, 0, 0);_boundsMapPos[PieceCoordinates + new Vector2(0, -j)] = transform.position + new Vector3(0, 0, -size * j);_boundsMapPos[PieceCoordinates + new Vector2(0, j)] = transform.position + new Vector3(0, 0, size * j);}}}private bool isshow = true;public void SetEnable(bool enable){isshow = enable;spriterender.enabled = enable;}private SpriteRenderer spriterender;public void SetSprite(Sprite sp){if (spriterender == null)spriterender = GetComponent<SpriteRenderer>();spriterender.sprite = sp;if (!spriterender.enabled && isshow)spriterender.enabled = true;if (GetComponent<BoxCollider>() == null){BoxCollider box = gameObject.AddComponent<BoxCollider>();box.isTrigger = false;}}
}

再来一个相机上挂的

  主要就是移动和范围控制 以防拖动到了范围外的地方去

using UnityEngine;
using UnityEngine.EventSystems;public class CameraMove : MonoBehaviour
{public float speed = 2;private Camera cameraMain;private Vector2 screenSize;public Vector3 oldpos;void Start (){cameraMain = Camera.main;oldpos = cameraMain.transform.position;}void LateUpdate (){ControlCamera();}/// <summary>/// 控制相机/// </summary>void ControlCamera() {//视角拉伸if (Input.GetAxis("Mouse ScrollWheel") != 0&&!EventSystem.current.IsPointerOverGameObject()){cameraMain.orthographicSize += Input.GetAxis("Mouse ScrollWheel") * -1;if (MapManager.ins == null) return;if (cameraMain.orthographicSize<3f){if (MapManager.ins.zoom<18){cameraMain.orthographicSize = 5f;MapManager.ins.ChangeLayer(MapManager.ins.zoom + 1);}else{if (cameraMain.orthographicSize < 2f) cameraMain.orthographicSize = 2f;}}if (cameraMain.orthographicSize>6f){if (MapManager.ins.zoom > 13){cameraMain.orthographicSize = 4f;MapManager.ins.ChangeLayer(MapManager.ins.zoom - 1);}else{if (cameraMain.orthographicSize > 7f) cameraMain.orthographicSize = 7f;}}}//视角滑动//非精准移动if (Input.GetMouseButton(2)||Input.GetMouseButton(1)){//调用float x = Input.GetAxis("Mouse X");float y = Input.GetAxis("Mouse Y");transform.Translate(new Vector3(-x, -y, 0) * Time.deltaTime * 90 * speed);//范围圈定GetOffset();MaxPos = MapManager.ins.mapAxesTransfer.GetRightPos();if (transform.position.x > MaxPos.x- Screenoffset.x){transform.position = new Vector3(MaxPos.x - Screenoffset.x, 20, transform.position.z);}if (transform.position.z > MaxPos.z- Screenoffset.z){transform.position = new Vector3(transform.position.x, 20, MaxPos.z - Screenoffset.z);}if (transform.position.x < Screenoffset.x){transform.position = new Vector3(Screenoffset.x, 20, transform.position.z);}if (transform.position.z < Screenoffset.z){transform.position = new Vector3(transform.position.x, 20, Screenoffset.z);}}}public Vector3 MaxPos;private Vector3 Screenoffset;RaycastHit hit;Ray ray;void GetOffset(){ray = Camera.main.ScreenPointToRay(screenSize);if(Physics.Raycast(ray,out hit)){Screenoffset = transform.position- hit.point;}}
}

ok 到这里呢  就基本大功告成了  

效果图的话 录得gif太大了 这里超出了限制 上传了资源可以下载看下

 

更多推荐

unity中实现百度本地瓦片地图生成地图与坐标转换

本文发布于:2024-03-06 21:15:56,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1716397.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:地图   瓦片   坐标   unity

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!