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

分帧加载

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

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.LoadLevelAdditiveAsyncAsyncOperation 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 分帧加载和分块加载相关推荐

  1. 【笔记】unity大地图分块加载

    1.大地图分块加载     chunk的大小可动态调整     写工具做地块拆分         如果地块是由1个个小格子单元组成的,则可按位置进行划分保存成多个chunk预设         如果地 ...

  2. Unity可自定义loading页的异步加载工具,免费下载,使用说明

    本文是针对ZTools中异步加载工具的使用说明,包含实现过程以及如何使用  免费下载地址:请到[ https://gitee.com/jacobkay/unity-ZTools-LoadSceneAs ...

  3. Python图片转gif(将静态图转化为分块加载的动态图)

    简介 将静态图转化为分块加载的动态图 方案 1. PIL: 1. 创建背景图2. 将原图拆分成N块并依次合成到背景图的相应位置, 得到N张素材图3. 将N张素材图合成GIF2. pygifsicle对 ...

  4. 游戏制作之路-unity捕鱼达人(一 开始以及加载界面的制作)

    Unity捕鱼达人的制作 一.创建开始界面 根据上一篇我们所讲的,很容易就能把开始界面做出来. 二.游戏加载场景 游戏加载条的制作,我是用到了unity中的slider UI组件,如图 slider组 ...

  5. unity-大地图分块加载研究

    title: unity-大地图分块加载研究 categories: Unity3d tags: [unity, 大地图, 分块, lightmap] date: 2018-12-12 11:22:1 ...

  6. 信号处理(二)音频信号的分帧, 加窗

    1 .语音信号的三个参数 语音信号有三个重要的参数:声道数.取样频率和量化位数. 声道数:单声道或者双声道 采样频率:一秒钟对声音采样的次数,例如10000HZ代表一秒钟将信号分解为10000份,当采 ...

  7. cocos2dx 大地图分块加载的研究(初)

    本文转自http://blog.csdn.net/dinko321/article/details/46739563 项目里面需要加载一个很大的地图,目测最少是4096x4096的分辨率. 先不考虑什 ...

  8. 语音信号的分帧加窗的matlab实现

    [x,fs,nbits]=wavread('5_1.wav'); x1=enframe(x,200,100);%分帧 x2=enframe(x,hamming(200),100);%加窗 figure ...

  9. 数据分块加载——BigPipe 技术【类似facebook】

    一.原理 分块加载,加载完一块,就先把页面数据刷给用户,再加载下面的,直到加载完毕 二.基础需知: 三.服务端和php的相应配置 如果想实现分块加载[bigpipe技术],还需要对nginx.conf ...

最新文章

  1. 文件名转换为utf8 c语言,文件名编码转换:从 gb* 转向 utf8 必备工具 convmv
  2. Codeforces Round #599A~D题解
  3. java $和$$的区别_Java #{}和${}区别
  4. mysql用户的权限分配
  5. Redis Flushdb 命令
  6. 如何监控mysql主从之间的延迟
  7. mybatis加载xml配置文件
  8. matlab myupdatefcn,MATLAB笔记
  9. 堆叠顺序的误区和z-index
  10. 【CodeVs 6128 Lence的方块们】
  11. php教程 TTP中GET与POST的区别
  12. 纯小白如何拥有一个自己的服务器,并建立网站?
  13. css 字体样式设置大全
  14. IP RAN基站回传中的三大组网方案
  15. geek_2013年How-To Geek的节日礼物指南:男孩,女孩,极客和有情机器人的书籍
  16. Keso眼中的移动互联网罗生门
  17. 点击富文本部分文字跳转功能
  18. php人事考勤系统,基于PHPMySQL的考勤系统
  19. 2022完整版搜索引擎优化指南清单(你可以按步骤执行的SEO工作清单)
  20. JTA Transaction

热门文章

  1. 【Python数据处理】数据降维
  2. 区块链-浏览器-可视化
  3. Vue键盘事件keyup、keydown
  4. SpringDataMongoDB-3
  5. 川大计算机学硕奖学金,四川大学研究生奖助体系实施方案
  6. 2023 PS零基础入门教程
  7. 小陆的AE学习日记:C#中.distinct()方法无效问题
  8. QSPI Operation with parallel mode
  9. web游戏框架哪家强?国内外精选优质框架分析及注意事项
  10. 眉毛鼻子嘴巴等位置定位 以及相关的曲线 matlab