设计一个通用的属性管理系统,需要打破具体游戏类型的限制(如RPG、FPS、策略游戏),支持任意属性类型、灵活的计算规则、动态效果叠加,并能适配不同业务场景(角色、物品、技能、环境等)。核心目标是:可扩展、低耦合、规则可配置、支持多实体复用

一、通用属性系统的核心需求

  1. 属性类型无关:不预设属性(如攻击、防御),支持用户自定义任意属性(数值、文本、布尔等)。
  2. 计算规则可配置:属性的最终值计算逻辑(如基础值+加成、乘区叠加、优先级规则)可动态定义,而非硬编码。
  3. 多维度加成支持:支持临时加成(buff)、永久加成(装备)、条件加成(如“生命值低于50%时攻击+20%”)等。
  4. 跨实体复用:同一套系统可管理角色、怪物、物品、场景等不同实体的属性。
  5. 事件驱动:属性变化时能触发回调,方便UI、战斗等外部系统响应。
  6. 可持久化:支持属性数据的保存与加载(如存档、网络同步)。

二、通用属性系统设计方案

1. 核心数据结构定义

(1)属性标识(AttributeKey)

用“键”标识属性,避免硬编码枚举,支持动态扩展:

/// <summary>
/// 属性唯一标识(可自定义任意属性)
/// </summary>
public struct AttributeKey : IEquatable<AttributeKey>
{
    public string Name; // 属性名称(如"HP"、"Attack"、"MoveSpeed")
    public Type ValueType; // 属性值类型(int、float、bool、string等)

    public AttributeKey(string name, Type valueType)
    {
        Name = name;
        ValueType = valueType;
    }

    // 实现相等性比较(用于字典键)
    public bool Equals(AttributeKey other) => Name == other.Name && ValueType == other.ValueType;
    public override int GetHashCode() => HashCode.Combine(Name, ValueType);
}

示例:定义“生命值”“暴击率”属性:

var hpKey = new AttributeKey("HP", typeof(float));
var critChanceKey = new AttributeKey("CriticalChance", typeof(float));
(2)属性值容器(AttributeValue)

统一封装不同类型的属性值,支持类型安全访问:

/// <summary>
/// 属性值容器(支持多类型)
/// </summary>
public class AttributeValue
{
    public AttributeKey Key { get; }
    private object _value; // 内部存储原始值

    public AttributeValue(AttributeKey key, object initialValue)
    {
        Key = key;
        SetValue(initialValue);
    }

    // 类型安全设置值
    public void SetValue(object value)
    {
        if (value.GetType() != Key.ValueType)
            throw new ArgumentException($"属性{Key.Name}类型不匹配,预期{Key.ValueType}");
        _value = value;
    }

    // 类型安全获取值
    public T GetValue<T>()
    {
        if (typeof(T) != Key.ValueType)
            throw new InvalidCastException($"属性{Key.Name}无法转换为{typeof(T)}");
        return (T)_value;
    }

    // 克隆(用于快照或备份)
    public AttributeValue Clone() => new AttributeValue(Key, _value);
}
(3)属性修饰器(AttributeModifier)

定义对属性的“修改规则”,支持多种加成类型和生命周期:

/// <summary>
/// 属性修饰器(描述如何修改属性)
/// </summary>
public class AttributeModifier
{
    public string Id { get; } // 唯一标识(用于移除特定修饰器)
    public AttributeKey TargetKey { get; } // 目标属性
    public ModifyType ModifyType { get; } // 修饰类型(加法、乘法、覆盖等)
    public object Value { get; } // 修饰值(与属性同类型)
    public int Priority { get; } // 优先级(高优先级先计算)
    public bool IsPermanent { get; } // 是否永久生效(如装备)
    public float Duration { get; } // 持续时间(非永久时生效,-1为无限)
    public Func<bool> Condition { get; } // 生效条件(如"HP<50%"才生效)

    // 构造函数(简化版)
    public AttributeModifier(
        string id,
        AttributeKey targetKey,
        ModifyType modifyType,
        object value,
        int priority = 0,
        bool isPermanent = false,
        float duration = -1,
        Func<bool> condition = null)
    {
        Id = id;
        TargetKey = targetKey;
        ModifyType = modifyType;
        Value = value;
        Priority = priority;
        IsPermanent = isPermanent;
        Duration = duration;
        Condition = condition ?? (() => true); // 默认无条件生效
    }
}

/// <summary>
/// 修饰类型(支持多种计算规则)
/// </summary>
public enum ModifyType
{
    Add, // 加法:base + value(如HP+100)
    Multiply, // 乘法:base * (1 + value)(如攻击+20% → value=0.2)
    Override, // 覆盖:直接设置为value(如临时锁血为1)
    Min, // 最小值限制:最终值不低于value(如HP最低1)
    Max, // 最大值限制:最终值不高于value(如暴击率最高100%)
    PercentAdd // 百分比加法:base + (base * value)(与Multiply的区别是顺序影响结果)
}

2. 属性计算器(AttributeCalculator)

核心模块,负责根据“基础值”和“修饰器”计算属性的最终值,支持自定义计算逻辑:

/// <summary>
/// 属性计算器(处理属性计算逻辑)
/// </summary>
public class AttributeCalculator
{
    // 默认计算逻辑(可被覆盖)
    public virtual object CalculateFinalValue(AttributeValue baseValue, List<AttributeModifier> modifiers)
    {
        // 1. 筛选生效的修饰器(满足条件且未过期)
        var validModifiers = modifiers
            .Where(m => m.Condition.Invoke())
            .OrderByDescending(m => m.Priority) // 按优先级排序
            .ToList();

        // 2. 从基础值开始计算
        object currentValue = baseValue.Clone().GetValue<object>();

        // 3. 应用修饰器(根据类型处理)
        foreach (var modifier in validModifiers)
        {
            currentValue = ApplyModifier(currentValue, modifier);
        }

        return currentValue;
    }

    // 应用单个修饰器
    private object ApplyModifier(object currentValue, AttributeModifier modifier)
    {
        // 根据属性类型和修饰类型处理(以float为例)
        if (currentValue is float floatValue && modifier.Value is float modValue)
        {
            return modifier.ModifyType switch
            {
                ModifyType.Add => floatValue + modValue,
                ModifyType.Multiply => floatValue * (1 + modValue),
                ModifyType.Override => modValue,
                ModifyType.Min => Mathf.Max(floatValue, modValue),
                ModifyType.Max => Mathf.Min(floatValue, modValue),
                ModifyType.PercentAdd => floatValue + (floatValue * modValue),
                _ => floatValue
            };
        }

        // 支持其他类型(如int、bool等)
        // ...(省略int、bool等类型的处理逻辑)

        throw new NotSupportedException($"不支持{currentValue.GetType()}类型的{modifier.ModifyType}修饰");
    }
}

3. 属性管理器(GenericAttributeSystem)

管理一个实体(如角色、物品)的所有属性,提供属性注册、修饰器添加/移除、最终值查询等接口:

/// <summary>
/// 通用属性管理器(实体的属性入口)
/// </summary>
public class GenericAttributeSystem
{
    private readonly Dictionary<AttributeKey, AttributeValue> _baseAttributes = new(); // 基础属性
    private readonly List<AttributeModifier> _modifiers = new(); // 所有修饰器
    private readonly AttributeCalculator _calculator; // 计算器(可自定义)
    private readonly Dictionary<AttributeKey, object> _finalValuesCache = new(); // 最终值缓存

    // 属性变化事件(参数:属性键、旧值、新值)
    public event Action<AttributeKey, object, object> OnAttributeChanged;

    public GenericAttributeSystem(AttributeCalculator calculator = null)
    {
        _calculator = calculator ?? new AttributeCalculator(); // 默认计算器
    }

    // 注册基础属性
    public void RegisterBaseAttribute(AttributeKey key, object initialValue)
    {
        if (!_baseAttributes.ContainsKey(key))
        {
            _baseAttributes[key] = new AttributeValue(key, initialValue);
            RecalculateFinalValue(key); // 初始计算
        }
    }

    // 添加修饰器(如buff、装备)
    public void AddModifier(AttributeModifier modifier)
    {
        if (_modifiers.Any(m => m.Id == modifier.Id)) return; // 避免重复添加
        _modifiers.Add(modifier);
        // 若修饰器有持续时间,启动计时器自动移除
        if (!modifier.IsPermanent && modifier.Duration > 0)
        {
            TimerManager.Instance.StartTimer(modifier.Duration, () => RemoveModifier(modifier.Id));
        }
        RecalculateFinalValue(modifier.TargetKey); // 重新计算目标属性
    }

    // 移除修饰器(如buff过期、卸装备)
    public void RemoveModifier(string modifierId)
    {
        var modifier = _modifiers.FirstOrDefault(m => m.Id == modifierId);
        if (modifier == null) return;
        _modifiers.Remove(modifier);
        RecalculateFinalValue(modifier.TargetKey); // 重新计算目标属性
    }

    // 获取属性最终值(从缓存或计算)
    public T GetFinalValue<T>(AttributeKey key)
    {
        if (!_finalValuesCache.TryGetValue(key, out var value))
        {
            // 缓存未命中,重新计算
            RecalculateFinalValue(key);
            value = _finalValuesCache[key];
        }
        return (T)value;
    }

    // 重新计算属性最终值并触发事件
    private void RecalculateFinalValue(AttributeKey key)
    {
        if (!_baseAttributes.TryGetValue(key, out var baseValue)) return;

        // 筛选影响该属性的修饰器
        var relevantModifiers = _modifiers.Where(m => m.TargetKey.Equals(key)).ToList();

        // 计算新值
        var oldValue = _finalValuesCache.TryGetValue(key, out var ov) ? ov : baseValue.GetValue<object>();
        var newValue = _calculator.CalculateFinalValue(baseValue, relevantModifiers);

        // 更新缓存
        _finalValuesCache[key] = newValue;

        // 触发变化事件
        OnAttributeChanged?.Invoke(key, oldValue, newValue);
    }

    // 清空所有临时修饰器(如角色死亡重置)
    public void ClearTemporaryModifiers()
    {
        var tempModifiers = _modifiers.Where(m => !m.IsPermanent).ToList();
        foreach (var mod in tempModifiers)
        {
            _modifiers.Remove(mod);
            RecalculateFinalValue(mod.TargetKey);
        }
    }
}

4. 扩展与适配

(1)自定义计算器(复杂规则场景)

如果游戏需要特殊计算逻辑(如“先乘后加”“多乘区分离”),可继承AttributeCalculator重写计算逻辑:

/// <summary>
/// 多乘区计算器(如攻击加成区分"装备乘区"和"技能乘区")
/// </summary>
public class MultiMultiplierCalculator : AttributeCalculator
{
    public override object CalculateFinalValue(AttributeValue baseValue, List<AttributeModifier> modifiers)
    {
        float baseVal = baseValue.GetValue<float>();
        float additive = 0;
        float equipMulti = 1; // 装备乘区
        float skillMulti = 1; // 技能乘区

        // 分离不同类型的修饰器
        foreach (var mod in modifiers)
        {
            if (mod.Value is not float val) continue;
            if (mod.ModifyType == ModifyType.Add) additive += val;
            else if (mod.Id.StartsWith("Equip_")) equipMulti *= (1 + val); // 装备前缀的乘区
            else if (mod.Id.StartsWith("Skill_")) skillMulti *= (1 + val); // 技能前缀的乘区
        }

        // 最终值 = (基础值 + 加法) * 装备乘区 * 技能乘区
        return (baseVal + additive) * equipMulti * skillMulti;
    }
}
(2)跨实体属性同步(如组队加成)

通过“属性代理”实现一个实体的属性影响其他实体:

/// <summary>
/// 组队加成代理(A的属性影响B)
/// </summary>
public class PartyBuffProxy
{
    private readonly GenericAttributeSystem _sourceSystem; // 源实体(如队长)
    private readonly GenericAttributeSystem _targetSystem; // 目标实体(如队员)
    private AttributeKey _sourceKey; // 源属性(如队长的"领导力")
    private AttributeKey _targetKey; // 目标属性(如队员的"攻击")

    public PartyBuffProxy(GenericAttributeSystem source, GenericAttributeSystem target, 
                          AttributeKey sourceKey, AttributeKey targetKey)
    {
        _sourceSystem = source;
        _targetSystem = target;
        _sourceKey = sourceKey;
        _targetKey = targetKey;

        // 源属性变化时,更新目标的修饰器
        _sourceSystem.OnAttributeChanged += OnSourceAttributeChanged;
        // 初始同步
        OnSourceAttributeChanged(sourceKey, null, _sourceSystem.GetFinalValue<float>(sourceKey));
    }

    private void OnSourceAttributeChanged(AttributeKey key, object oldVal, object newVal)
    {
        if (key != _sourceKey) return;
        float sourceValue = (float)newVal;
        // 移除旧修饰器
        _targetSystem.RemoveModifier("PartyBuff_Attack");
        // 添加新修饰器(如领导力每1点,队员攻击+1%)
        _targetSystem.AddModifier(new AttributeModifier(
            id: "PartyBuff_Attack",
            targetKey: _targetKey,
            modifyType: ModifyType.Multiply,
            value: sourceValue * 0.01f,
            isPermanent: true
        ));
    }
}
(3)数据持久化

通过序列化基础属性和永久修饰器实现存档:

/// <summary>
/// 属性存档数据
/// </summary>
[Serializable]
public class AttributeSaveData
{
    public List<AttributeKeyData> BaseAttributes; // 基础属性
    public List<AttributeModifierData> PermanentModifiers; // 永久修饰器
}

// 序列化时将AttributeKey转换为可序列化的结构(略)
// ...

// 保存与加载
public class AttributePersistence
{
    public AttributeSaveData Save(GenericAttributeSystem system)
    {
        return new AttributeSaveData
        {
            BaseAttributes = system._baseAttributes.Keys.Select(k => new AttributeKeyData(k)).ToList(),
            PermanentModifiers = system._modifiers
                .Where(m => m.IsPermanent)
                .Select(m => new AttributeModifierData(m))
                .ToList()
        };
    }

    public void Load(GenericAttributeSystem system, AttributeSaveData data)
    {
        // 加载基础属性(略)
        // 加载永久修饰器(略)
    }
}

三、通用系统的优势与适用场景

优势:

  1. 零预设属性:无需修改代码即可新增属性(如从“攻击”扩展到“火焰伤害”“击退概率”)。
  2. 规则灵活:通过自定义计算器和修饰器类型,支持任意计算逻辑(如MMO的复杂乘区、roguelike的随机加成)。
  3. 多实体复用:同一套系统可管理角色、NPC、武器、场景Buff(如“领域内所有单位防御+10”)。
  4. 低耦合:外部系统(UI、战斗)仅通过事件和查询接口交互,无需依赖内部实现。

适用场景:

  • 多类型游戏(RPG、MOBA、策略、生存等);
  • 需要频繁新增属性或调整平衡的游戏(如运营期的活动Buff);
  • 支持用户自定义内容的游戏(如Mod、编辑器)。

四、使用示例(角色属性管理)

// 1. 创建属性系统(使用默认计算器)
var playerAttrSystem = new GenericAttributeSystem();

// 2. 注册基础属性
var hpKey = new AttributeKey("HP", typeof(float));
var attackKey = new AttributeKey("Attack", typeof(float));
playerAttrSystem.RegisterBaseAttribute(hpKey, 100f); // 基础HP=100
playerAttrSystem.RegisterBaseAttribute(attackKey, 20f); // 基础攻击=20

// 3. 订阅属性变化事件(UI更新)
playerAttrSystem.OnAttributeChanged += (key, oldVal, newVal) =>
{
    if (key.Name == "HP")
        Debug.Log($"HP变化:{oldVal} → {newVal}");
};

// 4. 添加装备修饰器(永久攻击+5)
playerAttrSystem.AddModifier(new AttributeModifier(
    id: "Sword_001",
    targetKey: attackKey,
    modifyType: ModifyType.Add,
    value: 5f,
    isPermanent: true
));

// 5. 添加技能Buff(10秒内攻击+20%)
playerAttrSystem.AddModifier(new AttributeModifier(
    id: "Skill_Berserk",
    targetKey: attackKey,
    modifyType: ModifyType.Multiply,
    value: 0.2f, // +20%
    duration: 10f
));

// 6. 查询最终攻击(20 + 5)* (1 + 0.2) = 30
float finalAttack = playerAttrSystem.GetFinalValue<float>(attackKey);

总结

通用属性管理系统的核心是**“将属性定义、计算规则、修饰逻辑分离”**,通过抽象层(属性键、修饰器、计算器)实现灵活性。设计时需避免过度工程化——可先基于基础框架实现核心功能,再根据游戏需求扩展(如添加乘区分离、条件判定、网络同步等)。这套架构能支撑从简单到复杂的各种属性场景,大幅降低后期维护和扩展成本。