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
相关产品推荐
相关产品推荐

