Unity Performance Optimization - Dynamic Atlas

1. What is a dynamic atlas:

Unity Motion Atlas is a technology used in the Unity engine to handle game texture optimization. It can pack multiple textures into one atlas, reducing the number of textures that need to be loaded in the game, thereby improving game performance.

At runtime, Unity Dynamic Atlas will dynamically generate textures according to the needs of the game and pack them into an atlas, which can greatly reduce game loading time and memory usage.

Second, the advantages and disadvantages of dynamic atlas:

 Benefits of Unity Motion Atlases include:

  1. Reduce texture loading time: Using a dynamic atlas can pack multiple textures into one atlas, and only one texture needs to be loaded at runtime, thereby reducing loading time and memory usage.
  2. Improve game performance: Reducing texture loading time and memory usage can improve game performance and make the game more stable on different platforms.
  3. Simplified development: Unity Dynamic Atlas can automatically manage the packaging and loading of textures, simplifying the development process and reducing the need for manual operations.

 Disadvantages of Unity Motion Atlases include:

  1. Generating an atlas requires certain computing resources: Certain computing resources are required to generate a dynamic atlas. If there are many textures in the game, it may cause the game to freeze or run slowly.
  2. Need to adjust the texture size: When using the dynamic atlas, you need to adjust the texture of different sizes, otherwise it may cause texture distortion or deformation.
  3. Certain configuration and debugging are required: Using the dynamic atlas requires certain configuration and debugging, and the textures of the game need to be classified and packaged so that the dynamic atlas can be generated at runtime.

3. Implementation scheme of dynamic atlas:

This article does not explain more about the principle of dynamic atlas, and directly presents a simple demo to let everyone understand dynamic atlas.

The following is the management class of dynamic atlas:

using System.Collections.Generic;
using UnityEngine;

public class DynamicAtlasManager : MonoBehaviour
{
    public int atlasSize = 2048;
    public TextureFormat textureFormat = TextureFormat.RGBA32;
    public bool useMipmaps = false;

    private static DynamicAtlasManager _instance;
    public static DynamicAtlasManager Instance
    {
        get
        {
            if (_instance == null)
            {
                GameObject go = new GameObject("DynamicAtlasManager");
                _instance = go.AddComponent<DynamicAtlasManager>();
            }

            return _instance;
        }
    }

    private Dictionary<string, Texture2D> _atlasDictionary;
    private Dictionary<string, Rect> _spriteRects;
    private Dictionary<string, Sprite> _originalSpritesCache;

    void Awake()
    {
        _atlasDictionary = new Dictionary<string, Texture2D>();
        _spriteRects = new Dictionary<string, Rect>();
        _originalSpritesCache = new Dictionary<string, Sprite>();
    }

    public void AddSpritesToDynamicAtlas(string atlasName, Sprite[] sprites)
    {
        if (sprites == null || sprites.Length == 0) return;

        Texture2D atlas;
        if (_atlasDictionary.ContainsKey(atlasName))
        {
            atlas = _atlasDictionary[atlasName];
        }
        else
        {
            atlas = new Texture2D(atlasSize, atlasSize, textureFormat, useMipmaps);
            atlas.filterMode = FilterMode.Bilinear;
            _atlasDictionary.Add(atlasName, atlas);
        }

        for (int i = 0; i < sprites.Length; i++)
        {
            if (!_originalSpritesCache.ContainsKey(sprites[i].name))
            {
                _originalSpritesCache.Add(sprites[i].name, sprites[i]);
            }
        }

        int xOffset = 0;
        int yOffset = 0;
        int maxHeight = 0;

        for (int i = 0; i < sprites.Length; i++)
        {
            Sprite sprite = sprites[i];
            Texture2D spriteTexture = sprite.texture;

            if (xOffset + sprite.rect.width > atlas.width)
            {
                xOffset = 0;
                yOffset += maxHeight;
                maxHeight = 0;
            }

            // Copy the texture using CopyTexture method
            Graphics.CopyTexture(spriteTexture, 0, 0, (int)sprite.rect.x, (int)sprite.rect.y, (int)sprite.rect.width, (int)sprite.rect.height, atlas, 0, 0, xOffset, yOffset);

            _spriteRects[sprite.name] = new Rect(xOffset, yOffset, sprite.rect.width, sprite.rect.height);

            xOffset += (int)sprite.rect.width;
            maxHeight = Mathf.Max(maxHeight, (int)sprite.rect.height);
        }
    }


    public Sprite GetSpriteFromDynamicAtlas(string atlasName, string spriteName)
    {
        if (!_atlasDictionary.ContainsKey(atlasName) || !_spriteRects.ContainsKey(spriteName))
        {
            return null;
        }

        Texture2D atlas = _atlasDictionary[atlasName];
        Rect spriteRect = _spriteRects[spriteName];

        // Get the original sprite
        if (!_originalSpritesCache.ContainsKey(spriteName))
        {
            return null;
        }

        Sprite originalSprite = _originalSpritesCache[spriteName];

        // Calculate the border of the new sprite based on the original sprite's border
        Vector4 border = originalSprite.border;

        // Create the new sprite with the correct border
        return Sprite.Create(atlas, spriteRect, new Vector2(0.5f, 0.5f), originalSprite.pixelsPerUnit, 0, SpriteMeshType.Tight, border);
    }
}

 The following is the demo code of the dynamic atlas:

using UnityEngine;
using UnityEngine.UI;

public class DynamicAtlasDemo : MonoBehaviour
{
    public Sprite sprite1;
    public Sprite sprite2;
    public Sprite sprite3;
    public Image image1;
    public Image image2;
    public Image image3;

    private DynamicAtlasManager _dynamicAtlasManager;

    void Start()
    {
        _dynamicAtlasManager = DynamicAtlasManager.Instance;

        // Add sprites to the dynamic atlas
        _dynamicAtlasManager.AddSpritesToDynamicAtlas("DemoAtlas", new Sprite[] { sprite1, sprite2, sprite3 });

        image1.sprite = _dynamicAtlasManager.GetSpriteFromDynamicAtlas("DemoAtlas", sprite1.name);
        image2.sprite = _dynamicAtlasManager.GetSpriteFromDynamicAtlas("DemoAtlas", sprite2.name);
        image3.sprite = _dynamicAtlasManager.GetSpriteFromDynamicAtlas("DemoAtlas", sprite3.name);
    }
}

Number of draw calls before optimization: 5

Optimized number of draw calls: 3

 

 

Remark:

The dynamic atlas above is just a simple solution that can be expanded and optimized according to project needs.

Everyone is welcome to like, comment and pay attention to Sanlian, and the performance optimization is continuously updated.

Guess you like

Origin blog.csdn.net/qq_33808037/article/details/130018125