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

Unity重建SVG Sprite报错:Not allowed to override geometry on sprite

Unity SpriteRenderer更新Sprite时的竞态条件问题

在动态创建带图标的GameObject流程中,首次运行正常,但远程更新SVG文件后重建对象时,会触发Not allowed to override geometry on sprite错误,同时出现999+条Incomplete mesh data in Sprite. Please reimport or recreate Sprite.警告。

问题复现代码

创建GameObject的核心逻辑:

for (int i = 0; i < data.Count; i++)
{
    GameObject obj = new(data[i].Details.UID.ToString());
    obj.transform.SetParent(itemsContainer.transform);
    obj.AddComponent<RectTransform>();

    SelectionItem levelViewItem = obj.AddComponent<SelectionItem>();

    GameObject icon = new("icon"); 
    RectTransform iconTransform = icon.AddComponent<RectTransform>();
    icon.transform.SetParent(obj.transform);

    SpriteRenderer iconSprite = icon.AddComponent<SpriteRenderer>();
    iconSprite.sprite = data[i].Details.LoadIcon(); // 从磁盘加载并生成Sprite

    levelViewItem.icon = iconSprite;
    temp[i] = levelViewItem;
}

临时解决方案(存在延迟问题):
移除循环内的Sprite赋值,延迟2秒后批量设置:

await Task.Delay(2000);
foreach (var item in temp)
{
   SpriteRenderer ren = item.transform.GetChild(0).GetComponent<SpriteRenderer>();
   ren.sprite = books[0].Details.LoadIcon();
}

SVG转Sprite的核心代码

public static Sprite ProcessSvg(string svgFilePath, string name)
{
     int pixelsPerUnit = 1;
     bool flipYAxis = true;
     if (!string.IsNullOrEmpty(svgFilePath))
     {
         using (StreamReader textReader = File.OpenText(svgFilePath))
         {
              var sceneInfo = SVGParser.ImportSVG(textReader);
              List<Geometry> geometries = TessellateScene(sceneInfo.Scene, new TessellationOptions
              {
                  StepDistance = 1f,
                  SamplingStepSize = 1,
                  MaxCordDeviation = 0,
                  MaxTanAngleDeviation = 0
               });
               Sprite sprite = BuildSprite(geometries, pixelsPerUnit, Alignment.Center, Vector2.zero, 128, flipYAxis);
               sprite.name = name;
               return sprite;
        }
    }
    else
    {
       Debug.LogError("SVG content is empty!");
       return null;
    }
}

问题原因分析

  • 同步循环中,SVG解析、Sprite生成与GameObject初始化同时进行,Sprite的网格数据尚未完全构建完成,就被Unity渲染线程尝试读取,导致数据不完整。
  • 重建对象时,旧Sprite可能仍被未销毁的对象引用,或未被垃圾回收,新Sprite的几何数据被错误覆盖,触发Not allowed to override geometry on sprite错误。

解决方案

1. 异步生成Sprite,主线程赋值

先完成所有GameObject的结构创建,再在后台线程批量生成Sprite,最后回到主线程赋值(Unity API必须在主线程调用):

// 第一步:创建所有UI结构,暂不设置Sprite
for (int i = 0; i < data.Count; i++)
{
    GameObject obj = new(data[i].Details.UID.ToString());
    obj.transform.SetParent(itemsContainer.transform);
    obj.AddComponent<RectTransform>();

    SelectionItem levelViewItem = obj.AddComponent<SelectionItem>();

    GameObject icon = new("icon"); 
    RectTransform iconTransform = icon.AddComponent<RectTransform>();
    icon.transform.SetParent(obj.transform);

    SpriteRenderer iconSprite = icon.AddComponent<SpriteRenderer>();
    levelViewItem.icon = iconSprite;
    temp[i] = levelViewItem;
}

// 第二步:后台线程批量生成Sprite
await Task.Run(() =>
{
    var spriteCache = new Dictionary<string, Sprite>();
    foreach (var itemData in data)
    {
        string uid = itemData.Details.UID.ToString();
        if (!spriteCache.ContainsKey(uid))
        {
            spriteCache[uid] = itemData.Details.LoadIcon();
        }
    }

    // 回到主线程赋值
    UnityMainThreadDispatcher.Instance.Enqueue(() =>
    {
        for (int i = 0; i < temp.Length; i++)
        {
            string uid = data[i].Details.UID.ToString();
            temp[i].icon.sprite = spriteCache[uid];
        }
    });
});

注:需要实现UnityMainThreadDispatcher单例,用于在后台线程完成后将任务排入主线程执行(可通过Update方法遍历任务队列实现)。

2. 重建前彻底清理旧资源

在重建对象前,销毁所有旧对象并强制回收未使用资源,避免旧Sprite干扰:

// 销毁容器内所有旧对象
foreach (Transform child in itemsContainer.transform)
{
    Destroy(child.gameObject);
}
// 强制回收未使用资源
Resources.UnloadUnusedAssets();
GC.Collect();

3. 优化Sprite生成的有效性检查

在ProcessSvg方法中,添加Sprite有效性检查,确保返回的Sprite数据完整:

Sprite sprite = BuildSprite(geometries, pixelsPerUnit, Alignment.Center, Vector2.zero, 128, flipYAxis);
sprite.name = name;

// 检查Sprite是否有效
if (sprite == null || sprite.texture == null || sprite.rect.width <= 0 || sprite.rect.height <= 0)
{
    Debug.LogError($"生成无效Sprite:{name}");
    return null;
}

return sprite;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 21:05:04