在Unity中用ScriptableObject替代Enum是否过度设计?
阵营关系处理:ScriptableObject vs 带显式值的Enum
核心结论
不是你对ScriptableObject(简称SO)的使用有误解,而是两种方案各有适用场景——关键看你的阵营/关系需求是动态配置导向还是轻量固定导向。
方案一:带显式值的Enum(适合固定场景)
如果你的阵营数量少、关系逻辑长期稳定(比如默认敌对,仅少数友好组合),带显式值的Enum完全够用,而且简单直接。
解决Enum的修改隐患:给每个阵营Enum指定固定的、间隔较大的整数值,避免调整顺序或删除元素时破坏序列化数据。配合静态关系映射字典,就能快速获取阵营关系:
public enum Faction { A = 100, B = 200, C = 300 } public enum RelationshipType { Hostile, Friendly, Neutral } public static class FactionRelations { private static readonly Dictionary<(Faction, Faction), RelationshipType> _relationMap = new() { {(Faction.A, Faction.B), RelationshipType.Hostile}, {(Faction.A, Faction.C), RelationshipType.Friendly}, // 补充其他需要自定义的阵营组合 }; public static RelationshipType GetRelation(Faction a, Faction b) { return _relationMap.TryGetValue((a, b), out var rel) ? rel : RelationshipType.Hostile; } // 简化业务代码调用的快捷方法 public static bool IsHostile(Faction a, Faction b) => GetRelation(a, b) == RelationshipType.Hostile; }
这种方案优势是代码轻量、性能好,不需要额外的SO资源,适合小体量、需求稳定的场景。
方案二:优化后的ScriptableObject方案(适合动态配置场景)
你之前的SO方案问题在于关系分散管理,导致获取关系繁琐。正确的做法是用一个全局关系数据库SO统一管理所有阵营关系,发挥SO可编辑、无需编译的优势。
优化后的结构:
- FactionSO:仅存储阵营基础标识(名称、唯一ID),不包含关系逻辑
- FactionRelationDatabaseSO:作为全局可引用资源,集中存储所有阵营间的关系映射
- RelationshipType:用简单Enum定义固定的关系类型(敌对/友好/中立),因为这类类型很少变动
示例代码:
// 阵营SO,仅存基础信息 [CreateAssetMenu(fileName = "NewFaction", menuName = "Game/Faction")] public class FactionSO : ScriptableObject { public string factionName; public Guid factionId; // 用Guid避免重名或引用失效问题 } // 固定的关系类型Enum public enum RelationshipType { Hostile, Friendly, Neutral } // 全局关系数据库SO [CreateAssetMenu(fileName = "FactionRelationDB", menuName = "Game/Faction Relation Database")] public class FactionRelationDatabaseSO : ScriptableObject { [Serializable] public class RelationEntry { public FactionSO factionA; public FactionSO factionB; public RelationshipType relation; } public List<RelationEntry> relationEntries; private Dictionary<(FactionSO, FactionSO), RelationshipType> _relationCache; private void OnEnable() { // 初始化缓存,避免每次遍历列表 _relationCache = new Dictionary<(FactionSO, FactionSO), RelationshipType>(); foreach (var entry in relationEntries) { _relationCache[(entry.factionA, entry.factionB)] = entry.relation; // 若关系是双向的(A对B友好则B对A也友好),添加反向映射;单向关系则去掉这行 _relationCache[(entry.factionB, entry.factionA)] = entry.relation; } } public RelationshipType GetRelation(FactionSO a, FactionSO b) { return _relationCache.TryGetValue((a, b), out var type) ? type : RelationshipType.Hostile; } // 快捷判断方法,简化业务代码调用 public bool IsHostile(FactionSO a, FactionSO b) => GetRelation(a, b) == RelationshipType.Hostile; public bool IsFriendly(FactionSO a, FactionSO b) => GetRelation(a, b) == RelationshipType.Friendly; }
这种方案的优势:
- 策划可直接在Unity编辑器中添加/修改阵营关系,无需修改代码或重新编译
- 支持后期动态新增阵营,扩展性强
- 关系逻辑集中管理,避免分散维护的混乱
如何选择?
- 选带显式值的Enum:阵营数量固定(比如你现在的A/B/C三个)、关系逻辑很少变动,且不需要可视化配置
- 选优化后的SO方案:后期可能新增大量阵营、需要非技术人员调整关系,或者存在单向关系等复杂逻辑
内容的提问来源于stack exchange,提问作者FrontBadger
相关产品推荐
相关产品推荐

