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

Unity使用字典批量加载Resources资源实现2D角色换肤问题

问题根因

你的代码无法生效是两个C#语法和字典特性的常识问题,和Unity资源加载本身无关:

  • Dictionary<TKey, TValue>的Key是只读的,遍历字典返回的KeyValuePair<TKey, TValue>是值类型,你对pair.Key的赋值只是修改了值拷贝的临时变量,既不会改动字典内的存储内容,也不会同步修改你类中定义的_faces、_hoods等字段,属于完全无效的赋值。
  • 你初始化字典时,_faces等字段还未加载资源,默认值都是null,相当于把一堆null作为Key存入字典,就算Key可写,也无法关联到你实际需要赋值的目标字段。
推荐实现方案

最稳妥的实现是把映射关系改成「资源路径 -> 加载完成后的赋值回调」,完全类型安全,无反射开销,后续扩展也方便。

// 部位Sprite数组字段,和你原有定义保持一致
private Sprite[] _faces;
private Sprite[] _hoods;
private Sprite[] _elbowsLeft;
private Sprite[] _elbowsRight;
private Sprite[] _shouldersLeft;
private Sprite[] _shouldersRight;
private Sprite[] _wristsLeft;
private Sprite[] _wristsRight;
private Sprite[] _torsos;
private Sprite[] _pelvises;
private Sprite[] _legsLeft;
private Sprite[] _legsRight;
private Sprite[] _bootsLeft;
private Sprite[] _bootsRight;

private Dictionary<string, Action<Sprite[]>> _spriteLoadMap;

private void InitLoadMap()
{
    const string resRoot = "Rogue/";
    _spriteLoadMap = new Dictionary<string, Action<Sprite[]>>()
    {
        { $"{resRoot}Rogue_face", sprites => _faces = sprites },
        { $"{resRoot}Rogue_hood", sprites => _hoods = sprites },
        { $"{resRoot}Rogue_elbow_l", sprites => _elbowsLeft = sprites },
        { $"{resRoot}Rogue_elbow_r", sprites => _elbowsRight = sprites },
        { $"{resRoot}Rogue_shoulder_l", sprites => _shouldersLeft = sprites },
        { $"{resRoot}Rogue_shoulder_r", sprites => _shouldersRight = sprites },
        { $"{resRoot}Rogue_wrist_l", sprites => _wristsLeft = sprites },
        { $"{resRoot}Rogue_wrist_r", sprites => _wristsRight = sprites },
        { $"{resRoot}Rogue_torso", sprites => _torsos = sprites },
        { $"{resRoot}Rogue_pelvis", sprites => _pelvises = sprites },
        { $"{resRoot}Rogue_leg_l", sprites => _legsLeft = sprites },
        { $"{resRoot}Rogue_leg_r", sprites => _legsRight = sprites },
        { $"{resRoot}Rogue_boot_l", sprites => _bootsLeft = sprites },
        { $"{resRoot}Rogue_boot_r", sprites => _bootsRight = sprites },
    };
}

private void LoadResources()
{
    InitLoadMap();
    foreach (var loadEntry in _spriteLoadMap)
    {
        Sprite[] loaded = Resources.LoadAll<Sprite>(loadEntry.Key);
        loadEntry.Value?.Invoke(loaded);
    }
}

*如果后续要新增皮肤部位,只需要在_spriteLoadMap里加一行映射即可,加载循环逻辑不用改动。如果后续替换成Addressables等新的加载管线,也只需要修改循环内的加载代码,映射关系完全可以复用。

注意:Resources.LoadAll会加载目标路径下的所有同类型资源,要确保对应Resources目录下没有无关Sprite,避免产生冗余内存占用。

可选简化方案:反射自动映射

如果你嫌手动写映射表麻烦,且能接受一次性的初始化反射开销,可以按字段命名规则自动匹配资源路径,后续新增部位字段只要命名符合规则就不需要改加载代码:

private void LoadResourcesAuto()
{
    const string resRoot = "Rogue/";
    // 获取当前类所有私有Sprite数组类型的实例字段
    var spriteFields = GetType().GetFields(
            System.Reflection.BindingFlags.NonPublic | 
            System.Reflection.BindingFlags.Instance)
        .Where(f => f.FieldType == typeof(Sprite[]))
        .ToList();

    foreach (var field in spriteFields)
    {
        // 字段名转资源名规则:_elbowsLeft -> Rogue_elbow_l
        string fieldPart = field.Name.TrimStart('_');
        string resSuffix = "Rogue_" + string.Concat(fieldPart.Select(c => 
            char.IsUpper(c) ? "_" + char.ToLower(c) : c.ToString()
        )).TrimStart('_');
        
        Sprite[] loaded = Resources.LoadAll<Sprite>($"{resRoot}{resSuffix}");
        field.SetValue(this, loaded);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:54:19