unity使用虚拟列表方法实现九宫格加载地形地图

地形地图管理器

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PlaneManager : MonoBehaviour
{
    public Slider slider;//控制地图缩放
    public Transform water;//水面
    public RectTransform mapBox;//地图的容器
    public Image map;//地图块
    public Transform player;//玩家
    public MyPlane plane;//地形块
    public static int wh = 100;//地形宽高
    Vector2Int pos = Vector2Int.zero;//人物当前所处地图id
    Dictionary<Vector2Int, MyPlane> show = new Dictionary<Vector2Int, MyPlane>();//已经显示出来的数据
    public static Dictionary<Vector2Int, Dictionary<Vector3, int>> treeDic = new Dictionary<Vector2Int, Dictionary<Vector3, int>>();//全部地图上的植物数据
    Queue<MyPlane> pool = new Queue<MyPlane>();//对象池 存储不使用的物体

    Dictionary<Vector2Int, Image> showImage = new Dictionary<Vector2Int, Image>();//已经显示出来的数据
    Queue<Image> MapPool = new Queue<Image>();//对象池 存储不使用的物体
    
    public static float mapp = 0;//地形与地图的大小比例
    void Start()
    {
        //计算比例尺(用于地图和地形的坐标转换)
        mapp = map.rectTransform.sizeDelta.x / (float)wh;
        //计算玩家初始高度
        player.transform.position = Vector3.up *( MyPlane.Turbulence(0, 0, wh) * 10);
        //初始化地形
        OnMove();
    }
    public void OnSlider()
    {
        //控制地图缩放
        mapBox.localScale = Vector3.one * slider.value;
    }
    //人物移动后调用
    public void OnMove()
    {
        //确定玩家所属地形块
        pos = new Vector2Int((int)(player.position.x - wh / 2) / wh, (int)(player.position.z - wh / 2) / wh);
        //获取周围9块地图
        Dictionary<Rect, Vector2Int> dic = new Dictionary<Rect, Vector2Int>();
        for (int i = -1; i <= 1; i++)
        {
            for (int j = -1; j <= 1; j++)
            {
                Rect rect = new Rect((i + pos.x) * wh, (j + pos.y) * wh, wh, wh);
                //这里是创建所以全部添加,如果是加载需要判断有数据再添加
                dic.Add(rect, new Vector2Int(i + pos.x, j + pos.y));
            }
        }
        //玩家矩形1.5倍地图块大小,与之重叠的地形块会被创建
        Rect playerRect = new Rect(player.position.x - wh / 2, player.position.z - wh / 2, wh * 1.5f, wh * 1.5f);
        //循环找到需要显示的数据
        List<Vector2Int> datas = new List<Vector2Int>();
        foreach (var item in dic)
        {
            if (IsLap(playerRect, item.Key))
            {
                datas.Add(item.Value);
            }
        }
        //找到已经显示但不需要显示的存入对象池
        List<Vector2Int> dustbin = new List<Vector2Int>();//垃圾箱
        foreach (var item in show)
        {
            if (!datas.Contains(item.Key))
            {
                //地形
                item.Value.gameObject.SetActive(false);
                pool.Enqueue(item.Value);//存入对象池
                dustbin.Add(item.Key);//存入垃圾箱
                //地图
                showImage[item.Key].gameObject.SetActive(false);
                MapPool.Enqueue(showImage[item.Key]);
            }
        }
        //清理垃圾箱
        foreach (var item in dustbin)
        {
            //地形
            show.Remove(item);
            //地图
            showImage.Remove(item);
        }
        //创建新数据
        foreach (var data in datas)
        {
            //判断数据是否已经显示
            if (!show.ContainsKey(data))
            {
                if (pool.Count > 0)
                {
                    //地形
                    show.Add(data, pool.Dequeue());
                    show[data].gameObject.SetActive(true);
                    //地图
                    showImage.Add(data, MapPool.Dequeue());
                    showImage[data].gameObject.SetActive(true);
                }
                else
                {
                    //地形
                    show.Add(data, Instantiate(plane));
                    //地图
                    showImage.Add(data, Instantiate(map,mapBox));
                }
                //地形
                show[data].Init(data, showImage[data]);
                //地图
                showImage[data].GetComponent<MapMove>().pos = data;
            }
        }
    }
    private void Update()
    {
        //这些应该是每次移动后调用(一般使用消息中心,Update消耗太大且浪费)
        OnMove();
        //计算地图轴心(使用轴心做移动才不会影响缩放后的位置)
        float px = (player.position.x + wh / 2) / wh;
        float py = (player.position.z + wh / 2) / wh;
        mapBox.pivot = new Vector2(px, py);
        //水面随人物移动
        water.position = new Vector3(player.position.x, 3, player.position.z);
    }
    //判断两个矩形是否相交
    public bool IsLap(Rect rect1, Rect rect2)
    {
        float rect1MinX = rect1.x - rect1.width / 2;
        float rect1MaxX = rect1.x + rect1.width / 2;
        float rect1MinY = rect1.y - rect1.height / 2;
        float rect1MaxY = rect1.y + rect1.height / 2;

        float rect2MinX = rect2.x - rect2.width / 2;
        float rect2MaxX = rect2.x + rect2.width / 2;
        float rect2MinY = rect2.y - rect2.height / 2;
        float rect2MaxY = rect2.y + rect2.height / 2;

        return rect1MinX < rect2MaxX && rect2MinX < rect1MaxX && rect1MinY < rect2MaxY && rect2MinY < rect1MaxY;
    }
}

地形块地图块统一计算

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class MyPlane : MonoBehaviour
{
    public GameObject[] treeArr;//植物预制体数组
    public MeshFilter filter;//网格过滤器
    public MeshRenderer renderer;//网格渲染器
    public MeshCollider collider;//网格碰撞框

    public Texture2D gao;//地图高处显示纹理
    public Texture2D di;//地图低处显示纹理
    public Texture2D texture;//地图纹理

    public float waterH = 3;//水平面高度
    List<Queue<GameObject>> pool = new List<Queue<GameObject>>();//植物对象池
    List<List<GameObject>> treeList = new List<List<GameObject>>();//植物列表
    private void Awake()
    {
        //根据植物类型创建列表和对象池
        for (int i = 0; i < treeArr.Length; i++)
        {
            pool.Add(new Queue<GameObject>());
            treeList.Add(new List<GameObject>());
        }
    }
    void Start()
    {
    }
    //初始化和更新地形
    public void Init(Vector2Int pos,Image image)
    {
        //把所有遗留植物存入对象池
        for (int i = 0; i < treeList.Count; i++)
        {
            foreach (var item in treeList[i])
            {
                item.SetActive(false);
                pool[i].Enqueue(item);
            }
        }
        //判的是否有植物数据,没有创建新数据
        Dictionary<Vector3, int> trees;
        if (PlaneManager.treeDic.ContainsKey(pos))
        {
            trees = PlaneManager.treeDic[pos];
        }
        else
        {
            trees = new Dictionary<Vector3, int>();
        }
        //创建地图纹理
        texture = new Texture2D(PlaneManager.wh, PlaneManager.wh);
        //创建地形网格
        Mesh mesh = new Mesh();
        //绘制网格使用顶点助手
        VertexHelper vh = new VertexHelper();
        //循环宽高
        for (int i = 0; i <= PlaneManager.wh; i++)
        {
            for (int j = 0; j <= PlaneManager.wh; j++)
            {
                //根据坐标计算高度
                float y = Turbulence((pos.x * PlaneManager.wh + i), (pos.y * PlaneManager.wh + j), PlaneManager.wh);
                //计算uv坐标
                float uvx = (float)i / (float)PlaneManager.wh;
                float uvy = (float)j / (float)PlaneManager.wh;
                //顶点
                Vector3 pp = new Vector3(i, y * 10, j);
                //添加顶点
                vh.AddVert(pp, Color.white, new Vector2(uvx, uvy));
                //添加绘制三角形
                if (i != PlaneManager.wh && j != PlaneManager.wh)
                {
                    //每4个顶点绘制2个三角形(所以宽高各少一个)
                    vh.AddTriangle(i * (PlaneManager.wh + 1) + j, i * (PlaneManager.wh + 1) + j + 1, (i + 1) * (PlaneManager.wh + 1) + j);
                    vh.AddTriangle(i * (PlaneManager.wh + 1) + j + 1, (i + 1) * (PlaneManager.wh + 1) + j + 1, (i + 1) * (PlaneManager.wh + 1) + j);

                    //绘制地图纹理
                    if (y * 10 < waterH)//低于水平线地图绘制蓝色
                    {
                        texture.SetPixel(i, j, Color.blue);
                    }
                    else//高于水平线地图绘制高低颜色差值
                    {
                        texture.SetPixel(i, j, Color.Lerp(di.GetPixel(i, j), gao.GetPixel(i, j), y));
                    }
                }
                //创建植物
                if (PlaneManager.treeDic.ContainsKey(pos))//有数据读取植物数据
                {
                    if (trees.ContainsKey(pp))
                    {
                        //获得植物id
                        int treeid = trees[pp];
                        GameObject tree;
                        //对象池有优先对象池取,没有再创建
                        if (pool[treeid].Count > 0)
                        {
                            tree = pool[treeid].Dequeue();
                            tree.SetActive(true);
                        }
                        else
                        {
                            tree = Instantiate(treeArr[treeid], transform);
                        }
                        //修改植物坐标
                        tree.transform.localPosition = pp;
                        //加入植物列表
                        treeList[treeid].Add(tree);
                    }
                }
                else if(y * 10 > waterH && Random.Range(0,100) < 10)//没植物数据随机创建(高于水平面才创建植物)
                {
                    //随机植物类型
                    int treeid = Random.Range(0, treeArr.Length);
                    GameObject tree;
                    if (pool[treeid].Count > 0)
                    {
                        tree = pool[treeid].Dequeue();
                        tree.SetActive(true);
                    }
                    else
                    {
                        tree = Instantiate(treeArr[treeid], transform);
                    }
                    tree.transform.localPosition = pp;
                    treeList[treeid].Add(tree);
                    //存入植物数据
                    trees.Add(pp, treeid);
                }
            }
        }
        if (!PlaneManager.treeDic.ContainsKey(pos))
        {
            //保存随机出来的植物数据
            PlaneManager.treeDic.Add(pos, trees);
        }
        //把顶点助手的信息写入mesh
        vh.FillMesh(mesh);
        //显示网格
        filter.mesh = mesh;
        //renderer.material.mainTexture = texture;
        collider.sharedMesh = mesh;
        //设置地形块位置
        transform.position = new Vector3(pos.x * PlaneManager.wh, 0, pos.y * PlaneManager.wh);

        //设置地图纹理显示类型
        texture.wrapMode = TextureWrapMode.Clamp;
        //保存纹理设置
        texture.Apply();
        //创建材质球(使用默认UI的shader)
        Material material = new Material(Shader.Find("UI/Default"));
        //为材质球设置纹理
        material.mainTexture = texture;
        //为地图块设置材质球
        image.material = material;
        //设置地图块位置
        image.rectTransform.anchoredPosition = new Vector2((pos.x + 0.5f) * image.rectTransform.sizeDelta.x, (pos.y + 0.5f) * image.rectTransform.sizeDelta.y);
    }

    //调整后的噪声算法(x坐标,y坐标,地图的宽高)用于计算地形高度
    public static float Turbulence(float x, float z, int wh)
    {
        x = x * 0.03f;
        z = z * 0.03f;
        float y = -0.5f;
        for (int f = 1; f <= (wh + 1) / 12; f *= 2)
        {
            y += Mathf.Abs(Mathf.PerlinNoise(z * f, x * f) / f);
        }
        return y;
    }
}

点击地图计算在地形上的位置(可用于导航移动)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class MapMove : MonoBehaviour, IPointerClickHandler
{
    public static Vector3 navpos;//点击后的地形位置
    public Vector2Int pos;//地图块坐标
    //点击
    public void OnPointerClick(PointerEventData eventData)
    {
        //计算点击位置相对于当前地图块的位置
        Vector2 mypos = (transform as RectTransform).InverseTransformPoint(eventData.position);
        //地图块轴心在中间所以加上一半的宽高
        mypos += (transform as RectTransform).sizeDelta / 2;
        //计算位于地图上的位置 = 点击位置 / 比例尺 + 地图块坐标 * 地形宽高
        Vector2 v2 = mypos / PlaneManager.mapp + pos * PlaneManager.wh;
        //计算该位置高度
        float y = MyPlane.Turbulence(v2.x, v2.y, PlaneManager.wh) * 10;
        //点击的位置坐标(静态可在任意位置获取)
        navpos = new Vector3(v2.x, y, v2.y);
        Debug.Log(navpos);
    }
}

  • 12
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值