如何基于Enum实现类似《刺客信条:枭雄》的游戏升级系统
嘿,这个需求我熟!用Enum来实现《刺客信条:枭雄》那种线性解锁、部分升级会替换旧项的系统完全可行,而且逻辑清晰、容易维护。我来给你拆解具体的实现思路和代码示例~
实现思路核心
首先明确你的两个核心需求:
- 线性解锁:必须按顺序解锁前置升级才能购买后续项
- 升级替换/保留:同系列的升级(比如Scavenger I→II)会替换旧项,独立升级(比如Looter)则新增保留
我们可以用Enum定义所有升级项,再配合自定义Attribute存储升级元数据(比如所属组、前置条件、效果),最后用一个升级管理器类来处理解锁逻辑和状态跟踪。
具体实现步骤
1. 定义升级项Enum及元数据
首先用Enum列出所有升级项,然后通过自定义Attribute给每个升级项附加关键信息:所属组(用来区分替换关系)、前置升级、显示名称和效果。
自定义UpgradeInfoAttribute
这个Attribute用来给Enum字段添加元数据:
[AttributeUsage(AttributeTargets.Field)] public class UpgradeInfoAttribute : Attribute { public string DisplayName { get; } // 游戏内显示的名称 public string UpgradeGroup { get; } // 所属升级组(同组替换) public UpgradeType? Prerequisite { get; } // 前置升级(可选) public string Effect { get; } // 升级效果描述 public UpgradeInfoAttribute(string displayName, string upgradeGroup, string effect, UpgradeType? prerequisite = null) { DisplayName = displayName; UpgradeGroup = upgradeGroup; Effect = effect; Prerequisite = prerequisite; } }
定义UpgradeType Enum
把你的升级项按需求分类,给每个项加上Attribute:
public enum UpgradeType { [UpgradeInfo("Scavenger I", "Scavenger", "提升敌人掉落 loot 数量10%")] ScavengerI, [UpgradeInfo("Scavenger II", "Scavenger", "提升敌人掉落 loot 数量25%", UpgradeType.ScavengerI)] ScavengerII, [UpgradeInfo("Looter", "Looter", "无需开锁器即可开启稀有宝箱", UpgradeType.ScavengerII)] Looter, // 可以继续添加其他升级项示例 [UpgradeInfo("开锁大师", "Lockpick", "减少开锁时间50%")] LockpickMaster, [UpgradeInfo("快速治疗 I", "QuickHeal", "减少治疗时间20%")] QuickHealI, [UpgradeInfo("快速治疗 II", "QuickHeal", "减少治疗时间40%", UpgradeType.QuickHealI)] QuickHealII }
2. 实现升级管理器类
这个类负责跟踪升级状态、验证解锁条件、处理购买逻辑(替换/新增):
public class UpgradeManager { // 存储当前生效的升级:key=升级组,value=当前激活的升级项 private readonly Dictionary<string, UpgradeType> _activeUpgrades = new(); // 存储所有已解锁的升级(包括被替换的,用来验证前置条件) private readonly HashSet<UpgradeType> _unlockedUpgrades = new(); /// <summary> /// 检查是否满足解锁某个升级的条件 /// </summary> public bool CanUnlockUpgrade(UpgradeType upgrade) { var upgradeInfo = GetUpgradeMetadata(upgrade); if (upgradeInfo == null) return false; // 有前置升级的话,必须已解锁前置 if (upgradeInfo.Prerequisite.HasValue && !_unlockedUpgrades.Contains(upgradeInfo.Prerequisite.Value)) { return false; } return true; } /// <summary> /// 购买升级,处理替换/新增逻辑 /// </summary> public bool PurchaseUpgrade(UpgradeType upgrade) { if (!CanUnlockUpgrade(upgrade)) { return false; // 不满足条件,购买失败 } var upgradeInfo = GetUpgradeMetadata(upgrade); // 标记该升级为已解锁(即使被替换,也保留解锁记录) _unlockedUpgrades.Add(upgrade); // 处理同组替换逻辑 if (!string.IsNullOrEmpty(upgradeInfo.UpgradeGroup)) { if (_activeUpgrades.ContainsKey(upgradeInfo.UpgradeGroup)) { // 替换同组旧升级,可以在这里添加旧效果移除的逻辑 _activeUpgrades[upgradeInfo.UpgradeGroup] = upgrade; } else { // 新增升级组,添加到生效列表 _activeUpgrades.Add(upgradeInfo.UpgradeGroup, upgrade); } } // 应用升级效果(比如修改玩家属性、解锁功能) ApplyUpgradeEffect(upgrade); return true; } /// <summary> /// 获取当前所有生效的升级 /// </summary> public IEnumerable<UpgradeType> GetActiveUpgrades() => _activeUpgrades.Values; /// <summary> /// 获取升级的元数据 /// </summary> private UpgradeInfoAttribute GetUpgradeMetadata(UpgradeType upgrade) { var field = typeof(UpgradeType).GetField(upgrade.ToString()); return field?.GetCustomAttribute<UpgradeInfoAttribute>(); } /// <summary> /// 应用升级效果的示例方法,根据实际需求扩展 /// </summary> private void ApplyUpgradeEffect(UpgradeType upgrade) { switch (upgrade) { case UpgradeType.ScavengerI: case UpgradeType.ScavengerII: // 调整玩家的 loot 倍率 break; case UpgradeType.Looter: // 解锁稀有宝箱的访问权限 break; case UpgradeType.LockpickMaster: // 减少开锁时间 break; // 其他升级的效果逻辑 } } }
3. 使用示例
// 初始化升级管理器 var upgradeManager = new UpgradeManager(); // 尝试直接买Scavenger II,没有前置Scavenger I,购买失败 bool boughtScavengerII = upgradeManager.PurchaseUpgrade(UpgradeType.ScavengerII); // 返回false // 先买Scavenger I,成功 bool boughtScavengerI = upgradeManager.PurchaseUpgrade(UpgradeType.ScavengerI); // 返回true // 再买Scavenger II,成功,此时Scavenger I被替换 boughtScavengerII = upgradeManager.PurchaseUpgrade(UpgradeType.ScavengerII); // 返回true // 买Looter,需要Scavenger II已解锁,成功,新增到生效列表 bool boughtLooter = upgradeManager.PurchaseUpgrade(UpgradeType.Looter); // 返回true // 获取当前生效的升级:Scavenger II、Looter var activeUpgrades = upgradeManager.GetActiveUpgrades();
为什么这样实现?
- 强类型安全:用Enum定义升级项,避免了字符串拼写错误,IDE还能自动补全
- 逻辑清晰:升级的元数据(组、前置)和业务逻辑(解锁、替换)分离,后续新增升级只需要加Enum字段和Attribute,不用改管理器核心逻辑
- 状态可控:用两个集合分别跟踪“已解锁”和“当前生效”的升级,既保证前置条件验证正确,又能精准控制当前生效的效果
内容的提问来源于stack exchange,提问作者Evie Frye
相关产品推荐
相关产品推荐

