unity3d 分段加载地形_unity3d 精细地形分级加载 - CSDN
  • 官方中文论坛中有一个帖子讲得比较在点,帖子地址在这里:http://forum.china.unity3d.com/forum.php?mod=viewthread&tid=117。总结起来有三点:地形分块,动态加载和卸载,内存优化。其中前两点是最

    在很多仿真和游戏应用中都需要大规模地形,这样会使3D环境似乎“无限大”,增加用户的真实感,比如飞行模拟游戏。那么在Unity中如何实现大规模地形场景呢?官方中文论坛中有一个帖子讲得比较在点,帖子地址在这里:http://forum.china.unity3d.com/forum.php?mod=viewthread&tid=117。总结起来有三点:地形分块,动态加载和卸载,内存优化。其中前两点是最基本的。

    最近一直在研究这个问题,也查了好多资料,博客、论坛,甚至一些科研论文,还并没有发现一套完整的解决方案(也许Asset Store上有,不过并没有找到免费的微笑)。于是决定在这里将研究过程中的一些思路和实施方法分享给诸位Unity开发者,我会根据研究的进度不定时地更新这个系列。欢迎各位一起讨论!

    我采用WorldMachine来绘制地形,并分块导出heightmap和texture。WorldMachine的使用暂时并没有深入研究,而分块导出的方法在WM手册上描述的比较详细。这一部分以后会单独更一篇,现在我们暂且不论。那么有了heightmap和texture如何创建Unity地形,或者说是Terrain Object呢?这里介绍两种方法:

    1. 在Editor中手动创建

    这部分很多博客都有介绍,简单介绍,有几个关键地方提一下。

    创建Terrain对象,它自动包含一个Terrain组件,包含了所有属性设置。


    点击这个Import Raw...按钮,导入你的高度图,我从WM中导出的heightmap资源采用.r16格式(16位raw),选择后弹出这个对话框:


    Unity会自动识别heightmap的一些信息。我的平台选择Windows(mac有什么不同呢?)。Terrain Size设置包括地形大小和高度,注意这个Y值是你希望的地形高度中最大值和最小值之间的差值,它决定了你的地形的整体高度。我一开始对这个值的存在有一点疑惑,直到方法2的实现才明白其中的细节(先按下不表微笑)。另外一个最重要的地方,一定要勾选Flip Vertically,为什么呢?WM三维空间中采用右手系(y轴向上),而Unity采用左手系(y轴也向上),在二者x轴正向相同的情况下,z轴恰好反转。如果没有勾选这个选项,你的texture显然就没法用了。设置完毕后,效果如下:


    加上贴图也很简单,不过要将texture尺寸设置为和地形一样的大小,最终效果如下:


    如此,一块地形就做好了,可以将它做成Prefab方便在场景中调用。不过这种方法的问题是,我的大地形可能要分许多块,那我总不能每一块都这样手工做一遍吧...不管工作量是否允许,面对这种重复工作我们都应该尽量去找到自动化实现的方法!因此就有了方法2。

    2. 通过Editor扩展批量生产

    不过说到底这个方法还是看的别人的微笑,在Unity Community Wiki这个社区发现的一篇好帖,上链接:http://wiki.unity3d.com/index.php/TerrainImporter。看完发现这个帖子是在Unity3版本时创建的微笑,似乎现在这个社区都没有什么人光顾了。。。

    Unity允许对Editor功能进行扩展,方法是在Editor文件夹下创建脚本,使用的API大多在UnityEditor命名空间中,这样我们可以用脚本代替一些手动工作。它的意义在于脚本和一些配置文档(比如txt,xml格式)作为静态的存在,可以保存我们的编辑指令、设置、操作等,方便完成对多个不同对象的重复操作,简单来说就是说就是“自动化”地“批量生产”。

    现在,我们要编写Editor Script来实现方法1中的工作。那么首先我们要查询Script API文档,搞明白地形创建的流程。地形的核心在于TerrainData,每一次我们在hiearchy视图中创建一个Terrain对象时,Unity会自动为它创建一个TerrainData类型的对象,如图那个灰色的东西就是。


    我们的heghtmap和texture都要交付于这个类型来实现。这里,我们先只介绍heightmap是如何加载进TerrainData对象的。先上代码:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    using UnityEditor;
    using System.IO;
    using System;
    
    public class ImportTerrain : MonoBehaviour
    {
        [MenuItem("AssetDatabase/ImportTerrain")]
        static void GenerateTerrain()
        {
            string terrainDataPath = "Assets/512Test/td.asset"; //路径
            //创建TerrainData对象
            TerrainData terrainData = (TerrainData)AssetDatabase.LoadAssetAtPath(
                terrainDataPath, typeof(TerrainData));
            if (!terrainData)
            {
                terrainData = new TerrainData();
                AssetDatabase.CreateAsset(terrainData, terrainDataPath);
            }
            //地形设置
            Vector3 terrainSize = new Vector3(512, 250, 512); //地形的大小,最高、最低点的差值
            terrainData.size = terrainSize;
            terrainData.heightmapResolution = 513;
            float[,] height = new float[512, 512]; //地形高度数组
            //读取高度文件
            FileInfo hmFile = new FileInfo(@"Assets/512Test/output_x0_y0.r16");
            FileStream hmFs = hmFile.OpenRead();
            const int BYTESIZE = 512 * 512 * 2;
            byte[] hmB = new byte[BYTESIZE];
            int result = hmFs.Read(hmB, 0, BYTESIZE); //读取.r16字节流
            hmFs.Close();
            //赋值
            int i = 0;
            for (int x = 0; x < 512; x++)
            {
                for (int y = 0; y < 512; y++)
                {
                    //注意两点:
                    //Windows字节序
                    //右手系转换到左手系
                    height[511-x, y] = (hmB[i++] + hmB[i++] * 256.0f) / 65535.0f;
                }
            }
            terrainData.SetHeights(0, 0, height); //一切都是为了这个方法...
        }
    }
    

    这个脚本在Editor中添加了一个ImportTerrain按钮,点击这个按钮会自动创建一个名为hd的TerrainData资源,有了这个资源我们可以在场景中使用脚本很方便地创建Terrain对象。这个功能实现的重点在于:如何从.r16格式的高度图文件中提取我们需要的TerrainData.SetHeights方法的参数——一个二维float型数组

    首先,如何解析.r16文件?从WorldMachine中导出的.r16文件时一种二进制文件,存储了所有高度的字节,每个高度都有16位精度(而raw格式是8位)。比如从WM中导出一张分辨率为512*512的heightmap,格式存储为.r16,这样每个高度2byte,共512*512*2byte=512kb,查看一下.r16文件大小果然是这样。那么使用.NET平台的库函数可以很方便地将这些字节读取到内存中,存储为一个字节数组

    其次,如何解析这个字节数组?显然每两个相邻字节表示一个高度值,在Windows中字节序是低8位在前,高8位在后存储(iOS正好相反)。从帖子中的代码来看,.r16是按照行优先存储的,并且是以左上角为坐标原点。还记得我们使用Editor中Import Heightmap按钮时,弹出界面有一个Flipp y(翻转y轴)的选项吧?没错,我们的代码要考虑左手系与右手系的不同。最后一点,地形高度数组的元素全部是介于0~1之间的float型数值。实际高度值还由最大高度和最小高度之间的差值决定,这个值在terrainData.size中设置。我们可以发现这里的实现,与方法1中高度图对话框的设置之间具有明显的对应关系,也能够解释方法1中我们留下的一些疑问。


    总结一下,这个脚本只是我参照原贴中javascript脚本,用C#写的测试脚本,存在着许多问题。比如,只实现了高度图的导入,并没有texture;代码可能比较丑陋微笑可靠性完全没有考虑;并没有结合配置文件来实现所谓的“批量生产”...就当是一个初步研究成果吧,后续我会在项目中完善剩余的功能,编写完整的代码模块。也希望继续分享后续的Unity研究,欢迎各位开发者参与讨论!


    参考文献(部分文中也有):

    1. 飞行模拟大地形方案:http://forum.china.unity3d.com/forum.php?mod=viewthread&tid=117

    2. 方法2的原文链接:http://wiki.unity3d.com/index.php/TerrainImporter

    3. Unity Script API文档:https://docs.unity3d.com/ScriptReference/TerrainData.html

    4. MSDN:https://msdn.microsoft.com/zh-cn/library/system.io(v=vs.110).aspx




    展开全文
  • #分帧加载和分块加载 在我们实际做项目的时候,往往会遇见需要创建大量数据的时候,这时如果在一帧里面大量创建数据,那我们的游戏就会发生卡顿从而降低了用户的体验。 为了解决这种情况,可以使用使用分帧加载使得...

    #分帧加载和分块加载
    在我们实际做项目的时候,往往会遇见需要创建大量数据的时候,这时如果在一帧里面大量创建数据,那我们的游戏就会发生卡顿从而降低了用户的体验。
    为了解决这种情况,可以使用使用分帧加载使得每帧只加载固定数量的数据来解决,也可以使用分块加载来实现加载当前场景所需的数据。

    分帧加载

    我们得到的信息是来自于服务器的,当我们需要显示信息的时候,不可能从服务器拿了一条显示一条信息。我们应该定义一个数组先把所有的信息进行保存,通过遍历来显示信息。

    public class fpsload : MonoBehaviour
    {
        List<string> InfoList;
        int InfoListIndex = 0;
    
        void start(){
            StratCoroutine(GetFromInfoList() );
        }
        IEnumerator GetFromInfoList()
        {
            foreach(string item in InfoList)
            {
                InfoListIndex++;
                if(InfoListIndex % 5 == 0) //每一帧提取5条数据
                Debug.Log("Load");
                yield return null; //在下一帧执行
            }
        }
    }
    

    分块加载

    1. 第一种,划分场景
      当我们需要一次性加载许多地图信息的时候,无疑会发生卡顿,我们此时可以把场景分为多个部分定位好它们的位置信息进行分块的加载。

    假设设计一个场景Scene把它分为两个部分LoadScene1和LoadScene2。

    //假设当前离开场景LoadScene1
    public class blockload : MonoBehaviour
    {
        
        void start(){
        }
    
        IEnumerator LoadScene2()
        {
            //通过SceneManager.LoadSceneAsync实现分块加载不同场景,5.x以前为Application.LoadLevelAdditiveAsync
            AsyncOperation async = SceneManager.LoadSceneAsyn("LoadScene2", LoadSceneMode.Additive);
        }
    }
    
    1. 第二种,分割地图
      对地图进行分块加载,如果说场景里的地形非常大,加上里面的各种模型、贴图、碰撞、渲染等,这将是一笔很庞大的CPU、GPU和内存开销,所以我们需要对地形进行分割成多个地形块,再进行合理的加载卸载来达到性能开销上的平衡。

    首先分割地图的分块加载的核心思想是:将整个大地形,分割为 n x n 的正方形小块chunk(我们在接下来的内容里,把这些小块统称为chunk)。在这里呢,我们为了效率,就使用一个免费开源的分割地形的小工具Terrain Slicing,具体使用大致如下图:

    在这里插入图片描述

    具体的分块加载思路
    这里我们首先把地形分割成16x16的形式(具体你的地形多大,可根据地形具体大小分割成合理的数量),后续我们会用一个chunk对象去管理每一个chunk实体,用具有键值对的数据结构(例如字典)把chunk对象按照chunk所处的行列位置去保存起来(这里我们就以下图这种排列方式去保存,键为位置ChunkVector2,值为Chunk对象),这样方便我们后面对chunk对象的获取以及对chunk对象的操作(例如chunk对象中chunk实体的加载、卸载、缓存)。

    public class Chunk
    {
        /// <summary>
        /// 在块列表中所处的位置
        /// </summary>
        ChunkVector2 m_position;
    
        /// <summary>
        /// 块的实体
        /// </summary>
        GameObject m_body;
    
        /// <summary>
        /// 块的资源路径
        /// </summary>
        string m_resPath;
    
        /// <summary>
        /// 块当前的状态
        /// </summary>
        ChunkState m_currentState = ChunkState.UnLoad;
    
        /// <summary>
        /// 创建一个块对象
        /// </summary>
        /// <param name="rowNum">在块列表中的第几行</param>
        /// <param name="colNum">在块列表中的第几列</param>
    
        public Chunk(int rowNum, int colNum)
        {
            m_position = new ChunkVector2(rowNum, colNum); --块的位置
            m_resPath = string.Format("TerrainPrefab/Terrain_Slice_{0}_{1}", (rowNum + 1), (colNum + 1)); --块的路径
        }
        public Chunk(ChunkVector2 position) : this(position.rowNum, position.colNum)
        {
        }
    
        //函数声明,需补充定义内容
        public void Display(){}; --块显示
        public void Cache(){}; --块缓存
        public void Unload(){}; --块卸载
        
        /// <summary>
        /// 更新自身状态
        /// </summary>
        /// <param name="state"></param>
    
        public void Update(ChunkState state)
        {
            if (m_currentState == state) //块状态没变化
            {
                Debug.LogErrorFormat(" {0} is already {1} ", m_position, m_currentState);
                return;
            }
            switch (state) //根据状态进行处理
            {
                case ChunkState.Display:
                    Display();
                    break;
                case ChunkState.Cache:
                    Cache();
                    break;
                case ChunkState.UnLoad:
                    Unload();
                    break;
            }
        }
    
    }
    

    图片中绿色区域的9个chunk代表展示在场景中的地形,而玩家则处于区域中心位置为A的chunk,红色区域中的25个chunk代表缓存区域,这个区域会根据玩家的远离被卸载掉,也可能因玩家的靠近则呈现出来。
    在这里插入图片描述

    玩家从chunk A移动到chunk E时,红色区域标记为U区域的chunk被卸载(unload)、标记为S的chunk被展示(show),蓝色区域标记为L的chunk则被加载(load),绿色区域标记为H的chunk被隐藏(hide)。
    在这里插入图片描述

    首先根据玩家位置获取玩家所在chunk的位置,这里chunk的位置指的是在整个地图的第几行第几列。

        /// <summary>
        /// 获取块坐标
        /// </summary>
        /// <param name="position">玩家的具体vector3位置</param>
        /// <returns></returns>
    
        ChunkVector2 GetCurrentChunkVector(Vector3 position)
        {
            int col = (int)(position.x / m_chunkLength); --取整
            int row = (int)(position.z / m_chunkLength);
    
            return new ChunkVector2(row, col);
        }
    

    然后通过当前的chunk位置来获得周围其他的chunk,并把这些chunk加入列表,此时我们就获得了实际要操作但状态未更新的chunk列表。

        /// <summary>
        /// 获取实际块列表
        /// </summary>
        /// <param name="currentVector">当前中心块位置</param>
        /// <returns></returns>
    
        List<ChunkVector2> GetActualChunkList(ChunkVector2 currentVector)
        {
            List<ChunkVector2> expectChunkPosList = new List<ChunkVector2>(); //expec为实际要操作chunk列表
            //当前中心点的行列
            int currentRow = currentVector.rowNum;
            int currentCol = currentVector.colNum;
            
            //-2——>2,实际要操作区域,即绿红组成的区域
            for (int i = -2; i <= 2; i++) --
            {
                for (int j = -2; j <= 2; j++)
                {
                    int expRow = currentRow + i;
                    int expCol = currentCol + j;
                    if (expRow < 0 || expCol < 0 || expRow > m_row-1 || expCol > m_col-1) //判断地图边界
                        continue;
                    expectChunkPosList.Add(new ChunkVector2(expRow, expCol)); 
                }
            }
            return expectChunkPosList;
        }
    

    然后将实际的chunk列表与当前的chunk列表做对比并更新当前的chunk列表,在更新当前列表的过程中,则对相应的chunk做相应的处理,最终使得当前chunk列表与实际chunk列表保持一致。

        /// <summary>
        /// 对比当前块列表与实际块列表,并更新当前块列表
        /// </summary>
        /// <param name="actulChunkList">实际块列表</param>
        /// <param name="currentPos">当前中心块位置</param>
    
        private void UpdateCurrentChunkList(List<ChunkVector2> actulChunkList, ChunkVector2 currentPos) //实际列表和当前中心点
        {
            //遍历并更新当前块列表
            for (int i = 0; i < m_currentChunkList.Count; i++) 
            {
                ChunkVector2 pos = m_currentChunkList[i]; //当前遍历到的块的位置
                Chunk chunk = m_chunkMap[pos]; //获取当前块的位置的对象
    
                if (!actulChunkList.Contains(pos)) //实际块列表里若不存在当前列表的指定元素,则卸载删除当前块列表的这个块元素
                {
                    chunk.Unload(); //卸载当前块不存在于实际块列表的块
    
                    m_currentChunkList.RemoveAt(i);//移除当前块列表中不存在与实际块列表的块
    
                    i--; //在遍历列表时删除列表元素 记得索引-1 否则无法正确遍历
                }
                else //若当前块列表的块对象在实际块列表中存在,更新当前块对象的状态,并在实际块列表中移出实际块对象
                {
                    actulChunkList.Remove(pos); //移除实际块列表和当前块列表中相同的块元素,注:移除完毕后,实际块列表中的元素
    
                    ChunkState actualState = GetChunkStateByRelativePosition(pos, currentPos);  //先获取chunk的实际状态,GetChunkStateByRelativePosition()作用是获取实际状态,在后面定义
    
                    chunk.Update(actualState); //调用更新块方法
                }
            }
    
            //前面的循环已经处理完当前块与实际块的是否存在的所有联系,经过处理后实际块列表剩下的为新加入的块,接下来更新实际块列表的状态并插入到实际块列表中
            for (int i = 0; i < actulChunkList.Count; i++)
            {
                ChunkVector2 pos = actulChunkList[i];
                Chunk chunk = m_chunkMap[pos];
    
                //先获取实际块chunk的状态
                ChunkState actualState = GetChunkStateByRelativePosition(pos, currentPos);
                //根据状态进行对应更新操作
                chunk.Update(actualState);
    
                m_currentChunkList.Add(pos); //更新完以后,将当前块将与实际块保持一致
            } 
    
            //卸载所有没有被使用的资源
            Resources.UnloadUnusedAssets(); 
        }
    

    获得实际chunk的位置列表后,通过以当前chunk位置为参照位置,判读出周围chunk对象应该属于具体哪种状态。

        /// <summary>
        /// 获取指定块的相对状态
        /// </summary>
        /// <param name="specified">指定块坐标</param>
        /// <param name="relative">参照块坐标</param>
        /// <returns>相对块状态</returns>
    
        ChunkState GetChunkStateByRelativePosition(ChunkVector2 specified, ChunkVector2 relative)
        {
            //求出实际块相对参照块的行列距离
            int rowAmount = Mathf.Abs(specified.rowNum - relative.rowNum);
            int colAmount = Mathf.Abs(specified.colNum - relative.colNum);
            
            //更具距离进行状态更新
            if (rowAmount > 2 || colAmount > 2)
            {
                return ChunkState.UnLoad;
            }
            if (rowAmount == 2 || colAmount == 2)
            {
                return ChunkState.Cache;
            }
            if (rowAmount <= 1 || colAmount <= 1)
            {
                return ChunkState.Display;
            }
    
            return ChunkState.UnLoad; //如果不在处理距离内,说明距离参照块很远
        }
    

    至此就初步完成指定chunk的加载、卸载、缓存。

    参考:https://blog.csdn.net/weixin_33835690/article/details/87446398
    参考:https://blog.csdn.net/qq_30825027/article/details/89324097

    展开全文
  • Unity大地型分块加载的实现新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants...

    为什么地形要分块加载

    要谈到地形为什么要分块加载,很明显,是为了实现对性能上的提升,如果说场景里的地形非常大,加上里面的各种模型、贴图、碰撞、渲染等等 这将是一笔很庞大的cpu、gpu、内存开销,所以我们需要对地形进行分割成多个地形块,再进行合理的加载卸载来达到性能开销上的平衡。

    具体效果

    话不多说,我们先来上图,来看看具体的效果

    实际运行效果
    虽然看起来简陋一些,但是基本的加载地形功能是初步完成了。


    分割地形

    首先分块加载的核心思想是:将整个大地形,分割为 n x n 的正方形小块chunk(我们在接下来的内容里,把这些小块统称为chunk)。在这里呢,我们为了效率,就使用一个免费开源的分割地形的小工具叫做Terrain Slicing,如果大家有更多的需求,可以尝试对这个小工具的源码进行优化和扩展或者重写,这里具体如何用代码去分割地形就不再深究了,具体使用大致如下图:
    在这里插入图片描述


    具体的分块加载思路

    这里我们首先把地形分割成16x16的形式(具体你的地形多大,可根据地形具体大小分割成合理的数量),后续我们会用一个chunk对象去管理每一个chunk实体,用具有键值对的数据结构(例如字典)把chunk对象按照chunk所处的行列位置去保存起来(这里我们就以下图这种排列方式去保存,键为位置ChunkVector2,值为Chunk对象),这样方便我们后面对chunk对象的获取以及对chunk对象的操作(例如chunk对象中chunk实体的加载、卸载、缓存)。

    public class Chunk
    {
        /// <summary>
        /// 在块列表中所处的位置
        /// </summary>
        ChunkVector2 m_position;
    
        /// <summary>
        /// 块的实体
        /// </summary>
        GameObject m_body;
    
        /// <summary>
        /// 块的资源路径
        /// </summary>
        string m_resPath;
    
        /// <summary>
        /// 块当前的状态
        /// </summary>
        ChunkState m_currentState = ChunkState.UnLoad;
    
    
        /// <summary>
        /// 创建一个块对象
        /// </summary>
        /// <param name="rowNum">在块列表中的第几行</param>
        /// <param name="colNum">在块列表中的第几列</param>
        public Chunk(int rowNum, int colNum)
        {
            m_position = new ChunkVector2(rowNum, colNum);
            m_resPath = string.Format("TerrainPrefab/Terrain_Slice_{0}_{1}", (rowNum + 1), (colNum + 1));
        }
        public Chunk(ChunkVector2 position)
            : this(position.rowNum, position.colNum)
        {
        }
    
        public void Display() {};
        public void Cache(){};
        public void Unload(){};
        
        /// <summary>
        /// 更新自身状态
        /// </summary>
        /// <param name="state"></param>
        public void Update(ChunkState state)
        {
            if (m_currentState == state)
            {
                Debug.LogErrorFormat(" {0} is already {1} ", m_position, m_currentState);
                return;
            }
            switch (state)
            {
                case ChunkState.Display:
                    Display();
                    break;
                case ChunkState.Cache:
                    Cache();
                    break;
                case ChunkState.UnLoad:
                    Unload();
                    break;
    
            }
        }
    
    }
    

    在这里插入图片描述
    图片取自于https://blog.csdn.net/jxw167/article/details/81483685这里
    我的思路主要来源于这个图片,图片中天蓝色区域的9个chunk代表展示在场景中的地形,而玩家则处于区域中心位置为A的chunk,红色区域中的25个chunk代表缓存区域,这个区域会根据玩家的远离被卸载掉,也可能因玩家的靠近则呈现出来,就比如

    在这里插入图片描述
    从玩家从chunk A移动到chunk E的时候,红色区域标记为U区域的chunk被卸载(unload),蓝色区域标记为L区域的chunk则被加载(load),标记为H区域的chunk被隐藏(hide),标记为S区域的chunk被展示出来(show)


    首先根据玩家位置获取玩家所在chunk的位置,这里chunk的位置指的是在整个地图的第几行第几列

        /// <summary>
        /// 获取块坐标
        /// </summary>
        /// <param name="position">玩家的具体vector3位置</param>
        /// <returns></returns>
        ChunkVector2 GetCurrentChunkVector(Vector3 position)
        {
            int col = (int)(position.x / m_chunkLength);
            int row = (int)(position.z / m_chunkLength);
    
            return new ChunkVector2(row, col);
        }
    

    然后通过当前的chunk位置来获得周围其他的chunk,并把这些chunk加入列表,此时我们就获得了实际要操作的chunk列表。

        /// <summary>
        /// 获取实际块列表
        /// </summary>
        /// <param name="currentVector">当前中心块位置</param>
        /// <returns></returns>
        List<ChunkVector2> GetActualChunkList(ChunkVector2 currentVector)
        {
            List<ChunkVector2> expectChunkPosList = new List<ChunkVector2>();
            int currentRow = currentVector.rowNum;
            int currentCol = currentVector.colNum;
    
            for (int i = -2; i <= 2; i++)
            {
                for (int j = -2; j <= 2; j++)
                {
                    int expRow = currentRow + i;
                    int expCol = currentCol + j;
                    if (expRow < 0 || expCol < 0 || expRow > m_row-1 || expCol > m_col-1)
                        continue;
                    expectChunkPosList.Add(new ChunkVector2(expRow, expCol));
                }
            }
            return expectChunkPosList;
        }
    

    然后将实际的chunk列表与当前的chunk列表做对比并去更新当前的chunk列表,在更新当前块列表的过程中,则对相应的chunk做相应的处理,最终使得当前chunk列表与实际chunk列表保持一致

     
        /// <summary>
        /// 对比当前块列表与实际块列表,并更新当前块列表
        /// </summary>
        /// <param name="actulChunkList">实际块列表</param>
        /// <param name="currentPos">当前中心块位置</param>
        private void UpdateCurrentChunkList(List<ChunkVector2> actulChunkList, ChunkVector2 currentPos)
        {
            for (int i = 0; i < m_currentChunkList.Count; i++)
            {
                ChunkVector2 pos = m_currentChunkList[i];
                Chunk chunk = m_chunkMap[pos];
                if (!actulChunkList.Contains(pos))//实际块列表里若不存在当前列表的指定元素 则卸载删除
                {
                    chunk.Unload();//卸载不存在于实际块列表的块
    
                    m_currentChunkList.RemoveAt(i);//移除当前块列表中不存在与实际块列表的块
    
                    i--;//在遍历列表时删除列表元素 记得索引-1 否则无法正确遍历
                }
                else
                {
                    actulChunkList.Remove(pos);//实际块列表移除和当前块列表中相同的元素 注:移除完毕后,实际块列表中的元素
                    //先获取chunk的实际状态
                    ChunkState actualState = GetChunkStateByRelativePosition(pos, currentPos);
    
                    chunk.Update(actualState);
                    
                }
    
            }
    
            for (int i = 0; i < actulChunkList.Count; i++)
            {
                ChunkVector2 pos = actulChunkList[i];
                Chunk chunk = m_chunkMap[pos];
                //先获取chunk的实际状态
                ChunkState actualState = GetChunkStateByRelativePosition(pos, currentPos);
                //使用实际状态去更新当前状态
                chunk.Update(actualState);
    
                m_currentChunkList.Add(pos);//这里添加完以后,当前块列表将与实际块列表保持一致
    
            }
    
            Resources.UnloadUnusedAssets();
    
        }
    

    获得实际chunk的位置列表后,通过以当前chunk位置为参照位置,判读出周围chunk对象应该属于具体哪种状态

        /// <summary>
        /// 获取指定块的相对状态
        /// </summary>
        /// <param name="specified">指定块</param>
        /// <param name="relativeVector">参照块坐标</param>
        /// <returns>相对块状态</returns>
        ChunkState GetChunkStateByRelativePosition(ChunkVector2 specified, ChunkVector2 relative)
        {
            int rowAmount = Mathf.Abs(specified.rowNum - relative.rowNum);
            int colAmount = Mathf.Abs(specified.colNum - relative.colNum);
    
            if (rowAmount > 2 || colAmount > 2)
            {
                return ChunkState.UnLoad;
            }
            if (rowAmount == 2 || colAmount == 2)
            {
                return ChunkState.Cache;
            }
            if (rowAmount <= 1 || colAmount <= 1)
            {
                return ChunkState.Display;
            }
    
            return ChunkState.UnLoad;
        }
    

    至此就初步完成指定chunk的加载、卸载、缓存。

    首先这是我第一次写技术博客,也希望这篇文章可以给你带来一些收获,如果有哪里写的不好,欢迎有经验的朋友可以给出一些建议和分享。
    为了可以更好的让大家理解,我决定把我的代码分享给大家,
    https://github.com/jiajiadelequ/Large-Terrain

    展开全文
  • unity 无缝大地图 ... 【UWA】Unity手游开发札记—大世界动态加载实战 https://blog.uwa4d.com/archives/1919.html https://blog.uwa4d.com/archives/OpenDay1126_1.html   Uni...

    unity 无缝大地图

    http://blog.csdn.net/u014635337/article/details/45133733

    【UWA】Unity手游开发札记—大世界动态加载实战

    https://blog.uwa4d.com/archives/1919.html

    https://blog.uwa4d.com/archives/OpenDay1126_1.html

     

    Unity大地形分块加载

    https://zhuanlan.zhihu.com/p/26884671

    https://zhuanlan.zhihu.com/p/26936538

     

    Unity大地形分块加载(一)

    https://zhuanlan.zhihu.com/p/26884671

    https://zhuanlan.zhihu.com/p/26936538

     

    unity3d 大场景地形怎么做的啊

    https://blog.csdn.net/hyk745/article/details/39648735

    展开全文
  • 动画系统非常的灵活强大,动画系统支持动画融合、混合、叠加动画,行走循环的时间同步,...这里做个笔记文章,记录整个从0开始使用Unity的动画系统。下载官网的CharacterAnimation例子里面带有3个动画模型,以及这三个
  • unity的动画系统非常的灵活强大,动画系统支持动画融合、混合、叠加动画,行走循环的时间同步,动画层,控制动画的各个方面,以及支持基于物理的布娃娃系统和程序动画...unity3d教程手册 [/url] Unity3D教程:
  •  UnrealDevelopmentKit(虚幻引擎开发工具包),简称UDK,是一套强大并免费的游戏引擎开发套件,可以用来制作高端的3D游戏或者场景展示。不过,如果你要制作商业游戏,并且获得不错的收益,UDK官方会乐于找你分享...
  • Swift 语言指南

    2016-01-04 11:19:57
    Swift 语言指南 这份指南汇集了 Swift 语言主流学习资源,并以开发者的视角整理编排。 ...想了解关于该指南及 Swift 更多信息的同学,可以阅读短文《致 Swift ... 想快速找到优秀开源项目的开发者,可以访问我们
1
收藏数 8
精华内容 3
关键字:

unity3d 分段加载地形