不管NGUI还是UGUI,图集都是在制作期间就生成了的,运行时是一张大图,这样做的好处在于我们可以在一定程度上去合并批次,但是图集通常在制作过程中,会分成commonatlas和系统atlas两类,一个界面prefab至少会用到两张图集,就会出现ABA的图集穿插打断合批的情况。还有一种游戏内容多了以后,各种图片也相应的变多,类似图标、commonatlas这种图集,一张2048x2048可能就放不下了,这时候如果用到两张2048x2048,就又出现了之前说的ABA的情况,而且内存上也上去了。这时候就出现了新的解决方案:动态图集。

动态图集其实就是我们在打包的时候,图片是零散的,但是最后运行时,自动生成一张空白大图片,然后将界面上用到的零散的图片绘制在这个大图上,只将这个大图传入到gpu里头,达到合批的效果。由于手机界面制作过程中,标准分辨率往往是低于2048的,所以一张2048的动态图集就能完全解决一个界面的绘制工作了,但是动态图集也是有缺点的,动态图集因为将图集的生成过程延迟到了游戏运行时,所以必然会比静态图集多了图集生成的成本,当然这也是可以优化的。并且在目前的动态图集生成方案中,还没有出现公开的支持压缩的动态图集解决方案,所以动态图集目前看来只能是RGBA32的格式。还有一点,静态图集由于图片在生成过程中是确定的,可以将分配算法做得很好,图集的利用率也能做到很高。动态图集由于图片是动态生成的,在游戏运行过程中也会动态的增减图片,类似操作系统的内存分配算法,图集必然会出现碎片,图集的利用率也不可能做得很高。

说了那么多 就做个demo来看看动态图集的威力吧。

这个demo只是简单的演示一下动态图集的主要思路,图片分配算法也只是将大图片分成128x128的一个一个分区,每个分区采用引用计数开控制是否在使用图片,用于维护整个UI系统的话,这种算法并不适用,但是如果只是用于icon图标的话,由于icon图标是固定尺寸的,所以这套算法就很合适了。下面上源码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class NxSpriteInfo
{private int _x;private int _y;private Sprite _sprite;private int _referenceCount;private int _width;private int _height;public int x { get { return _x; } }public int y { get { return _y; } }public Sprite sprite{get { return _sprite; }}public NxSpriteInfo(int x, int y, Texture2D mainTexture, int startX, int startY, int width, int height){_x = x;_y = y;_referenceCount = 0;_width = width;_height = height;_sprite = Sprite.Create(mainTexture, new Rect(startX, startY, width, height), Vector2.one / 2f);}public bool IsEmpty(){return _referenceCount == 0;}public void AddReference(){++_referenceCount;Debug.Log(string.Format("[AddReference]Sprite:[{0},{1}] ref:{2}", x, y, _referenceCount));}public void RemoveReference(){if (_referenceCount == 0) return;--_referenceCount;Debug.Log(string.Format("[RemoveReference]Sprite:[{0},{1}] ref:{2}", x, y, _referenceCount));}
}public class DynamicAtlas : MonoBehaviour
{private const int MAX_DYNAMIC_ATLAS_SIZE = 1024;private const int DYNAMIC_ATLAS_CELL_SIZE = 128;private const int DYNAMIC_ATLAS_CELL_COUNT = MAX_DYNAMIC_ATLAS_SIZE / DYNAMIC_ATLAS_CELL_SIZE;[SerializeField]private Texture2D _dynamicAtlasTex;// 策略 分成格子private List<NxSpriteInfo> _spriteCacheList;private Dictionary<int, int> _spriteRedirectMap = new Dictionary<int, int>();private void Awake(){_dynamicAtlasTex = new Texture2D(MAX_DYNAMIC_ATLAS_SIZE, MAX_DYNAMIC_ATLAS_SIZE, TextureFormat.RGBA32, false);_initCacheSprite();}private void _initCacheSprite(){int cellCount = DYNAMIC_ATLAS_CELL_COUNT;_spriteCacheList = new List<NxSpriteInfo>();for (int i = 0; i < cellCount; ++i){for (int j = 0; j < cellCount; ++j){_spriteCacheList.Add(new NxSpriteInfo(i, j, _dynamicAtlasTex,i * DYNAMIC_ATLAS_CELL_SIZE, j * DYNAMIC_ATLAS_CELL_SIZE,DYNAMIC_ATLAS_CELL_SIZE, DYNAMIC_ATLAS_CELL_SIZE));}}}public Sprite GetOrLoadSprite(Sprite sprite){// 拿缓存var spriteInstanceID = sprite.GetInstanceID();//Debug.Log(string.Format(" name: {0} instanceid: {1}", sprite.name, spriteInstanceID));int index = -1;if (_spriteRedirectMap.TryGetValue(spriteInstanceID, out index)){var newSprite = _spriteCacheList[index];newSprite.AddReference();return newSprite.sprite;}// 检查是不是本身就是动态生成的 如果是的话 什么都不用做for (int i = 0; i < _spriteCacheList.Count; ++i){var sp = _spriteCacheList[i];if (sp.sprite == sprite){return sprite;}}// 拿不到缓存就找个空格子新增var emptySprite = GetEmptySprite();if (emptySprite != null){// GPU上直接操作 速度快 兼容性差Graphics.CopyTexture(sprite.texture, 0, 0, (int)sprite.rect.x, (int)sprite.rect.y, (int)sprite.rect.width, (int)sprite.rect.height,_dynamicAtlasTex, 0, 0, (int)emptySprite.sprite.rect.x, (int)emptySprite.sprite.rect.y);// 这里要先删除上一个的index = GetIndex(emptySprite);foreach (var redirect in _spriteRedirectMap){if (redirect.Value == index){_spriteRedirectMap.Remove(redirect.Key);break;}}_spriteRedirectMap.Add(spriteInstanceID, GetIndex(emptySprite));emptySprite.AddReference();emptySprite.sprite.name = sprite.name + "(Dynamic)";return emptySprite.sprite;}// 找不到空格子就直接返回spritereturn sprite;}public void ReleaseSprite(Sprite sprite){for (int i = 0; i < _spriteCacheList.Count; ++i){var sp = _spriteCacheList[i];if (sp.sprite == sprite){sp.RemoveReference();break;}}}private NxSpriteInfo GetEmptySprite(){for (int i = 0; i < _spriteCacheList.Count; ++i){var sp = _spriteCacheList[i];if (sp.IsEmpty())return sp;}return null;}private int GetIndex(NxSpriteInfo sprite){return sprite.x * DYNAMIC_ATLAS_CELL_COUNT + sprite.y;}}

unity 动态图集相关推荐

  1. unity实现一个动态图集

    什么是Draw Call 在Unity中,每次引擎准备数据并通知GPU的过程称为一次Draw Call. 每次我们在在UI上显示一张图,就需要一次drawcall,这也是为啥要打图集的原因,为了减少d ...

  2. Unity UGUI图集专题

    一:图集介绍 什么是图集:我们可以将其理解为将一系列小图合并为一张大图.使用图集可以减少drawcall,提升效率. ​ 游戏中的图片模型最终是要给到显卡去渲染的,然后CPU通知GPU要开始渲染,这一 ...

  3. Unity一键图集生成工具,附源码 (基于NGUI和TexturePacker)

    https://blog.uwa4d.com/archives/NGUI_SplitChannels.html Unity一键图集生成工具,附源码 (基于NGUI和TexturePacker) 作者: ...

  4. unity动态修改标准材质自发光(Emission)

    目录 一.目的 1.想知道:unity动态修改标准材质自发光(Emission) 二.参考 1Unity利用材质自发光实现物体闪烁 三.操作:一:完成:变换材质自发光的数值 1.运行效果:材质变换了 ...

  5. Unity 打包图集

    Unity打包图集 public class MyTexturePak {private const string intPutPath = "/Emoji/Input/";pri ...

  6. Unity动态加载3D模型

    Unity动态加载3D模型 在Unity中创建游戏对象的方法有 3 种: 第一种是将物体模型资源由 Project 视图直接拖曳到 Hierarchy 面板中: 第二种是在 Unity 3D 菜单 G ...

  7. Unity 动态修改URP自带材质参数

    Unity 动态修改URP自带材质参数 前言 代码 Emission 参考连接 前言 修改此处默认参数 代码 Emission 启用自发光效果的代码是 material.EnableKeyword(& ...

  8. Unity 动态生成球体模型

    系列文章目录 Unity 动态生成球体模型 文章目录 系列文章目录 Unity 动态生成球体模型 前言 如何生成一个模型 球体模型的创建方法 计算正二十面体 正二十面体顶点 三角形连接顺序 前言 本篇 ...

  9. Unity动态字体在手机上出现字体丢失问题解决

    Unity动态字体在手机上出现字体丢失问题解决 参考文章: (1)Unity动态字体在手机上出现字体丢失问题解决 (2)https://www.cnblogs.com/bicker/p/3669176 ...

最新文章

  1. win10下硬盘安装CentOS7
  2. LA3989女士的选择
  3. 《Linux内核分析》实践4
  4. 七、【SAP-PM模块】信息系统 报表分析
  5. 关于ASP.NET未能映射路径问题
  6. 0811-按钮操作(加法计算器)(拖控件找控件代码属性名称)(frame center bounds)(上下左右移动button图片)...
  7. 跑来跑去:假人与AWS Lambda的第一次接触
  8. dlna和miracast可以共存吗_解决播放网络视频卡顿及DLNA和Miracast容易掉线的方法
  9. 图解深度学习-梯度下降法优化器可视化(SGD, Momentum,Adam, Adagrad and RMSProp)
  10. 另类的切图仔画图方案:svg编辑器+css
  11. 【uni-app】 Android 和IOS打开淘宝优惠券实现
  12. 卸载wps后,win10的office所有图标变白最简单方法
  13. TabLayout 的使用 更改下划线的长度,和一个奇葩的问题
  14. 编程比赛项目和时间汇总
  15. 《三体》刘慈欣:意识上传离现实还存在很大技术障碍
  16. 免越狱中控你们知道多少呢?
  17. 深入学习React函数组件性能优化三剑客useMemo、useCallback、memo
  18. Linux文件存储(1)什么是格式化
  19. python爬虫崔庆才_崔庆才老师爬虫原理讲解笔记
  20. Windows无法启动MySQL80服务(位于本地计算机)

热门文章

  1. 第一篇图像处理论文审稿意见修改说明
  2. 酷炫浪漫表白页面(附代码)HTML5代码类资源
  3. STM32 UART DMA实现未知数据长度接收(转自amoBBs)
  4. python+OpenCv笔记(八):图像噪声(椒盐噪声、高斯噪声)
  5. Alfresco 4.0安装手把手教程
  6. Maven之Nexus局域网私服的搭建以及上传下载的测试
  7. maven私服搭建,sonatype nexus
  8. 发布订阅模式vs观察者模式
  9. 解决win10使用Fiddler4无法手机抓包的问题(真正的大招!)
  10. 再来30个!中国联通5G应用创新案例