【Unity3D】关于unity中spriteAtlas的打包方案及使用时内存状态的多组对照实验

精灵图集(Sprite Atlas)简介

【Unity Manual】

unity项目中使用精灵(Sprite)和其他图形来创建其场景的视觉效果。这意味着单个项目中可能包含许多纹理(texture) 文件。Unity 通常会为场景中的每个纹理发出一个绘制调用(DrawCall);但是,在具有许多纹理的项目中,过多的绘制调用会占用大量资源,并会对项目的性能产生负面影响。

为了降低性能消耗,我们可以使用精灵图集(Sprite Atlas)技术,它能够将多个纹理合并成一个大纹理,当访问图集中的多个纹理时,也只需要调用一次DrawCall。

精灵图集的使用

关于Sprite Atlas 的使用可以参考–这里

在查找相关资料时由知乎文章:【Unity游戏开发】SpriteAtlas与AssetBundle最佳食用方案 得知不同的打包方案,即:

  • 对照组A&B,将散图文件夹作为一个AssetBundle包。
  • 对照组C&D,将Sprite Atlas文件作为一个AssetBundle包。

对照组实验

以下所有结论均在Unity 2021.3.11下得出

在这里插入图片描述
文件结构
在这里插入图片描述
两个AssetBundle包。
在这里插入图片描述
其中testImage使用了一个图集中的图元

在这里插入图片描述

  • A组:
    勾选Include in build
    打包散图文件夹,不打包SpriteAtlas
    使用方式:

    	_bundle2 = AssetBundle.LoadFromFile(Application.streamingAssetsPath + @"\testatlas.u3d");
        img1.sprite = _bundle2.LoadAsset<Sprite>("gameS-num1");
        img2.sprite = _bundle2.LoadAsset<Sprite>("gameS-num2");
    

    需要先加载atlas包,再使用prefab,否则build后 prefab无法正常显示纹理(editor下正常)

    结论:
    1. 使用两张散图,DrawCall仍记为1;
    2. AB包中除了所有散图外,仍会有一张合并后的大图;
    下图由AssetStudio分别读取AB包中的内容
    testatlas.u3d:
    在这里插入图片描述
    testimage.u3d:
    在这里插入图片描述
    3. 改变图元调用的次数后

        _bundle2 = AssetBundle.LoadFromFile(Application.streamingAssetsPath + @"\testatlas.u3d");
        img1.sprite = _bundle2.LoadAsset<Sprite>("gameS-num1");
        img2.sprite = _bundle2.LoadAsset<Sprite>("gameS-num1");
        img3.sprite = _bundle2.LoadAsset<Sprite>("gameS-num1");
    

    MemoryProfiler检查内存可知,所有关于图元 "gameS-num1"的引用在内存中有且仅有一份,也就是说,以上方式加载的图元,以单例形式存在于内存之中,不同于C组结论3 . 在这里插入图片描述- B组: 不勾选Include in build
    打包散图文件夹,不打包SpriteAtlas
    使用方式:

    	_bundle2 = AssetBundle.LoadFromFile(Application.streamingAssetsPath + @"\testatlas.u3d");
        // img1.sprite = _bundle2.LoadAsset<Sprite>("gameS-num1");
        // img2.sprite = _bundle2.LoadAsset<Sprite>("gameS-num2");
    

    结论:
    无法加载图片,加载testatlas.u3d AB时调用 SpriteAtlasManager.atlasRequested ;
    AB包中只有散图
    加载prefab时不会调用 SpriteAtlasManager.atlasRequested (与D组差异)
    prefab无法正常显示;
    在这里插入图片描述
    在这里插入图片描述

  • C组:
    勾选Include in build
    不打包散图文件夹,打包SpriteAtlas
    使用方式:

    	_bundle2 = AssetBundle.LoadFromFile(Application.streamingAssetsPath + @"\testatlas.u3d");
        atlas = _bundle2.LoadAsset<SpriteAtlas>("testAtlas");
        img1.sprite = atlas.GetSprite("gameS-num1");
        img2.sprite = atlas.GetSprite("gameS-num2");
    

    结论:
    1. 使用SpriteAtlas,调用多个图元,DrawCall记为1
    2. 由AssetStudio分别读取AB包中的内容
    testatlas.u3d:
    在这里插入图片描述
    testimage.u3d:
    在这里插入图片描述
    由此可见,AB包中除了所有散图外,会有一张合并后的大图;但是,prefab中也出现一份相同的图元,打包冗余

    1. 改变图元调用的次数后
        _bundle2 = AssetBundle.LoadFromFile(Application.streamingAssetsPath + @"\testatlas.u3d");
        atlas = _bundle2.LoadAsset<SpriteAtlas>("testAtlas");
        img1.sprite = atlas.GetSprite("gameS-num1");
        img2.sprite = atlas.GetSprite("gameS-num1");
        img3.sprite = atlas.GetSprite("gameS-num1");
    

    MemoryProfiler检查内存可知,所有关于图元 “gameS-num1"的引用都会clone出独立的一份,相对比A组造成大量的内存开销。同时由于prefab中也有一张图元,在加载AB包时,多次载入同一份图元"gameS-num1”。

在这里插入图片描述

  • D组:
    不勾选Include in build
    不打包散图文件夹,打包SpriteAtlas
    使用方式:

    	_bundle2 = AssetBundle.LoadFromFile(Application.streamingAssetsPath + @"\testatlas.u3d");
        atlas = _bundle2.LoadAsset<SpriteAtlas>("testAtlas");
        img1.sprite = atlas.GetSprite("gameS-num1");
        img2.sprite = atlas.GetSprite("gameS-num2");
    

    结论:
    prefab中出现一份散图,打包冗余
    AB包中除了所有散图外,仍会有一张合并后的大图;
    加载prefab时调用 SpriteAtlasManager.atlasRequested ;
    prefab无法正常显示
    在这里插入图片描述
    在这里插入图片描述

  • E组
    勾选Include in build
    将散图文件夹,SpriteAtlas打入同一个AB包中

  • F组
    不勾选Include in build
    将散图文件夹,SpriteAtlas打入同一个AB包中

实验对照表

打包方式是否勾选Include in build纹理合并是否成功是否冗余1先加载图元是否需要后绑定2先加载prefab是否需要后绑定2内存中图元的唯一性atlas.GetSprite(“xxx”)内存中图元的唯一性_bundle2.LoadAsset(“gameS-num1”);
对文件夹打包不可用
对文件夹打包(无意义)不可用不可用
对spriteAtlas文件打包不可用
对spriteAtlas文件打包不可用
将文件夹和spriteAtlas打入同一个包
将文件夹和spriteAtlas打入同一个包

总结

综上所诉,在使用SpriteAtlas时,根据不同的情况使用不同的打包方案。需要特备注重的是否冗余,是否需要精灵在内存唯一。

  1. 正确的打包方式应该是将图集和图元文件打入同一个AB包,在包体大小不变的情况下,还能使用不同的加载图元方式。一举多得 。
  2. 当预制体使用某一个图集中的图元时,建议不勾选include in build ,此时可以根据prefab需要的纹理加载对应的图集包。
  3. 当图集仅作为动态加载某些图元时,建议勾选include in build

完整测试用代码

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.U2D;
using UnityEngine.UI;

public class AtlasTest : MonoBehaviour
{
    public Image img0;
    public Image img1;
    public Image img2;
    public Image img3;
    private SpriteAtlas atlas;
    private AssetBundle _bundle;
    private GameObject _testImage;
    private AssetBundle _bundle2;

    // Start is called before the first frame update
    void Start()
    {
        SpriteAtlasManager.atlasRequested += OnCallBack_AtlasRequested;
        SpriteAtlasManager.atlasRegistered += OnCallBack_AtlasRegistered;
    }

    private void OnCallBack_AtlasRegistered(SpriteAtlas obj)
    {
        Debug.Log("atlasRegistered: " + obj.name);
        //img1.sprite = atlas.GetSprite("gameS-num1");
        //img2.sprite = atlas.GetSprite("gameS-num2");
    }

    private void OnCallBack_AtlasRequested(string arg1, Action<SpriteAtlas> arg2)
    {
        Debug.Log("无法在运行时找到图集资源:"+ arg1);

        //_bundle2 = AssetBundle.LoadFromFile(Application.streamingAssetsPath + @"\testatlas.u3d");
        atlas = _bundle2.LoadAsset<SpriteAtlas>("testAtlas");
        arg2.Invoke(atlas);
    }

    public void OnClick_Start()
    {
        _bundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath + @"\testimage.u3d");
        var testImage = _bundle.LoadAsset<GameObject>("testImage");
        _testImage=GameObject.Instantiate(testImage, img1.transform.parent);
    }
    public void OnClick_Start2()
    {
        _bundle2 = AssetBundle.LoadFromFile(Application.streamingAssetsPath + @"\testatlas.u3d");
        atlas = _bundle2.LoadAsset<SpriteAtlas>("testAtlas");
        img1.sprite = atlas.GetSprite("gameS-num1");
        img2.sprite = atlas.GetSprite("gameS-num1");
        img3.sprite = atlas.GetSprite("gameS-num1");
    }
    public void OnClick_Start3()
    {
        _bundle2 = AssetBundle.LoadFromFile(Application.streamingAssetsPath + @"\testatlas.u3d");
        img1.sprite = _bundle2.LoadAsset<Sprite>("gameS-num1");
        img2.sprite = _bundle2.LoadAsset<Sprite>("gameS-num1");
        img3.sprite = _bundle2.LoadAsset<Sprite>("gameS-num1");
    }
    public void OnClick_Unload()
    {
        //GameObject.Destroy(_testImage);
        //img1.sprite = null;
        //img2.sprite = null;

        _bundle?.Unload(true);
        _bundle2?.Unload(true);
    }


    // Update is called once per frame
    void Update()
    {
    }
}


打包用

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class ExportAssetBundles : MonoBehaviour
{
    [MenuItem("Example/Build Asset Bundles")]
    static void BuildABs()
    {
        // Put the bundles in a folder called "ABs" within the Assets folder.
        BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

  1. 其他引用图集中图元的prefabAB包中是否有多的图元。 ↩︎

  2. 是否调用了SpriteAtlasManager.atlasRequested 和 atlasRegistered。 ↩︎ ↩︎

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值