特点:使用Unity VisualScripting 中自带的NCalc库实现对string类型的复杂数值公式计算。
*可自行拓展,字符串公式中的属性([A.ATK])需要用[ ]包裹,并在Ib_Calculator拓展该属性名的解析。NCalc还支持逻辑运算,如 “Min([B.MAXHP] * 0.1,[A.ATK]*5)“,这是对目标造成其10%的最大生命值,但是最大不超过攻击者攻击力的五倍。详细使用方法可自行了解NCalc的使用。
示例:
如字符串公式 “[A.ATK] * 2 + [B.MAXHP] * 0.1” , 这是造成2倍攻击力,并附加目标10%的最大生命值。
=> result = 10 * 2 + 2000 * 0.1 = 220
void Start()
{
//攻击者属性
Ib_Calculator.NumericBoxForCal attacker = new Ib_Calculator.NumericBoxForCal()
{
MaxHp = 100f,
Hp = 100f,
Atk = 10f,
Def = 10f,
};
//受击者属性
Ib_Calculator.NumericBoxForCal defender = new Ib_Calculator.NumericBoxForCal()
{
MaxHp = 2000f,
Hp = 2000f,
Atk = 10f,
Def = 10f,
};
var result = Ib_Calculator.GetDamageExpressionValue("[A.ATK] * 2 + [B.MAXHP] * 0.1", attacker, defender);
Ib_Log.Info($"计算结果:{result}");
}

源码:
using System;
using System.Collections.Generic;
using System.Data;
using System.Text.RegularExpressions;
using Unity.VisualScripting.Dependencies.NCalc;
using UnityEngine;
namespace Ib_Core
{
/// <summary>
/// 负责数值计算
/// </summary>
public static class Ib_Calculator
{
//数据源
public interface INumericBoxForCal
{
public float MaxHp { get; }
public float Hp { get; }
public float Atk { get; }
public float Def { get; }
}
public struct NumericBoxForCal : INumericBoxForCal
{
public float MaxHp { get; set; }
public float Hp { get; set; }
public float Atk { get; set; }
public float Def { get; set; }
}
public static float GetDamageExpressionValue(string formula, INumericBoxForCal source, INumericBoxForCal target)
{
if (string.IsNullOrEmpty(formula))return 0f;
//如果是纯数字直接运行,否则交给NCalc进行计算
if (EfficientNCalcEvaluator.IsPureNumber(formula) && float.TryParse(formula, out var value)) return value;
return CalculateDamage_NCalc(GetDamageExpression(formula), source, target,formula);
}
#region 解析变量
[System.ThreadStatic] private static INumericBoxForCal _currentSource; // 当前的 a
[System.ThreadStatic] private static INumericBoxForCal _currentTarget; // 当前的 b
/// <summary>
/// 每次计算公式前注入计算数据
/// </summary>
/// <param name="source"></param>
/// <param name="target"></param>
private static void InsertSourceAndTarget(INumericBoxForCal? source, INumericBoxForCal? target)
{
_currentSource = source;
_currentTarget = target;
}
private static float GetNumeric(string name)
{
switch (name)
{
case "A.MAXHP" : return _currentSource?.MaxHp ?? 0f;
case "A.HP" : return _currentSource?.Hp ?? 0f;
case "A.ATK" : return _currentSource?.Atk ?? 0f;
case "A.DEF" : return _currentSource?.Def ?? 0f;
case "B.MAXHP" : return _currentTarget?.MaxHp ?? 0f;
case "B.HP" : return _currentTarget?.Hp ?? 0f;
case "B.ATK" : return _currentTarget?.Atk ?? 0f;
case "B.DEF" : return _currentTarget?.Def ?? 0f;
default: return 0f;
}
}
#endregion
#region 处理伤害公式
//缓存公式的Expression,相同公式复用同一个Expression
public static Dictionary<string, Expression> DamageExpressions { get; private set; } = new(512);
//获取缓存的Expression
public static Expression GetDamageExpression(string formula)
{
if (!DamageExpressions.TryGetValue(formula, out var expression))
{
var newFormula = formula.ToUpper();
// var newFormula = FormulaHelper.WrapVariablesInBrackets(formula.ToUpper());
expression = new Expression(newFormula, EvaluateOptions.IgnoreCase);
expression.EvaluateParameter += (flow, name, args) =>
{
args.Result = GetNumeric(name);
};
if (DamageExpressions.TryAdd(formula, expression))
{
Ib_Log.Info($"载入公式{newFormula}");
}
}
return expression;
}
public static float CalculateDamage_NCalc(Expression damageExpr,INumericBoxForCal attacker, INumericBoxForCal defender,string formula = null)
{
try
{
//注入源数据和目标数据
InsertSourceAndTarget(attacker, defender);
return System.Convert.ToSingle(damageExpr.Evaluate(null));
}
catch (Exception e)
{
Debug.LogError($"计算伤害公式时出错: {e.Message}.[{formula?? "Null"}].");
return 0f;
}
}
#endregion
}
/// <summary>
/// 提供高效的正则表达式计算
/// </summary>
public static class EfficientNCalcEvaluator
{
// 预编译正则表达式以获得最佳性能
private static readonly Regex _isNumberRegex = new Regex(@"^\s*[-+]?(\d+\.?\d*|\.\d+)([eE][-+]?\d+)?\s*$", RegexOptions.Compiled);
public static bool IsPureNumber(string expression)
{
if (string.IsNullOrWhiteSpace(expression))
return false;
return _isNumberRegex.IsMatch(expression);
}
}
}
评论(0)
暂无评论