Unity Memory Optimization Tips

        Memory management is a very important aspect when it comes to the development and optimization of Unity games. Properly managing and optimizing memory can significantly improve game performance and reduce resource consumption. In this blog, we'll explore some of Unity's memory optimization techniques, with code implementations and comments to help you better understand the concepts.

1. Use object pool

        In game development, frequently creating and destroying objects can incur the overhead of memory allocation and garbage collection. To avoid this, we can use object pools to reuse objects that have already been created, thereby reducing the number of memory allocations and garbage collections.

The following is a sample code of a simple object pool implementation. Let's take the bullet object in the game as an example:

public class BulletPool : MonoBehaviour
{
    public GameObject bulletPrefab; // 子弹预制体
    public int poolSize = 10; // 对象池大小

    private List<GameObject> bullets; // 存储子弹对象的列表

    private void Start()
    {
        bullets = new List<GameObject>(); // 初始化子弹列表

        // 创建一定数量的子弹对象并添加到对象池
        for (int i = 0; i < poolSize; i++)
        {
            GameObject bullet = Instantiate(bulletPrefab); // 实例化子弹对象
            bullet.SetActive(false); // 将子弹对象禁用
            bullets.Add(bullet); // 将子弹对象添加到列表中
        }
    }

    public GameObject GetBullet()
    {
        // 查找是否有已经被禁用的子弹对象,如果有,则将其激活并返回
        foreach (GameObject bullet in bullets)
        {
            if (!bullet.activeInHierarchy)
            {
                bullet.SetActive(true);
                return bullet;
            }
        }

        // 如果没有可用的子弹对象,则创建一个新的子弹对象并添加到对象池中
        GameObject newBullet = Instantiate(bulletPrefab);
        bullets.Add(newBullet);
        return newBullet;
    }
}

        In this example, we create a certain number of bullet objects in the Start method and add them to the object pool. When we need to get the bullet, we first check whether there is a bullet object that has been disabled, and if so, activate it and return it. If there is no bullet object available, we create a new bullet object and add it to the object pool.

        The advantage of using the object pool is that when the bullet is no longer used, we only need to disable it and keep it in the object pool instead of destroying it. This reduces memory allocation and garbage collection overhead and improves game performance.

2. Lazy loading of resources

        In a game, not all assets need to be loaded initially. Certain resources, such as level maps, audio, and textures, can be lazy-loaded when needed, reducing memory consumption at game startup.

Here is a sample code for a simple lazy loading resource:

public class LevelManager : MonoBehaviour
{
    private bool isLevelLoaded = false; // 关卡是否已加载
    private GameObject levelMap; // 关卡地图对象

    private void Update()
    {
        if (!isLevelLoaded && Input.GetKeyDown(KeyCode.Space))
        {
            LoadLevel();
        }
    }

    private void LoadLevel()
    {
        levelMap = Instantiate(Resources.Load<GameObject>("LevelMap")); // 延迟加载关卡地图资源
        isLevelLoaded = true;
    }
}

        In this example, we detect that the player presses the space bar in the Update method before loading the level map. Use the Resources.Load method to load resources based on their path. By lazy loading resources, we can avoid loading a lot of resources at once when the game starts, thereby reducing memory consumption.

3. Clean up unused resources

        During the running of the game, some unused resources may be generated, such as destroyed game objects, unused textures, etc. In order to release these resources in time, we can manually clean up.

Here is a simple sample code to clean up unused resources:

public class ResourceManager : MonoBehaviour
{
    private List<GameObject> unusedObjects; // 存储未使用的游戏对象
    private List<Texture2D> unusedTextures; // 存储未使用的纹理

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.C))
        {
            ClearUnusedResources();
        }
    }

    private void ClearUnusedResources()
    {
        unusedObjects = new List<GameObject>(FindObjectsOfType<GameObject>()); // 获取场景中所有的游戏对象
        unusedTextures = new List<Texture2D>(Resources.FindObjectsOfTypeAll<Texture2D>()); // 获取所有的纹理资源

        foreach (GameObject obj in unusedObjects)
        {
            if (obj == null)
            {
                unusedObjects.Remove(obj);
                Destroy(obj); // 销毁无效的游戏对象
            }
        }

        foreach (Texture2D tex in unusedTextures)
        {
            if (tex != null && tex.name.Contains("Unused"))
            {
                unusedTextures.Remove(tex);
                Resources.UnloadAsset(tex); // 卸载未使用的纹理资源
            }
        }
    }
}

        In this example, we detect that the player presses the "C" key in the Update method, and clean up unused game objects and texture resources. By traversing the list of game objects and textures, we can determine which resources are no longer used and perform corresponding cleanup operations.

It should be noted that when cleaning up resources, we need to be careful to ensure that the resources in use will not be deleted by mistake.

        By using object pools, lazy loading resources, and cleaning up resources that are no longer used, we can effectively optimize the memory management of Unity games, improve game performance and reduce resource consumption. The above are some simple sample codes, hoping to help you better understand and practice these technologies.

Guess you like

Origin blog.csdn.net/Asklyw/article/details/131000361