mirror of
https://gitee.com/jisol/jisol-game/
synced 2025-06-26 11:24:46 +00:00
218 lines
8.7 KiB
C#
218 lines
8.7 KiB
C#
using System;
|
||
|
||
namespace GAS.Runtime
|
||
{
|
||
/// <summary>
|
||
/// 复合属性
|
||
/// <para>
|
||
/// 计算公式:
|
||
/// 最终值 = (基础值 + 基础附加值) * (1 + 累加加成) * 累乘加成 * (1 - 最大值惩罚)
|
||
/// </para>
|
||
/// </summary>
|
||
public sealed class CompositeAttribute
|
||
{
|
||
public float Value { get; private set; }
|
||
public float MinValue { get; private set; }
|
||
public float MaxValue { get; private set; }
|
||
|
||
public delegate float OnPreValueChange(CompositeAttribute compositeAttribute, float oldValue, float newValue);
|
||
|
||
public delegate void OnPostValueChange(CompositeAttribute compositeAttribute, float oldValue, float newValue);
|
||
|
||
private OnPreValueChange _onPreValueChange;
|
||
private event OnPostValueChange _onPostValueChange;
|
||
|
||
public AbilitySystemComponent Owner { get; private set; }
|
||
|
||
public AttributeBase Base { get; }
|
||
public AttributeBase BaseAdditiveBonus { get; }
|
||
public AttributeBase AdditiveBonus { get; }
|
||
public AttributeBase MultiplicativeBonus { get; }
|
||
public AttributeBase MaxValuePenalty { get; }
|
||
|
||
/// <summary>
|
||
/// 计算公式:
|
||
/// 最终值 = (基础值 + 基础附加值) * (1 + 累加加成) * 累乘加成 * (1 - 最大值惩罚)
|
||
/// </summary>
|
||
/// <param name="base">基础值</param>
|
||
/// <param name="baseAdditiveBonus">基础附加值:默认0,应使用加/减法来修改。
|
||
/// 一般用于增加固定数值的装备加成: 比如增加10点攻击力。
|
||
/// 参考英雄联盟中的额外攻击力,若不需要区分基础值和基础附加值,无视此参数。
|
||
/// </param>
|
||
/// <param name="additiveBonus">累加加成:默认0,应使用加/减法来修改,25%加成对应的修改参数应该为0.25。
|
||
/// 将所有的累加加成求和之后作为一个加成值(1 + a1 + a2 + ... + an), 它们的收益是会"稀释"的。
|
||
/// 一般用于增加百分比的装备加成: 比如增加25%伤害。
|
||
/// </param>
|
||
/// <param name="multiplicativeBonus">累乘加成:默认1(需要你自己设置),应使用乘法(除法本质上可以, 但是不推荐, 除法看起来会比较绕, 而且除法性能上不如乘法)来修改,25%加成对应的修改参数应该为1.25。
|
||
/// 每个技能的加成值都以乘法来计算(m1 * m2 * ... * mn), 它们的收益不会被"稀释",因此累乘加成效果很强大,但同时也是游戏后期数值容易爆炸的根源! 具体使用累加还是累乘取决于设定。
|
||
/// 一般用于技能/天赋/被动加成: 比如增加5%总伤害(有些游戏会用"总增"这个词来描述此类效果)。
|
||
/// </param>
|
||
/// <param name="maxValuePenalty">最大值惩罚:默认0, 取值范围一般为[0~1]. 注意:25%惩罚对应的修改参数应该为0.25,而不是-0.25。
|
||
/// 仅在需要惩罚取最大值时使用,参考英雄联盟中的减少移动速度效果, 当被多个debuff减少移动速度时, 只适用减速幅度最大的效果。
|
||
/// 该属性应该是MaxValueOnly的,即只有最大值才会生效,不会叠加。
|
||
/// </param>
|
||
/// <param name="minValue">最小值</param>
|
||
/// <param name="maxValue">最大值</param>
|
||
public CompositeAttribute(
|
||
AttributeBase @base,
|
||
AttributeBase baseAdditiveBonus = null,
|
||
AttributeBase additiveBonus = null,
|
||
AttributeBase multiplicativeBonus = null,
|
||
AttributeBase maxValuePenalty = null,
|
||
float minValue = float.MinValue,
|
||
float maxValue = float.MaxValue)
|
||
{
|
||
Base = @base;
|
||
BaseAdditiveBonus = baseAdditiveBonus;
|
||
AdditiveBonus = additiveBonus;
|
||
MultiplicativeBonus = multiplicativeBonus;
|
||
MaxValuePenalty = maxValuePenalty;
|
||
|
||
MinValue = minValue;
|
||
MaxValue = maxValue;
|
||
|
||
Value = CalculateValue();
|
||
|
||
RegisterAttributeChangedListen();
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
_onPreValueChange = null;
|
||
_onPostValueChange = null;
|
||
UnRegisterAttributeChangedListen();
|
||
}
|
||
|
||
public void SetOwner(AbilitySystemComponent owner)
|
||
{
|
||
Owner = owner;
|
||
|
||
Base.SetOwner(owner);
|
||
BaseAdditiveBonus?.SetOwner(owner);
|
||
AdditiveBonus?.SetOwner(owner);
|
||
MultiplicativeBonus?.SetOwner(owner);
|
||
MaxValuePenalty?.SetOwner(owner);
|
||
}
|
||
|
||
public void Init(float baseValue) => Base.Init(baseValue);
|
||
|
||
public void SetBaseValue(float baseValue) => Base.SetBaseValue(baseValue);
|
||
|
||
public void SetMinValue(float minValue)
|
||
{
|
||
MinValue = minValue;
|
||
Value = CalculateValue();
|
||
}
|
||
|
||
public void SetMaxValue(float maxValue)
|
||
{
|
||
MaxValue = maxValue;
|
||
Value = CalculateValue();
|
||
}
|
||
|
||
public void SetMinMaxValue(float minValue, float maxValue)
|
||
{
|
||
MinValue = minValue;
|
||
MaxValue = maxValue;
|
||
Value = CalculateValue();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 可以使用这个来做自定义的伤害钳制函数, 甚至当成自定义的数值计算函数
|
||
/// 参考英雄联盟中移动速度的计算, 当移动速度超过或低于一定数值会被修正: https://leagueoflegends.fandom.com/wiki/Movement_speed
|
||
/// </summary>
|
||
public void SetPreValueChangeCallback(OnPreValueChange action)
|
||
{
|
||
_onPreValueChange = action;
|
||
}
|
||
|
||
public void RegisterPostValueChange(OnPostValueChange action)
|
||
{
|
||
_onPostValueChange += action;
|
||
}
|
||
|
||
public void UnregisterPostValueChange(OnPostValueChange action)
|
||
{
|
||
_onPostValueChange -= action;
|
||
}
|
||
|
||
private void RegisterAttributeChangedListen()
|
||
{
|
||
Base.RegisterPostCurrentValueChange(OnAttributeChanged);
|
||
BaseAdditiveBonus?.RegisterPostCurrentValueChange(OnAttributeChanged);
|
||
AdditiveBonus?.RegisterPostCurrentValueChange(OnAttributeChanged);
|
||
MultiplicativeBonus?.RegisterPostCurrentValueChange(OnAttributeChanged);
|
||
MaxValuePenalty?.RegisterPostCurrentValueChange(OnAttributeChanged);
|
||
}
|
||
|
||
private void UnRegisterAttributeChangedListen()
|
||
{
|
||
Base.UnregisterPostCurrentValueChange(OnAttributeChanged);
|
||
BaseAdditiveBonus?.UnregisterPostCurrentValueChange(OnAttributeChanged);
|
||
AdditiveBonus?.UnregisterPostCurrentValueChange(OnAttributeChanged);
|
||
MultiplicativeBonus?.UnregisterPostCurrentValueChange(OnAttributeChanged);
|
||
MaxValuePenalty?.UnregisterPostCurrentValueChange(OnAttributeChanged);
|
||
}
|
||
|
||
private void OnAttributeChanged(AttributeBase attribute, float attrOldValue, float attrNewValue)
|
||
{
|
||
var oldValue = Value;
|
||
var newValue = CalculateValue();
|
||
|
||
newValue = Math.Clamp(newValue, MinValue, MaxValue);
|
||
if (_onPreValueChange != null)
|
||
{
|
||
newValue = _onPreValueChange(this, oldValue, newValue);
|
||
}
|
||
|
||
if (Math.Abs(oldValue - newValue) < 0.000001f)
|
||
{
|
||
return;
|
||
}
|
||
|
||
Value = newValue;
|
||
_onPostValueChange?.Invoke(this, oldValue, newValue);
|
||
}
|
||
|
||
private float CalculateValue()
|
||
{
|
||
return CalculateTotalValue(Base, BaseAdditiveBonus, AdditiveBonus, MultiplicativeBonus, MaxValuePenalty);
|
||
}
|
||
|
||
public static float CalculateTotalValue(
|
||
AttributeBase @base,
|
||
AttributeBase baseAdditiveBonus = null,
|
||
AttributeBase additiveBonus = null,
|
||
AttributeBase multiplicativeBonus = null,
|
||
AttributeBase maxValuePenalty = null)
|
||
{
|
||
var totalValue = @base.CurrentValue;
|
||
|
||
if (baseAdditiveBonus != null)
|
||
{
|
||
totalValue += baseAdditiveBonus.CurrentValue;
|
||
}
|
||
|
||
// 如果值为0, 计算就没有意义了, 可以忽略
|
||
if (totalValue != 0)
|
||
{
|
||
if (additiveBonus != null)
|
||
{
|
||
totalValue *= 1 + additiveBonus.CurrentValue;
|
||
}
|
||
|
||
if (multiplicativeBonus != null)
|
||
{
|
||
totalValue *= multiplicativeBonus.CurrentValue;
|
||
}
|
||
|
||
if (maxValuePenalty != null)
|
||
{
|
||
totalValue *= 1 - maxValuePenalty.CurrentValue;
|
||
}
|
||
}
|
||
|
||
return totalValue;
|
||
}
|
||
}
|
||
} |