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

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

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

实在懒得找的我把我用的分享出来:https://download.csdn.net/download/qq_35080168/12411438

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

如下图 大概的目录结构 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]));
        else
            Resources.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*/;//256
        re.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; // PI
    static double DEF_2PI = 6.28318530712; // 2*PI
    static double DEF_PI180 = 0.01745329252; // PI/180.0
    static 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.655215d
    public double 右上角经度 = 118.214941d, 右上角纬度 = 36.888737d;//118.214941d, 36.888737d
    #endregion



    public 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()
    {
        //块坐标转平面坐标  块坐标乘以ppu
        Vector2D 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();
        else
            SetMap(TempPicPre, centerPos, InitPosition);
        Vector3 cameraPos;
        if (isRefresh)
            cameraPos = mapAxesTransfer.LLToWorld(CameraLL);
        else
            cameraPos = 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太大了 这里超出了限制 上传了资源可以下载看下https://download.csdn.net/download/qq_35080168/12411872

 

评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值