ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

ET 框架 ConditionExpr 条件表达式:将 Excel/Luban 配置编译为行为树运行时判定

ET 框架 ConditionExpr 条件表达式:将 Excel/Luban 配置编译为行为树运行时判定 ET 框架 ConditionExpr 条件表达式将 Excel/Luban 配置编译为行为树运行时判定【免费下载链接】ETUnity3D Client And C# Server Framework项目地址: https://gitcode.com/GitHub_Trending/et/ETcn.etetet.conditionexpr是 ET 框架中用于把 Excel/Luban 表格里填写的条件表达式编译成行为树并在运行时返回条件检查结果成功返回 0失败返回错误码的配置驱动模块。本指南完整讲解其 Luban 字段用法、表达式语法与错误码绑定规则、行为树编译原理词法分析、语法分析、节点映射、变量注册机制、专用条件节点扩展方式以及运行时调用与测试方法读完即可在进入副本、领取奖励、任务接取、功能开启等玩法判定场景中直接落地使用。一、模块定位与典型用途cn.etetet.conditionexpr的核心设计是把策划写在配置表里的可读条件表达式在配置加载时一次性编译为行为树Behavior Tree运行时通过行为树调度器逐节点求值。这样既让策划无需写代码即可表达复杂条件又复用了 ET 行为树成熟的执行与错误码机制。典型业务场景README 列举进入副本条件 领取奖励条件 任务接取条件 功能开启条件这些场景的共同点是条件往往由多个子条件组合而成如“等级达标 且 拥有钥匙或 VIP 达标”并且失败时需要向客户端返回明确的错误码提示。ConditionExpr 恰好把“条件组合”与“错误码”两件事统一进了一个表达式。二、Luban 字段与配置方式2.1 定义的 Bean本包在 Luban 侧定义了一个 beanET.ConditionExpr共三个字段Expr string 条件表达式 ErrorCode int 叶子条件没有显式错误码时使用 Desc string 策划备注不参与运行时判断其中Expr是条件表达式本体ErrorCode是叶子条件未显式绑定错误码时的兜底错误码即 README 示例中的9999Desc仅作策划注释编译与运行都不读取它。2.2 业务表中引用业务表如副本表、任务表中通过 Luban 的 bean 引用语法直接声明该字段##var EnterCondition ##type ET.ConditionExpr ##comment 进入条件配置对象创建时Luban 生成类会调用ConditionExpr.EndInit()见 ConditionExpr.Partial.cs内部执行partial void EndInit() { this.Root ConditionExprCompiler.Compile(this.Expr, this.ErrorCode); }即表达式在配置加载阶段就被编译运行期不再有任何字符串解析开销编译结果保存在ConditionExpr.Root属性类型为ConditionRoot继承自BTRoot见 ConditionRoot.cs。2.3 填写示例Expr (HP 10 : 10001 || MP 100 : 10002) Speed 0 : 10003 ErrorCode 9999 Desc 进入副本条件三、表达式语法3.1 比较运算支持六种比较运算符与数值常量组合成叶子条件 !右侧数值支持负整数词法分析器在-后紧跟数字时会按数字读取见 ConditionExprLexer.cs 的ReadNumber分支。3.2 逻辑运算 || ! ()词法层面对应的 token 类型分别为And、Or、Not、LeftParen、RightParen!只有在不构成!时才被识别为逻辑非。3.3 错误码绑定叶子条件后用冒号绑定该条件失败时返回的错误码HP 10 : 10001若叶子条件未写: errorCode则使用ET.ConditionExpr.ErrorCode兜底HP 103.4 运算优先级! ||优先级由语法分析器ConditionExprParser.cs按ParseOr - ParseAnd - ParseUnary - ParsePrimary的递归下降结构天然保证!优先级最高其次最低||。建议复杂条件显式加括号以避免歧义(HP 10 : 10001 || MP 100 : 10002) Speed 0 : 100033.5 多 owner 变量当BTEnv中有多个 owner如对战双方时用OwnerKey.Variable形式指定从哪个 owner 读取Unit1.HP 0 || Unit2.MP 100解析规则见 ConditionExprParser.cs 的ReadVariable不带.的裸变量ownerKey 默认为ConditionExprEnvKeys.Unit常量值为Unit见 ConditionExprEnvKeys.cs带.的变量.前为 owner key、.后为变量名.出现在开头/结尾或出现多个.都会抛错condition variable reference invalid目标节点必须声明public string OwnerKey字段否则编译表达式时报错SetOwnerKeyField会检查字段存在且类型为string。Unit1、Unit2是调用方传入BTEnv的 owner keyHP、MP仍来自NumericType常量名由BTNumericCompare.OwnerKey字段接收 owner key。3.6 专用节点的括号参数专用条件节点可以使用括号传字符串参数Friend1(HP) 0 Friend2(HP, MP) 100Friend1、Friend2是通过ConditionVariableAttribute注册的节点名HP、MP会按文本写入节点的 publicstring[] Params字段解析见ReadParams以逗号分隔、右括号结束允许空参数列表()。只有声明了Params字段的节点才能使用括号参数HP(Friend1)这类数值节点参数写法会在编译表达式时因节点没有Params字段而报错。四、返回规则与行为树节点映射4.1 返回值约定0 条件成功 非 0 条件失败值为错误码ConditionRoot的运行时入口由 ConditionRootHandler.cs 处理空表达式无子节点直接返回ERR_Success多于一个子节点则抛异常每个表达式编译后只挂一个根子节点否则将求值委托给BTDispatcher.Instance.Handle。4.2 组合节点的短路语义BTSequence对应从左到右执行 遇到第一个失败节点直接返回该节点错误码 全部成功返回 0BTSelector对应||从左到右执行 任意子节点成功返回 0 全部失败时返回第一个失败节点的错误码BTNot对应!子节点失败时! 成功返回 0 子节点成功时! 失败返回 BTNot.ErrorCodeBTNot.ErrorCode默认取表达式的默认错误码ConditionExprParser的ParseUnary中not.ErrorCode this.defaultErrorCode也可用: errorCode单独覆盖。4.3 组合返回示例对表达式(HP 10 : 10001 || MP 100 : 10002) Speed 0 : 10003HP 满足且 Speed 满足 - 0 HP 不满足但 MP 满足且 Speed 满足 - 0 HP 和 MP 都不满足 - 10001 HP 或 MP 满足但 Speed 不满足 - 100034.4 编译期结构优化从 ConditionExprParser.cs 可以看到两个优化细节相邻同类组合会折叠AddSequenceChild/AddSelectorChild在追加子节点时若子节点本身就是同类型的BTSequence/BTSelector会直接展开合并子节点列表避免产生无谓的嵌套层级括号后可以直接跟错误码(cond) : errorCodeApplyErrorCode会把它写入叶子节点或BTNot若括号内是组合节点则抛出condition group error code only support leaf or not node异常从语法层面强制错误码必须绑定在叶子或取反节点上。五、变量注册机制5.1 NumericType 自动注册普通数值变量全部来自NumericType。ConditionVariableRegistry初始化Awake见 ConditionVariableRegistry.cs时会遍历NumericType枚举的所有值把枚举名注册为变量名统一映射到BTNumericCompare节点类型并保存枚举值用于运行时取值扫描带ConditionVariableAttribute特性的类型校验其必须继承BTCondition且变量名非空、不与已有变量重复重复会抛duplicate condition variable。注册结果类似HP - BTNumericCompare, NumericType.HP MP - BTNumericCompare, NumericType.MP Speed - BTNumericCompare, NumericType.Speed因此表达式中出现的变量名必须与NumericType常量名完全一致HP 10 MP 100 Speed 0 Unit1.HP 0 Unit2.MP 100若变量没有注册编译时抛出condition variable not registered: Xxx5.2 注册表为实例存储注意实现细节注册结果保存在ConditionVariableRegistry实例字段variableNodeTypes、numericTypes两个 Dictionary中不是静态字典该类以[CodeProcess]、[AllowInstance]标记并继承SingletonConditionVariableRegistry。解析器在编译时通过ConditionVariableRegistry.Instance访问注册表未初始化时会抛出condition variable registry is not initialized。六、普通数值比较的运行时取值普通数值条件编译成BTNumericCompare字段定义见 BTNumericCompare.csOwnerKey、NumericType、Op、Value、ErrorCode。运行时取值逻辑见 BTNumericCompareHandler.csprotected override int Run(BTNumericCompare node, BTEnv env) { Unit owner env.GetEntityUnit(node.OwnerKey); NumericComponent numericComponent owner.NumericComponent; long left numericComponent.GetAsLong(node.NumericType); return ConditionCompareHelper.Compare(left, node.Op, node.Value) ? ErrorCode.ERR_Success : node.ErrorCode; }对应 README 中的伪代码即Unit unit env.GetEntityUnit(node.OwnerKey); NumericComponent numericComponent unit.GetComponentNumericComponent(); long value numericComponent.GetAsLong(node.NumericType);由此得到两条运行时约束调用方必须在BTEnv中传入表达式使用的 owner key裸变量默认读取env.AddEntity(ConditionExprEnvKeys.Unit, unit)带 owner key 的变量则读取前缀对应的 key如env.AddEntity(Unit1, unit1)、env.AddEntity(Unit2, unit2)当前数值比较 Handler 按Unit契约读取要求对应Unit携带NumericComponent数值取long后与常量比较比较逻辑封装在 ConditionCompareHelper.cs比较操作符枚举ConditionCompareOp见 ConditionCompareOp.cs。七、扩展专用条件节点当某个变量无法从NumericComponent读取如 VIP 等级来自充值系统、好友数量来自社交模块时通过ConditionVariableAttribute注册专用节点。7.1 节点定义示例[ConditionVariable(VipLevel)] public class BTVipLevelCompare : BTCondition { public string[] Params; public ConditionCompareOp Op; public long Value; public int ErrorCode; }特性定义见 ConditionVariableAttribute.cs允许AllowMultiple true即一个类可注册多个变量名。包的测试目录中提供了一个参考实现 Conditionexpr_ParamCompareNode.cs。7.2 注册规则README 原文完整继承专用节点必须继承 BTCondition 如果要接收表达式中的比较符、目标值和错误码需要声明 Op / Value / ErrorCode public 字段 如果要支持 OwnerKey.Variable 语法需要声明 OwnerKey public 字段类型必须是 string 如果要支持 NodeName(Param1, Param2) 语法需要声明 Params public 字段类型必须是 string[] 变量名不能和已有 NumericType 或其它 ConditionVariableAttribute 重复 注册结果保存在 ConditionVariableRegistry 实例字段中不使用静态字典7.3 解析器如何填充专用节点ConditionExprParser.cs 的ParseCompare流程从注册表拿到变量对应的节点类型并Activator.CreateInstance若是BTNumericCompare直接强类型赋值NumericType / Op / Value / ErrorCode并可选写入Params、OwnerKey否则通过反射按名字写Op / Value / ErrorCodeSetCompareField、OwnerKey要求string类型、Params要求string[]类型缺失字段时抛出condition node field not found: ...。所以BTNumericCompare与BTVipLevelCompare是平行关系都继承BTCondition差异仅在 Handler专用节点的 Handler 由你实现负责按业务规则取值并比较。注意字段名必须是Op/Value/ErrorCode/OwnerKey/Params与BTNumericCompare保持一致反射才能命中。八、运行时调用8.1 使用配置中的 Root配置加载完成后直接使用 Luban 对象里的Root字段ConditionExpr condition mapConfig.EnterCondition; BTEnv env BTEnv.Create(scene, unit.Id); try { env.AddEntity(ConditionExprEnvKeys.Unit, unit); int errorCode BTHelper.RunTree(condition.Root, env); if (errorCode ! ErrorCode.ERR_Success) { return errorCode; } } finally { env.Dispose(); }BTHelper.RunTree走行为树调度最终落到ConditionRootHandlerenv.Dispose()负责回收本次求值临时分配的BTEnv。8.2 直接编译字符串也可以绕开配置表在代码中直接编译字符串ConditionRoot root ConditionExprCompiler.Compile( (HP 10 : 10001 || MP 100 : 10002) Speed 0 : 10003, 9999);Compile的完整调用链为见 ConditionExprCompiler.cspublic static ConditionRoot Compile(string expr, int defaultErrorCode 1) { ConditionExprLexer lexer new(expr); ListConditionToken tokens lexer.Tokenize(); ConditionExprParser parser new(tokens, defaultErrorCode); return parser.Parse(); }即Lexer 词法分析 - Parser 递归下降语法分析 - 生成行为树节点默认错误码参数缺省值为 1。这也是EndInit()在配置加载时走的同一条编译路径。8.3 多 owner key 示例ConditionRoot root ConditionExprCompiler.Compile( Unit1.HP 0 || Unit2.MP 100, 9999); BTEnv env BTEnv.Create(scene, unit1.Id); try { env.AddEntity(Unit1, unit1); env.AddEntity(Unit2, unit2); int errorCode BTHelper.RunTree(root, env); } finally { env.Dispose(); }九、测试与验证本包测试位于Packages/cn.etetet.conditionexpr/Scripts/Hotfix/Test测试文件包括Conditionexpr_Compile_Test.csConditionexpr_LubanBean_Test.csConditionexpr_Run_Test.cs以及测试专用节点 Conditionexpr_ParamCompareNode.cs。执行命令dotnet build ET.sln Remove-Item ./Logs -Recurse -Force -ErrorAction SilentlyContinue Test --NameConditionexpr | dotnet ./Bin/ET.App.dll --SceneNameTest测试覆盖点表达式编译结构 Luban bean EndInit 编译 NumericType 注册到 BTNumericCompare 运行时错误码返回 多 owner key 数值读取 专用节点 Params 参数解析十、常见错误速查现象原因与排查condition variable not registered: Xxx变量名拼写错误或未在NumericType/ConditionVariableAttribute中注册condition variable registry is not initialized编译发生在ConditionVariableRegistry初始化之前condition node owner key field not found: ...表达式使用了OwnerKey.Variable但节点未声明public string OwnerKeycondition node params field not found: ...表达式使用了NodeName(...)但节点未声明public string[] Paramscondition node field not found: ...Op专用节点未声明Op / Value / ErrorCodepublic 字段condition group error code only support leaf or not node错误码: xxxx绑定在了组合节点括号包裹的复合条件上duplicate condition variable: Xxx变量名与已有NumericType或其它特性注册名重复运行时报空引用/错误错误码调用方未向BTEnv传入表达式用到的 owner key或对应Unit缺少NumericComponent结语ConditionExpr 是 ET 框架中配置驱动玩法逻辑的关键一环策划用一行可读字符串描述复杂准入条件与失败提示配置加载期经Lexer Parser编译为行为树运行期由BTSequence / BTSelector / BTNot组合短路求值并返回精确错误码。配合NumericType自动注册与ConditionVariableAttribute专用节点扩展无论数值型还是业务定制型条件都能以统一、可测试、无字符串运行时解析的方式接入副本、任务、奖励、功能开关等系统。【免费下载链接】ETUnity3D Client And C# Server Framework项目地址: https://gitcode.com/GitHub_Trending/et/ET创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表