尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Flame + Jenny:掌握 YarnSpinner 变量系统(名称、类型、作用域与 `<<declare>>`/`<<local>>`/`<<set>>` 全解析)

Flame + Jenny:掌握 YarnSpinner 变量系统(名称、类型、作用域与 `<<declare>>`/`<<local>>`/`<<set>>` 全解析) Flame Jenny掌握 YarnSpinner 变量系统名称、类型、作用域与declare/local/set全解析【免费下载链接】flameA Flutter based game engine.项目地址: https://gitcode.com/GitHub_Trending/fl/flame导读在基于 Flame 的游戏引擎生态中flame_jenny包内置了jenny库——一个以 Yarn Spinner 为蓝本的非官方移植版对话系统。而**变量variable**正是让对话从固定台词升级为动态剧情的核心设施无论是记录金币数量、玩家姓名、分支选择状态还是在多个节点之间共享游戏状态都依赖变量来存储与传递信息。本篇以 doc/other_modules/jenny/language/expressions/variables.md 为主线结合jenny源码与配套命令文档系统讲解变量名的合法规则、三种内置类型string/number/bool、全局与局部作用域以及声明、初始化、赋值三大命令的完整语法与实战示例。读完你将能独立编写带变量驱动的.yarn对话脚本并理解其底层的存储与类型校验机制。什么是变量名称、值、类型与作用域与任何编程语言一样.yarn脚本中的变量是一个存放某条信息的容器。每个变量都具备四个基本属性名称name在脚本中引用它的标识符值value它当前保存的具体数据类型type决定它能存什么样的数据作用域scope决定它在哪里可以被访问。理解这四点就理解了 YarnSpinner 变量系统的全部核心规则。变量名规则$前缀与合法命名在.yarn脚本中变量的名称是引用它的唯一方式。所有变量名必须以$符号开头其后跟随一个字母或下划线再后面可以是任意数量的字母、数字或下划线。综合正则规则即$[A-Za-z_][A-Za-z0-9_]*。以下都是合法的变量名$i $WARNING $_secret_ $door10 $climbed_over_wall_and_avoided_all_guard_patrols $DoorPassword而以下命名都是非法的$2000_years // $ 后面不能直接跟数字 $[main] // $ 后面不能直接跟 [ 等特殊字符 today // 缺少 $ 前缀注意 前缀在内部用于节点访问计数变量勿与其冲突 victory // 缺少 $ 前缀从源码实现看$符号由分词器在解析阶段识别在 packages/flame_jenny/jenny/lib/src/parse/ascii.dart 中定义了$dollar 0x24这一 ASCII 常量作为词法分析的基础字符集之一用于将$变量名识别为变量 token 而非普通文本。建议命名时遵循可读性原则长变量名如$climbed_over_wall_and_avoided_all_guard_patrols虽然合法但通常用于强调其语义短名称适用于局部临时变量。变量类型string、number与bool每个变量在首次声明时即确定类型此后永不改变。YarnSpinner以及jenny只支持三种变量类型类型可存储内容示例值bool仅true或falsetrue、falsenumber整数或小数0、42、2.5string任意文本the most random number is 4类型由声明语句确定例如// 创建 number 类型变量 $money初始值 100 declare $money 100 // 创建 String 类型变量 $name初始值为 declare $name as String这一设计在运行时也被严格执行在 packages/flame_jenny/jenny/lib/src/variable_storage.dart 的setVariable方法中赋值操作会校验新值与旧值的类型是否一致若试图把bool赋给number变量将抛出DialogueError: Redefinition of variable ... is not allowed编译/运行期错误。也就是说类型的静态性不仅是文档约定更是引擎层面的强制约束。变量的值初始化与更新每个变量在创建时都会被赋予一个初始值之后可以通过 set 命令替换其值——但新值的类型必须与原类型相同。set $money 10 // 将 $money 的值增加 10关于取值的细节表达式的求值规则见 doc/other_modules/jenny/language/expressions/expressions.md表达式总是同步求值不能在求值过程中等待用户输入、跨线程执行耗时计算或随时间推进若确需此类行为应通过用户自定义命令user-defined command在后台计算完成后把结果写入全局变量再由表达式读取。声明全局变量declare编译期执行与位置要求declare 用于创建全局变量并赋予初始值。它与大多数命令有一个关键区别declare在编译期即解析.yarn脚本时执行而不是在对话运行时执行。当对话真正运行时该变量早已初始化完毕因此该命令对运行过程无任何效果。正因为此declare必须写在节点之外、脚本的根层级以明确表达这不是节点运行时执行的命令。典型写法declare $monicker boy --------------- title: Greeting --------------- Teacher: Welcome to the class, {$monicker}! 这里declare引入了名为$monicker的String类型变量初始值为boy。随后它在 Greeting 节点内以行内表达式{$monicker}的形式被使用。值得注意的是到节点运行时该值可能早已被其他节点或游戏本体修改——declare的真正作用是告知 Jenny 这是一个合法变量名且类型是 String。三种语法形式declare支持三种语法分别对应不同的使用场景// 形式一类型由表达式推导 declare $VARIABLE EXPRESSION // 形式二显式声明类型使用默认初始值false / 0 / declare $VARIABLE as TYPE // 形式三显式标注类型同时校验表达式类型一致性 declare $VARIABLE EXPRESSION as TYPE形式一中EXPRESSION可以是字面量也可以是更复杂的表达式在编译期求值以得到初始值变量类型由表达式类型推导得出形式二中TYPE必须是Bool、Number、String之一变量会被初始化为false、0或形式三适合表达式类型不够直观、希望声明更明确的场景编译器会校验EXPRESSION的类型与TYPE是否一致不一致则在编译期报错。综合示例与工程建议declare $prefix Mr. declare $gold 100 declare $been_to_hell false declare $name as String declare $distanceTraveled as Number declare $birthDay randomRange(1, 365) as Number declare $vulgarity GetObscenitySetting() as Bool从源码层面看DeclareCommand类是一个空壳packages/flame_jenny/jenny/lib/src/structure/commands/declare_command.dart 中该类没有保存任何字段其execute直接抛出AssertionError这正是编译期执行、运行时不可见这一设计在代码中的直接体现。两个重要的工程实践集中声明、优先解析官方推荐把所有declare语句放入一个独立的.yarn文件并确保该文件最先被解析从而保证所有全局变量在后续节点使用前都已声明完毕存档恢复时机如果你的游戏支持存档通常也需要保存 Yarn 全局变量的值。恢复存档值必须在所有.yarn脚本解析完成之后进行——否则引擎会认为变量被声明了两次而报错。此外官方建议为每个declare配上文档注释doc-comment说明用途就像为类的公开成员写文档一样// 玩家当前持有的金币数量由 give / take 等自定义命令维护 declare $gold 100创建局部变量locallocal 在当前节点内创建变量并初始化功能与declare类似区别在于其可见范围仅限单个节点。语法为local $VARIABLE EXPRESSION local $VARIABLE EXPRESSION as TYPE第二种形式相当于显式类型标注若表达式类型与TYPE不符编译期即报错。局部变量有两条硬性限制同一节点内每个局部变量只能声明一次局部变量的名字不能与任何全局变量重名。典型应用场景——临时计算结果不值得污染全局命名空间title: a_dice_roll --- local $roll dice(6) if $roll 1 Youve rolled 1, rotten luck... elseif $roll 2 Youve rolled 2, which is still below the average. Try harder! elseif $roll 3 Youve rolled 3.14159265 (well, almost). elseif $roll 4 Your roll is an unlucky number. Please roll again else Youve rolled 10 (when rounded to the nearest ten). Good job! endif 在这个例子里$roll只是掷骰子时的一次性中间结果用local声明是最合适的选择。源码层面LocalCommand在运行时通过storage.setVariable(variable, expression.value)把求值结果写入变量存储见 packages/flame_jenny/jenny/lib/src/structure/commands/local_command.dart——这与set的执行路径完全一致区别仅在于声明时机与作用域约束由解析器在编译阶段完成检查。更新变量值setset 用于更新已存在变量的值。在使用set之前变量必须先通过declare或local声明。set既支持普通赋值也支持复合赋值修改型赋值// 普通赋值 set $VARIABLE EXPRESSION set $VARIABLE to EXPRESSION // 复合赋值 set $VARIABLE EXPRESSION set $VARIABLE - EXPRESSION set $VARIABLE * EXPRESSION set $VARIABLE / EXPRESSION set $VARIABLE % EXPRESSION // 复合赋值等价于 set $VARIABLE $VARIABLE EXPRESSION set $VARIABLE $VARIABLE - EXPRESSION set $VARIABLE $VARIABLE * EXPRESSION set $VARIABLE $VARIABLE / EXPRESSION set $VARIABLE $VARIABLE % EXPRESSION在所有情况下EXPRESSION的类型必须与$VARIABLE的类型一致否则触发编译期错误。一个完整的多选分支示例展示了set在玩家选择中的典型用法declare $favorite_color as String title: ColorQuiz --- What is your favorite color? - White set $favorite_color to White - Red set $favorite_color to Red - Yellow set $favorite_color Yellow - Blue Oh, Nice! Which shade of blue? - Azure - Cerulean - Lapis Lazuli Umm, I dont know how to spell that. Ill just put you down as blue. set $favorite_color Blue - Black set $favorite_color Black Thats mine too! set $affinity 3 - Prefer not to tell Aww... Maybe if I ask again really nicely? jump ColorQuiz 注意第 9 行set $affinity 3这里$affinity必须在别处先用declare声明为number类型复合赋值才能通过类型校验。运行时的SetCommand与LocalCommand共享同一套执行逻辑见 packages/flame_jenny/jenny/lib/src/structure/commands/set_command.dart最终都会落到VariableStorage.setVariable由存储层统一完成类型必须一致的强校验。因此无论脚本写得多花哨底层只有一条铁律变量类型一经声明终身不变。作用域全局与局部YarnSpinner 的变量要么是全局的要么是局部的全局变量通过declare引入一旦创建即可在脚本任意位置访问所有全局变量名全局唯一局部变量通过local创建只能在创建它的节点内部使用。不同节点中可以使用同名的局部变量它们被视为完全不同的变量。declare $global_variable 0 title: MyNode --- local $local_variable 1 底层支撑VariableStorage 的存储与校验jenny的变量系统在运行时由一个VariableStorage统一管理见 packages/flame_jenny/jenny/lib/src/variable_storage.dart。其核心机制值得开发者了解存储结构内部就是MapString, dynamic variables变量名含$前缀作为键类型访问器getBooleanValue/getNumericValue/getStringValue按类型读取getVariableAsExpression会把存储值包装成BooleanVariable/NumericVariable/StringVariable表达式节点供行内插值如{$monicker}与条件判断如if $roll 1使用类型守卫setVariable在写入前检查值必须是String/num/bool三者之一且新旧类型必须一致否则抛DialogueError清理与存档clear()默认保留以前缀开头的节点访问计数变量可用于visited()等函数存档恢复时请注意这一行为。这套设计保证了脚本层的类型静态性与运行时一致性也让游戏侧可以直接通过存储层读写对话状态实现引擎与内容解耦。小结变量是.yarn对话脚本动态化的基石。本篇覆盖了jenny变量系统的完整知识命名$开头后跟字母/下划线再跟字母/数字/下划线类型string、number、bool三选一声明后不可变引擎在VariableStorage.setVariable中强制校验声明declare全局、编译期执行、必须置于根层级、支持三种语法与local节点内临时变量、不可与全局重名赋值set支持/to普通赋值与、-、*、/、%复合赋值。结合 doc/other_modules/jenny/language/language.md 中的其他语言特性if条件、jump跳转、visited()访问统计、行内表达式等变量将把对话从线性台词变成真正可交互、可持久化的游戏叙事系统。若需在游戏中集成对话运行可继续阅读 doc/other_modules/jenny/jenny.md 中关于YarnProject、DialogueRunner、DialogueView的运行时架构说明。【免费下载链接】flameA Flutter based game engine.项目地址: https://gitcode.com/GitHub_Trending/fl/flame创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表