Bootstrap

Unity 3D 运行模式下 资源的加载与优化 之 Resources

运行模式和编辑模式是完全不同的,编辑模式下可以放很多很多资源,这些资源并不是全部打包到发布的游戏包中的。

打包时,Unity会自动过滤掉没有引用的资源,注意:Resources目录和StreamingAssets目录下的会都打包。

//--引用资源

只有被引用到的资源Unity才会打包,那么如何分辨资源是否被引用?

一张贴图被一个材质引用,这个材质被一个Cube引用,这个Cube被一个场景引用,这个场景被添加进了Scenes In Build中。

那么以上各个资源都会被打包进游戏包。

//--Resources

Resources文件夹是Unity中标志性的目录,这个目录下的资源无论引用与否,都会被强制打包在游戏包中。

Resources文件夹可以是顶层目录,也可以是某个文件夹的子目录,Unity会自动将它们合并在一起打包。

代码中动态读取这些资源并且加载它。

using UnityEngine;

public class MyResourcesLoadTest : MonoBehaviour
{
    private void Start()
    {
        Texture texture = Resources.Load<Texture>("Textures/unity");
        Debug.Log(texture.name);
    }
}

注意:由于Resources目录下的资源都会被打包,所以尽可能不要把不需要运行时加载的资源,或者已经废弃掉的资源放进去,因为这样会增大包体。此外,Resources下的资源尽量不要直接引用在场景中,不然这个资源会被场景和Resources打成两份。

//--删除对象

运行时,需要使用GameObject.Destroy()和GameObject.DestroyImmediate()方法删除游戏对象。

其中GameObject.Destroy()会等一帧再彻底删除。因为有可能在这一帧的后面还有地方在操作这个对象,所以建议使用它来删除对象。

GameObject.DestroyImmediate()表示立即删除,如果这句代码后面有地方操作这个删除的对象,立刻就会报错。

using UnityEngine;

public class MyDestroyTest : MonoBehaviour
{
    public GameObject go1;
    public GameObject go2;
    public GameObject go3;
    private void Start()
    {
        GameObject.Destroy(go1);
        //--延时一秒后删除
        GameObject.Destroy(go2, 1.0f);
        //--立即删除
        GameObject.DestroyImmediate(go3);
    }
}

//--删除资源

我们在场景中创建一个Cube,再创建一个材质,引用一张贴图。运行时,把Cube删除掉。

通过Profiler中依然可以看到这个贴图在内存中。

我们可以使用Resources.UnloadAsset()以及Resources.UnloadUnusedAssets()方法强制卸载资源。

资源卸载是异步操作,所以可以使用isDone来判断是否完成。

using UnityEngine;

public class MyDestroyTest : MonoBehaviour
{
    public GameObject go1;
    private AsyncOperation m_Operation;
    private void Start()
    {
        GameObject.Destroy(go1);
        m_Operation = Resources.UnloadUnusedAssets();
    }

    private void Update()
    {
        if (m_Operation != null)
        {
            if (m_Operation.isDone)
            {
                m_Operation = null;
                Debug.Log("资源卸载完成!");
            }
        }
    }
}

(如果有图集引用这个图的话,还是卸载不了。)

//--GC

在C#中,可能会有很多临时对象引用这个游戏资源,这很可能会导致Resources.UnloadUnusedAssets()无法释放掉。因此,在卸载无用资源前,需要保证C#完成垃圾收集工作,而且有时候进行一遍垃圾回收是没用的。最好调用两边GC()和Resources.UnloadUnusedAssets()。

using UnityEngine;
using UnityEngine.Events;

public class MyDestroyTest : MonoBehaviour
{
    public GameObject go1;
    private AsyncOperation m_Operation;
    private void Start()
    {
        GameObject.Destroy(go1);
        MyGC gc = GetComponent<MyGC>() ?? gameObject.AddComponent<MyGC>();
        gc.UnloadUnusedAssets(delegate ()
        {
            gc.UnloadUnusedAssets(delegate ()
            {
                DestroyImmediate(gc);
                Debug.Log("彻底卸载资源!");
            });
        });
    }

    public class MyGC : MonoBehaviour
    {
        public AsyncOperation m_Operation;
        public UnityAction m_Callback;

        public void UnloadUnusedAssets(UnityAction callback)
        {
            m_Callback = callback;
            m_Operation = Resources.UnloadUnusedAssets();
            System.GC.Collect();            
        }

        private void Update()
        {
            if (m_Operation != null)
            {
                if (m_Operation.isDone)
                {
                    m_Operation = null;
                    m_Callback();
                    //DestroyImmediate(this);
                }
            }
        }
    }


}

 

;