You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中多线程优化大网格瓦片生成卡顿问题求助

Unity瓦片网格生成卡顿优化方案

原脚本核心问题

  1. 循环内重复调用Resources.Load,无缓存机制,每次加载都产生IO耗时
  2. 嵌套循环一次性实例化所有瓦片,单帧执行大量Unity API操作(Instantiate、Transform.position赋值),直接阻塞主线程
  3. 协程未实现真正的分批处理,构造函数同步执行所有生成逻辑

优化步骤

1. 预加载资源并缓存

提前在主线程加载所有需要的瓦片预制体,彻底避免循环内重复加载:

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

public class GridGenerationSprite : MonoBehaviour
{
    private Sprite mySprite;
    private SpriteRenderer sr;

    [SerializeField] SettingGeneration settingGeneration;
    [SerializeField] float tileSize;
    [SerializeField] int size;

    private float xDir;
    private float yDir;
    private byte[,] cells;
    // 新增资源缓存字典
    private Dictionary<byte, GameObject> tilePrefabCache = new Dictionary<byte, GameObject>();

    private void Awake()
    {
        // 预加载所有可能用到的瓦片资源
        PreloadTilePrefabs();
    }

    private void PreloadTilePrefabs()
    {
        // 这里根据你的SettingGeneration逻辑,获取所有可能的瓦片类型标识
        // 示例:假设瓦片类型是0-9的byte值,可根据实际情况调整
        for (byte i = 0; i < 10; i++)
        {
            if (!tilePrefabCache.ContainsKey(i))
            {
                var prefab = Resources.Load<GameObject>(i.ToString());
                if (prefab != null)
                {
                    tilePrefabCache.Add(i, prefab);
                }
            }
        }
    }

2. 协程分批实例化

将大循环拆分为小批次,每帧处理部分瓦片,避免单帧耗时过载:

private void Start()
    {
        StartCoroutine(GenerateGridInBatches());
    }

    IEnumerator GenerateGridInBatches()
    {
        cells = settingGeneration.startSetting(size);
        int totalTiles = size * size;
        int batchSize = 50; // 每帧处理50个瓦片,可根据设备性能调整
        int processed = 0;

        while (processed < totalTiles)
        {
            int endIndex = Mathf.Min(processed + batchSize, totalTiles);
            for (; processed < endIndex; processed++)
            {
                int x = processed / size;
                int y = processed % size;
                byte tileType = cells[x, y];

                if (tilePrefabCache.TryGetValue(tileType, out var prefab))
                {
                    GameObject tile = Instantiate(prefab, transform);
                    tile.name = $"X: {x} Y: {y}";
                    float posX = y * tileSize;
                    float posY = -x * tileSize;
                    tile.transform.position = new Vector2(posX, posY);
                }
            }
            yield return null; // 让出主线程,等待下一帧渲染
        }
    }
}

3. 多线程辅助数据预处理(可选)

如果瓦片的类型计算、位置逻辑复杂,可使用Task在子线程预处理数据,再回到主线程实例化:

private void Start()
    {
        StartCoroutine(GenerateGridWithMultithreading());
    }

    IEnumerator GenerateGridWithMultithreading()
    {
        List<TileData> tileDatas = new List<TileData>();

        // 子线程处理纯数据计算,不调用任何Unity API
        yield return Task.Run(() =>
        {
            var cells = settingGeneration.startSetting(size);
            for (int x = 0; x < size; x++)
            {
                for (int y = 0; y < size; y++)
                {
                    byte type = cells[x, y];
                    float posX = y * tileSize;
                    float posY = -x * tileSize;
                    tileDatas.Add(new TileData(x, y, type, posX, posY));
                }
            }
        }).AsCoroutine();

        // 主线程分批实例化
        int batchSize = 50;
        int processed = 0;
        int total = tileDatas.Count;
        while (processed < total)
        {
            int endIndex = Mathf.Min(processed + batchSize, total);
            for (; processed < endIndex; processed++)
            {
                var data = tileDatas[processed];
                if (tilePrefabCache.TryGetValue(data.Type, out var prefab))
                {
                    GameObject tile = Instantiate(prefab, transform);
                    tile.name = $"X: {data.X} Y: {data.Y}";
                    tile.transform.position = new Vector2(data.PosX, data.PosY);
                }
            }
            yield return null;
        }
    }

    // 存储瓦片数据的结构体,子线程可安全处理
    private struct TileData
    {
        public int X;
        public int Y;
        public byte Type;
        public float PosX;
        public float PosY;

        public TileData(int x, int y, byte type, float posX, float posY)
        {
            X = x;
            Y = y;
            Type = type;
            PosX = posX;
            PosY = posY;
        }
    }
}

关键注意事项

  • Unity多线程限制:子线程绝对不能调用任何Unity引擎API(如Instantiate、Transform操作、Resources.Load),仅能处理纯数据计算
  • 资源缓存优先级:预加载缓存是提升性能最直接的手段,避免重复IO操作
  • 分批处理必要性:即使不用多线程,分批实例化也能大幅降低单帧耗时,解决卡顿问题

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 01:20:33