Unity3D中基于ScriptableObject实现可编辑的复杂奖励数据结构
实现支持Unity编辑器可视化编辑的Roguelike奖励系统ScriptableObject
因为Unity编辑器不支持直接序列化Dictionary并提供可视化编辑界面,我们可以用可序列化的嵌套类+列表来模拟Lua嵌套表的结构,同时在运行时构建字典缓存保证查询效率。具体实现步骤如下:
1. 定义基础枚举与数据类
先明确核心分类的枚举,以及单个奖励的结构:
// 职业枚举 public enum CharacterClass { Melee, Healer, Ranger, Mage } // 稀有度枚举 public enum RewardRarity { Common, Uncommon, Rare, Legendary } // 单个奖励的具体数据,标记为可序列化让编辑器识别 [Serializable] public class RewardEntry { public string rewardName; public int statBoost; public Sprite rewardIcon; // 可根据需求添加更多属性(如物品ID、技能解锁标记等) }
2. 构建嵌套的可序列化组类
用两层嵌套的可序列化类,对应「职业→稀有度→奖励列表」的层级:
// 单个稀有度对应的奖励组 [Serializable] public class RarityRewardGroup { public RewardRarity rarity; public List<RewardEntry> rewards = new List<RewardEntry>(); } // 单个职业对应的稀有度奖励组集合 [Serializable] public class ClassRewardGroup { public CharacterClass characterClass; public List<RarityRewardGroup> rarityGroups = new List<RarityRewardGroup>(); }
3. 实现核心ScriptableObject
用List<ClassRewardGroup>替代外层字典,同时在内部构建字典缓存用于运行时快速查询:
[CreateAssetMenu(fileName = "RewardDatabase", menuName = "Roguelike/Reward Database")] public class RewardDatabase : ScriptableObject { // 编辑器可见的列表,支持直接增删改 [SerializeField] private List<ClassRewardGroup> classRewardGroups = new List<ClassRewardGroup>(); // 运行时缓存用的字典,保证查询效率 private Dictionary<CharacterClass, Dictionary<RewardRarity, List<RewardEntry>>> _rewardCache; private void OnEnable() { BuildRewardCache(); } private void OnValidate() { // 编辑器修改数据时自动更新缓存 BuildRewardCache(); } // 构建运行时查询用的字典缓存 private void BuildRewardCache() { _rewardCache = new Dictionary<CharacterClass, Dictionary<RewardRarity, List<RewardEntry>>>(); foreach (var classGroup in classRewardGroups) { var rarityDict = new Dictionary<RewardRarity, List<RewardEntry>>(); foreach (var rarityGroup in classGroup.rarityGroups) { // 处理重复稀有度的情况(可选,根据需求覆盖或报错) if (rarityDict.ContainsKey(rarityGroup.rarity)) { Debug.LogWarning($"重复的稀有度 {rarityGroup.rarity} 存在于职业 {classGroup.characterClass} 中,已覆盖原有数据"); } rarityDict[rarityGroup.rarity] = new List<RewardEntry>(rarityGroup.rewards); } _rewardCache[classGroup.characterClass] = rarityDict; } } // 对外提供的查询方法 public List<RewardEntry> GetRewards(CharacterClass characterClass, RewardRarity rarity) { if (_rewardCache.TryGetValue(characterClass, out var rarityDict) && rarityDict.TryGetValue(rarity, out var rewards)) { return rewards; } Debug.LogWarning($"未找到职业 {characterClass}、稀有度 {rarity} 对应的奖励"); return new List<RewardEntry>(); } }
4. 编辑器增强(可选)
添加自定义Editor类,自动校验重复的职业/稀有度,避免配置错误:
#if UNITY_EDITOR using UnityEditor; using System.Linq; [CustomEditor(typeof(RewardDatabase))] public class RewardDatabaseEditor : Editor { public override void OnInspectorGUI() { base.OnInspectorGUI(); var db = target as RewardDatabase; if (db == null) return; // 检查重复职业 var duplicateClasses = db.classRewardGroups.GroupBy(g => g.characterClass) .Where(g => g.Count() > 1) .Select(g => g.Key); if (duplicateClasses.Any()) { EditorGUILayout.HelpBox($"发现重复职业:{string.Join(", ", duplicateClasses)}", MessageType.Warning); } // 检查每个职业下的重复稀有度 foreach (var classGroup in db.classRewardGroups) { var duplicateRarities = classGroup.rarityGroups.GroupBy(g => g.rarity) .Where(g => g.Count() > 1) .Select(g => g.Key); if (duplicateRarities.Any()) { EditorGUILayout.HelpBox($"职业 {classGroup.characterClass} 下存在重复稀有度:{string.Join(", ", duplicateRarities)}", MessageType.Warning); } } } } #endif
使用方式
- 在Unity编辑器右键菜单选择
Roguelike/Reward Database,创建奖励数据库资产 - 在Inspector面板中直接添加/删除
ClassRewardGroup,选择对应职业 - 每个职业组下添加
RarityRewardGroup,选择稀有度并填充奖励条目 - 代码中通过
GetRewards()方法按职业和稀有度查询奖励
内容的提问来源于stack exchange,提问作者mAts
相关产品推荐
相关产品推荐

