一 使用前提

1,需要使用资源热更新

2,使用Assetbundle资源热更(AssetBundle是产生webstream的元凶)

二 为什么要用AssetBundle

AssetBundle本质上就是一个压缩算法,只不过比起zip等一些压缩多了一些信息,比如平台信息(Ios,android),依赖信息等,既然是压缩,那就很好理解了,AssetBundle就是为了减少包体的大小的。Assets/Resources目录其实也是一样的

三 怎么生成AssetBundle

  1. using UnityEditor;
  2. using UnityEngine;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. public class PushAndPop
  6. {
  7. [MenuItem("Build/test")]
  8. static void Test()
  9. {
  10. string path1 ="Assets/Resources/UI1.prefab";
  11. List<string> deps1 = GetDepends(path1);
  12. Execute(path1, deps1);
  13.  
  14. string path2 = "Assets/Resources/UI2.prefab";
  15. List<string> deps2 = GetDepends(path2);
  16. Execute(path2, deps2);
  17.  
  18. }
  19. //获取path的依赖资源路径
  20. static List<string> GetDepends(string path)
  21. {
  22. List<string> deps = AssetDatabase.GetDependencies(new string[] { path }).ToList<string>();
  23. return deps;
  24. }
  25. //打包资源path成AssetBundle
  26. static void Execute(string path, List<string> deps)
  27. {
  28. string SavePath = Application.streamingAssetsPath + "/";
  29.  
  30. BuildAssetBundleOptions buildOp = BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets
  31. | BuildAssetBundleOptions.DeterministicAssetBundle;
  32.  
  33. //先打包依赖
  34. BuildPipeline.PushAssetDependencies();
  35. string bundleName = "";
  36. foreach (var dep in deps)
  37. {
  38. Debug.Log(dep);
  39. string ext = System.IO.Path.GetExtension(dep);
  40. if (ext == ".png" || ext==".shader" || ext==".FBX" || ext==".ttf")
  41. {
  42.  
  43. Object sharedAsset = AssetDatabase.LoadMainAssetAtPath(dep);
  44. bundleName = sharedAsset.name.Replace('/', '_') + ext.Replace('.','_');
  45. BuildPipeline.BuildAssetBundle(sharedAsset, null, SavePath + bundleName + ".assetbundle", buildOp, BuildTarget.StandaloneWindows);
  46. }
  47. }
  48.  
  49. //再打包主资源
  50. BuildPipeline.PushAssetDependencies();
  51. Object mainAsset = AssetDatabase.LoadMainAssetAtPath(path);
  52. bundleName = mainAsset.name.Replace('/', '_');
  53. BuildPipeline.BuildAssetBundle(mainAsset, null, SavePath + mainAsset.name + ".assetbundle", buildOp, BuildTarget.StandaloneWindows);
  54. BuildPipeline.PopAssetDependencies();
  55.  
  56. BuildPipeline.PopAssetDependencies();
  57. AssetDatabase.Refresh();
  58. }
  59.  
  60. }

实例代码把Resources目录下的UI1和UI2打成了AssetBundle,并把他们放到了StreamingAssets目录,如下

其中myAtlas_png和Unlit_Transparent Colored_shader是UI1和UI2的依赖资源

四 加载AssetBundle并且实例化资源

加载要先依赖再主资源

  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4.  
  5. public class AssetLoad : MonoBehaviour
  6. {
  7. void OnGUI()
  8. {
  9.  
  10. //依赖加载按钮
  11. if (GUI.Button(new Rect(0f, 30f, 100f, 20f), "Load Share Res"))
  12. {
  13. StartCoroutine(Load(@"file://" + Application.streamingAssetsPath + "/myAtlas_png.assetbundle"));
  14. StartCoroutine(Load(@"file://" + Application.streamingAssetsPath + "/Unlit_Transparent Colored_shader.assetbundle"));
  15. }
  16.  
  17. if (GUI.Button(new Rect(0f, 60f, 100f, 20f), "Load UI1"))
  18. {
  19. StartCoroutine(LoadAndInstantiate(@"file://"+Application.streamingAssetsPath + "/UI1.assetbundle"));
  20. }
  21.  
  22. if (GUI.Button(new Rect(0f, 90f, 100f, 20f), "Load UI2"))
  23. {
  24. StartCoroutine(LoadAndInstantiate(@"file://" + Application.streamingAssetsPath + "/UI2.assetbundle"));
  25. }
  26.  
  27. }
  28.  
  29. // 加载
  30. IEnumerator Load(string url)
  31. {
  32. WWW www = new WWW(url);
  33. yield return www;
  34. AssetBundle ab = www.assetBundle;
  35. //Object obj = ab.mainAsset;
  36. ab.LoadAll();
  37. Debug.Log("load" + url);
  38. }
  39.  
  40. // 加载并实例化
  41. IEnumerator LoadAndInstantiate(string url)
  42. {
  43. WWW www = new WWW(url);
  44. yield return www;
  45.  
  46. if (!System.String.IsNullOrEmpty(www.error))
  47. {
  48. Debug.Log(www.error);
  49. }
  50. else
  51. {
  52. Object main = www.assetBundle.mainAsset;
  53. GameObject.Instantiate(main);
  54. }
  55. }
  56.  
  57. }

实例用www从StreamingAssets目录加载资源,加载完成后

 五 AssetBundle的内存占用

我们先看看加进来的AssetBudle都使用了哪些内存,点击unity的菜单栏Window->Profiler,打开如下窗口

点击到Memory那一栏,在点击Simple旁边的下三角,选择Detailed

点击other,看到下面有一栏WebStream

这就是我们加进来的4个AssetBundle的占用的内存,后面带有各自占用内存

但是还没完,我们点开Assets这一栏

我们看到Texture2D下面有一张我们用到的图片,Shader下面也有

那么问题来了,为什么加进来的AssetBundle占有两处内存(WebStream和Asset),其实也很好理解,我们上面知道AssetBundle和压缩一样,这就好比我们在网站下载了一个zip的压缩文件,如果我们不解压是看不了里面的内容的,这就相当于WebStream下面的内存,Unity是识别不了的。这时候你用压缩软件把zip文件解压了,并且把文件解压在另外的目录,这个解压的操作和unity的ab.loadAll()操作是一样一样的,有木有,有木有,加载出来的Asset资源,Unity就能识别了。

六 AssetBundle内存释放

上面我们说过AssetBundle的内存占用分为WebStream和Asset,那么他们分别是怎么释放的,在什么时候释放,这是个问题

首先,我们释放WebStream的内存,调用ab.Unload(false)函数,可以释放AssetBundle的WebStream的内存

修改Load函数

  1. // 加载
  2. List<AssetBundle> ablist = new List<AssetBundle>();
  3. IEnumerator Load(string url)
  4. {
  5. WWW www = new WWW(url);
  6. yield return www;
  7. AssetBundle ab = www.assetBundle;
  8. //Object obj = ab.mainAsset;
  9. ab.LoadAll();
  10. //缓存ab
  11. ablist.Add(ab);
  12. Debug.Log("load" + url);
  13. }
  14.  
  15. // 加载并实例化
  16. IEnumerator LoadAndInstantiate(string url)
  17. {
  18. WWW www = new WWW(url);
  19. yield return www;
  20.  
  21. if (!System.String.IsNullOrEmpty(www.error))
  22. {
  23. Debug.Log(www.error);
  24. }
  25. else
  26. {
  27. Object main = www.assetBundle.mainAsset;
  28. GameObject.Instantiate(main);
  29. }
  30. //释放WebStream
  31. foreach(var ab in ablist)
  32. {
  33. if(ab != null)
  34. {
  35. ab.Unload(false);
  36. }
  37. }
  38. }

运行我们在看看

只剩下两个ui主资源的Webstream了,把他俩的ab加入释放列表,也是可以释放的。

unload(false)释放了Webstream的内存,我们再看看怎么释放Asset的内存,就是类似Texture2D下的图片内存等

释放Asset的内存使用Resources.UnloadAsset(obj) obj就是LoadAll加载出来的资源

我们再次修改我们的加载函数

  1. // 加载
  2. List<AssetBundle> ablist = new List<AssetBundle>();
  3. List<Object> objlist = new List<Object>();
  4. IEnumerator Load(string url)
  5. {
  6. WWW www = new WWW(url);
  7. yield return www;
  8. AssetBundle ab = www.assetBundle;
  9. //Object obj = ab.mainAsset;
  10. Object[] objs = ab.LoadAll();
  11. //缓存ab
  12. ablist.Add(ab);
  13. objlist.AddRange(objs.ToList<Object>());
  14. Debug.Log("load" + url);
  15. }

添加一个按钮作为释放的操作

  1. if(GUI.Button(new Rect(0f, 120f, 100f, 20f), "Unload"))
  2. {
  3. foreach (var obj in objlist)
  4. {
  5. Resources.UnloadAsset(obj);
  6. }
  7. }

当点击Unload的时候释放了png和shader的Asset内存,我们发现UI也不可见了,所以这个释放操作是在所有引用到这个依赖的GameObject都Destroy的时候调用的,不然会出现资源丢失的情况

 七 WebStream的释放时机


我们知道Webstream是用unlaod(false)释放的,但是我们发现,如果UI1和UI2都引用了png,当你实例化UI1后释放png的Webstream,在实例化UI2就不成功了,因为UI2依赖png,所以Webstream和Asset内存一样,也要在所有引用到这个依赖的GameObject都Destroy的时候才能释放。

最后修改的代码如下

  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. public class AssetLoad : MonoBehaviour
  6. {
  7.  
  8. void OnGUI()
  9. {
  10.  
  11. //依赖加载按钮
  12. if (GUI.Button(new Rect(0f, 30f, 100f, 20f), "Load Share Res"))
  13. {
  14. StartCoroutine(Load(@"file://" + Application.streamingAssetsPath + "/myAtlas_png.assetbundle"));
  15. StartCoroutine(Load(@"file://" + Application.streamingAssetsPath + "/Unlit_Transparent Colored_shader.assetbundle"));
  16. }
  17.  
  18. if (GUI.Button(new Rect(0f, 60f, 100f, 20f), "Load UI1"))
  19. {
  20. StartCoroutine(LoadAndInstantiate(@"file://"+Application.streamingAssetsPath + "/UI1.assetbundle"));
  21. }
  22.  
  23. if (GUI.Button(new Rect(0f, 90f, 100f, 20f), "Load UI2"))
  24. {
  25. StartCoroutine(LoadAndInstantiate(@"file://" + Application.streamingAssetsPath + "/UI2.assetbundle"));
  26. }
  27.  
  28. if (GUI.Button(new Rect(0f, 120f, 100f, 20f), "Detroy"))
  29. {
  30. GameObject go = GameObject.Find("UI1(Clone)");
  31. GameObject.Destroy(go);
  32. go = GameObject.Find("UI2(Clone)");
  33. GameObject.Destroy(go);
  34. }
  35.  
  36. if (GUI.Button(new Rect(0f, 150f, 100f, 20f), "Unload"))
  37. {
  38. //释放WebStream
  39. foreach (var ab in ablist)
  40. {
  41. if (ab != null)
  42. {
  43. ab.Unload(false);
  44. }
  45. }
  46. //释放Asset
  47. foreach (var obj in objlist)
  48. {
  49. Resources.UnloadAsset(obj);
  50. }
  51. }
  52. }
  53.  
  54. // 加载
  55. List<AssetBundle> ablist = new List<AssetBundle>();
  56. List<Object> objlist = new List<Object>();
  57. IEnumerator Load(string url)
  58. {
  59. WWW www = new WWW(url);
  60. yield return www;
  61. AssetBundle ab = www.assetBundle;
  62. //Object obj = ab.mainAsset;
  63. Object[] objs = ab.LoadAll();
  64. //缓存ab
  65. ablist.Add(ab);
  66. objlist.AddRange(objs.ToList<Object>());
  67. Debug.Log("load" + url);
  68. }
  69.  
  70. // 加载并实例化
  71. IEnumerator LoadAndInstantiate(string url)
  72. {
  73. WWW www = new WWW(url);
  74. yield return www;
  75.  
  76. if (!System.String.IsNullOrEmpty(www.error))
  77. {
  78. Debug.Log(www.error);
  79. }
  80. else
  81. {
  82. AssetBundle ab = www.assetBundle;
  83. ablist.Add(ab);
  84. Object main = ab.mainAsset;
  85. GameObject.Instantiate(main);
  86. }
  87.  
  88. }
  89.  
  90. }

八 游戏项目中的内存解决方案

一般在游戏中,游戏资源很多,依赖关系很更复杂,有可能一个png有很多个GameObject引用,如果不及时的释放没有引用的资源,游戏很可能因为内存不足变得卡顿,甚至闪退,

一个很好的办法就是关联主资源和所有实例化的GameObject,并且给主资源引用的依赖计数,每当有一个主资源引用到依赖,依赖引用计数就+1,每当实例化一个GameObject,主资源的计数+1,当删除一个GamObject的时候,主资源计数-1,当主资源计数为0,主资源依赖计数分别-1,如果依赖的计数为0,释放这个依赖的Webstream和Asset内存。

Unity资源内存管理--webstream控制的更多相关文章

  1. 从Profile中窥探Unity的内存管理

    刨根问底U3D---从Profile中窥探Unity的内存管理 这篇文章包含哪些内容 这篇文章从Unity的Profile组件入手,来探讨一下Unity在开发环境和正式环境中的内存使用发面的一些区别, ...

  2. 刨根问底U3D---从Profile中窥探Unity的内存管理

    这篇文章包含哪些内容 这篇文章从Unity的Profile组件入手,来探讨一下Unity在开发环境和正式环境中的内存使用发面的一些区别, 并且给出了最好控制内存的方法(我想你已经知道了...Prefa ...

  3. Unity游戏开发中的内存管理_资料

    内存是手游的硬伤——Unity游戏Mono内存管理及泄漏http://wetest.qq.com/lab/view/135.html 深入浅出再谈Unity内存泄漏http://wetest.qq.c ...

  4. 【转载】Unity 优雅地管理资源,减少占用内存,优化游戏

    转自:星辰的<Unity3D占用内存太大的解决方法> 最近网友通过网站搜索Unity3D在手机及其他平台下占用内存太大. 这里写下关于Unity3D对于内存的管理与优化. Unity3D  ...

  5. [转]全面理解Unity加载和内存管理

    [转]全面理解Unity加载和内存管理 最近一直在和这些内容纠缠,把心得和大家共享一下: Unity里有两种动态加载机制:一是Resources.Load,一是通过AssetBundle,其实两者本质 ...

  6. Unity 全面理解加载和内存管理

    最近一直在和这些内容纠缠,把心得和大家共享一下: Unity里有两种动态加载机制:一是Resources.Load,一是通过AssetBundle,其实两者本质上我理解没有什么区别.Resources ...

  7. Unity 3D中的内存管理

    本文欢迎转载,但烦请保留此行出处信息:http://www.onevcat.com/2012/11/memory-in-unity3d/ Unity3D在内存占用上一直被人诟病,特别是对于面向移动设备 ...

  8. Unity动态加载和内存管理(三合一)

    原址:http://game.ceeger.com/forum/read.php?tid=4394#info 最近一直在和这些内容纠缠,把心得和大家共享一下: Unity里有两种动态加载机制:一是Re ...

  9. 理解Unity加载和内存管理

    转自:http://game.ceeger.com/forum/read.php?tid=4394#info Unity里有两种动态加载机制:一是Resources.Load,一是通过AssetBun ...

随机推荐

  1. LintCode 1.A+B的问题

    LintCode 1.A+B的问题 描述 给出两个整数 a 和 b , 求他们的和. 答案 public class Solution { /** * @param a: An integer * @ ...

  2. 「面向打野编程」iOS多线程:CGD

    「面向打野编程」iOS多线程:CGD 前言 参考网络其他文章而写,渣水平,抛砖引玉. 虽然Concurrent意思为并发,但由于队列的实际效果,以下称为并行队列. 当前iPhone的CPU核心数远小于 ...

  3. npm配置国内镜像资源+淘宝镜像

    将npm的注册表源设置为国内的镜像 1.国内用户,建议将npm的注册表源设置为国内的镜像,可以大幅提升安装速度 2.国内优秀npm镜像推荐及使用:http://riny.net/2014/cnpm/ ...

  4. NetBus —— 让你的 App 内部随处感知网络的变化

    简介 NetBus 是一个实时监听网络状态的一个框架,接入简单.只需要几步就可以在 Activity 和 Fragment 以及其他要观测的类中很方便的收到网络状态变化的通知. 愿景 可以在 App ...

  5. spring配置JNDI(Java Naming and Directory Interface,Java命名和目录接口)数据源

    1.在tomcat下的server.xml的 <GlobalNamingResources> </GlobalNamingResources>添加下面代码 <Resour ...

  6. LeetCode--030--串联所有单词的字串(java)

    给定一个字符串 s 和一些长度相同的单词 words.找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置. 注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要 ...

  7. mysql5.7.25安装

    附:mysql安装包 链接:https://pan.baidu.com/s/1vROdBSw0GiMWCRpuwmqFCg 提取码:ug4o a.运行mysql-installer-community ...

  8. Goroutine通信与thread in java间的通信

    // This file contains the implementation of Go channels. // Invariants: //  At least one of c.sendq ...

  9. linux所有命令不能用显示-bash: ls: command not found

    所有的命令都显示找不到了,原因是修改了/etc/profile造成的 解决方法 1.修正属性文件中的错误 /usr/bin/vi /etc/profile 2.生效属性文件 source /etc/p ...

  10. Tensorflow选择性初始化图中的变量

    import tensorflow as tf def initialize_uninitialized(sess): global_vars = tf.global_variables() is_n ...