ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Flame 游戏引擎 Jenny 对话系统随机函数指南:`dice`、`random` 与 `random_range` 的原理与实战

Flame 游戏引擎 Jenny 对话系统随机函数指南:`dice`、`random` 与 `random_range` 的原理与实战 Flame 游戏引擎 Jenny 对话系统随机函数指南dice、random与random_range的原理与实战【免费下载链接】flameA Flutter based game engine.项目地址: https://gitcode.com/GitHub_Trending/fl/flame本篇技术指南聚焦 Flame 游戏引擎内置对话语言系统 Jenny基于 YarnSpinner 语法中三个随机函数dice(n)、random()与random_range(a, b)。文中以 random.md 为骨架结合 packages/flame_jenny 包内的源码实现与单元测试系统讲解每个函数的调用语义、参数约束、截断规则、错误行为、可复现随机源的替换方式以及在游戏叙事掷骰、概率事件、随机掉落中的落地写法。读完本文你将能在自己的 Yarn 脚本中正确、稳妥地使用全部随机能力并能用种子化随机源进行可复现调试。随机函数在 Jenny 中的定位在 Jenny 的表达式系统中函数与任何编程语言或数学中的函数概念一致接收若干参数计算并返回结果调用时函数名后必须紧跟括号即使无参也要写()。Jenny 内置约 20 个函数按用途分为随机、数值、类型转换与杂项四组完整清单见 functions.md其中随机组正是本文要讲的三个函数dice(n)模拟掷骰random()生成[0, 1)均匀随机浮点数random_range(a, b)在自定义闭区间内取随机整数。三者每次求值都会产生不同结果。关键设计在于它们并非各自使用独立的随机源而是统一复用YarnProject.random这个随机数生成器。该字段在 yarn_project.dart 中定义为/// Random number generator used by the dialogue whenever randomization is /// needed. Random random;默认情况下random在YarnProject构造时以Random()初始化无固定种子每次运行结果随机。但你可以替换它为自定义生成器——例如Random(seed)——以实现两件事调试可复现同一脚本同一种子每次跑出完全相同的随机序列便于定位逻辑问题存档一致性防止玩家重载游戏后获得与之前不同的随机结果例如掷骰事件重载后结果变了。这一点在测试中体现得淋漓尽致dice_test.dart与random_range_test.dart均通过YarnProject()..random Random(420)之类的方式注入固定种子从而断言精确的期望输出。dice(n)掷一颗 n 面骰语义与边界规则dice(n)返回1到n之间含两端的随机整数。例如dice(6)等价于掷一颗常规六面骰结果落在 1~6。规则要点参数n必须是数值类型且大于等于 1若n非整数运行时会被截断为整数因此dice(3.5)等价于dice(3)若n截断后小于等于 0运行时抛出DialogueError错误信息为Argument to dice() must be positive: $n参数个数不为 1、或参数不是数值类型则在编译阶段直接报TypeError。基本用法set $roll dice(6) set $coin_flip if(dice(2) 1, H, T)第一行把一次 d6 的结果存入变量$roll第二行用dice(2)模拟硬币1 为正面 H2 为反面 T再配合内置函数if()做条件映射。想实现掷两枚 d6 求和这种经典骰子判定直接相加即可set $roll_2d6 dice(6) dice(6)set指令与if()条件函数的完整说明分别见 set.md 与 misc.md。源码级原理dice的实现位于 dice.dart。其make静态构造器负责编译期校验参数数量必须恰好为 1且args[0].expression.isNumeric必须为真否则调用errorFn报TypeError。真正的随机逻辑在valuegetter 中override num get value { final n _n.value.toInt(); if (n 0) { throw DialogueError(Argument to dice() must be positive: $n); } return _yarn.random.nextInt(n) 1; }可见其工作分三步先把参数toInt()截断再检查是否大于 0不满足抛DialogueError最后借助_yarn.random.nextInt(n) 1得到 1~n 的均匀整数。nextInt(n)本身返回[0, n)加 1 后即得到闭区间[1, n]两端都可达。dice通过 _common.dart 中的builtinFunctions注册表dice: DiceFn.make接入解析器。值得一提的是该注册表注释提示新增内置函数时需同步更新doc/_sphinx/extensions/yarn_lexer.py中的语法高亮词表这保证了文档示例的代码着色与真实函数名保持一致。测试验证dice_test.dart 覆盖了完整的边界情形注入Random(420)后连续四轮{dice(6)}的期望输出依次为3、6 3、2 1 2、1 6 4 3证明序列可复现且分布在 1~6DiceFn(const NumLiteral(3.14), ...)连续取值得到1、1、2、3实证小数参数被截断等价于dice(3)dice(0)触发hasDialogueError(Argument to dice() must be positive: 0)dice()无参、dice(3, 6)多参、dice(three)字符串均在YarnProject.parse阶段报TypeError并给出精确的行列号定位。random()均匀随机浮点数与概率事件语义与用法random()返回一个0到1之间含 0、不含 1的随机浮点数用于实现指定概率触发的事件。典型场景如下if random() 0.001 // This happens only with 0.1% probability You found it! The Holy Grail! endifrandom() p成立的概率恰为p因此只需调整阈值即可精确控制事件出现频率 0.001对应 0.1% 0.25对应 25%依此类推。if指令语法详见 if.md。它同样可以组合进表达式例如set $random random()将本次采样存入变量以便复用同一次事件判定中多次调用random()会得到不同数值务必注意。源码级原理random.dart 的实现极其简洁make只校验不允许携带任何参数传参即编译报错function random() requires no arguments取值逻辑仅一行override num get value _yarn.random.nextDouble();nextDouble()是dart:math.Random的标准 API返回[0, 1)区间内均匀分布的浮点数与文档语义完全一致。因为整段随机逻辑都汇聚在可替换的_yarn.random上只要注入带种子的生成器random()的取值序列同样完全可复现。random_range(a, b)自定义闭区间内的随机整数语义与边界规则random_range(a, b)返回a到b之间含两端的随机整数可看作dice()的泛化版本——用于需要自定义范围的场合不限于从 1 开始。规则要点参数a、b必须为数值类型求值时会各自toInt()截断为整数要求a b否则运行时抛出DialogueError错误信息为In random_range(a$lower, b$upper) the upper bound cannot be less than the lower bound参数个数不为 2、或任一参数非数值均在编译期报TypeError错误信息分别为function random_range() requires two arguments、the first argument should be numeric、the second argument should be numeric。基本用法原文档给出的示例是用它模拟抛硬币0 与 1 等概率set $coin_flip bool(random_range(0, 1))random_range(0, 1)只会产生 0 或 1配合内置类型转换函数bool()规则见 type.md即得到布尔值简洁地完成二分支随机。自定义范围的更多场景random_range(3, 6)模拟四面的 3~6 骰、random_range(0, 100)生成百分比、random_range(-12, 12)生成含负数的偏移量。源码级原理random_range.dart 的valuegetter 清晰地呈现了算法override num get value { final lower _a.value.toInt(); final upper _b.value.toInt(); if (upper lower) { throw DialogueError( In random_range(a$lower, b$upper) the upper bound cannot be less than the lower bound, ); } return _yarn.random.nextInt(upper - lower 1) lower; }其核心是nextInt(upper - lower 1) lower区间宽度upper - lower 1保证了闭区间内每个整数等概率出现包括两端加lower完成偏移。当a b时宽度为 1nextInt(1)恒返回 0函数退化为恒等返回a——这一退化行为在测试中有专门断言。测试验证random_range_test.dart 覆盖注入Random(7)后random_range(3, 6)、random_range(-12, 12)、random_range(0, 100)、random_range(50, 70)的期望输出分别为3、0、57、59验证含负数的范围与正确性100 次random_range(-20, 20)压力测试统计出的min -20、max 20实证区间边界确实可达random_range(0, 0)恒为 0min max的退化情形random_range(10, 0)触发hasDialogueError报错信息与源码完全一致random_range(1)少参、random_range(true, 12)首参非数值、random_range(3, seventeen)次参非数值均抛TypeError并带行列号。编译期检查与运行时错误的分工通过源码与测试可以总结出 Jenny 对随机函数的两道防线这是编写 Yarn 脚本时需要牢记的心智模型错误类别触发条件抛出时机示例TypeError编译期参数个数不符、参数类型非数值、random()携带参数YarnProject.parse()解析阶段附带行列号定位{dice()}、{dice(3, 6)}、{dice(three)}、{random_range(1)}、{random_range(true, 12)}DialogueError运行时数值截断后超出语义边界dice的n 0random_range的upper lower表达式真正求值对话执行时dice(0)、random_range(10, 0)也就是说能在编译期发现的写错数量/类型绝不拖到运行期而涉及动态取值边界的问题例如参数来自变量则留待运行时校验并抛出带上下文的DialogueError。开发时应尽可能保证编译零错误同时在运行时对可能越界的动态参数做好兜底判断。实战组合在游戏对话中安全使用随机掷骰判定与概率分支title: Treasure --- set $roll dice(20) if $roll 15 Your keen eyes spot a hidden lever. (Rolled $roll) else You find nothing but dust. (Rolled $roll) endif 把dice()结果先存入变量再参与多次判定可避免多次采样导致逻辑不一致。可复现调试在初始化YarnProject时注入种子生成器即可让随机序列确定化final project YarnProject()..random Random(2024);随后无论运行多少次dice、random()、random_range的取值序列都完全一致非常适合对齐期望输出、回归测试与排查概率逻辑正式发布时再改回默认的无种子Random()。运行时完整接入方式可参考 jenny_runtime.md。与其他函数的协作随机函数可与数值函数floor、round、int等见 numeric.md、类型转换函数bool、number、string见 type.md自由组合。例如// 在 0~99 中随机取数并四舍五入到十位 set $luck round(random_range(0, 99) / 10) * 10 // 10% 概率的暴击结果存为布尔 set $crit bool(random_range(0, 9) 0)小结dice(n)1~n 闭区间随机整数参数截断且必须 ≥ 1random()[0, 1)均匀浮点数适合random() p的概率事件random_range(a, b)a~b 闭区间随机整数要求a b三者共用YarnProject.random随机源可替换为种子化生成器实现调试可复现与存档一致性参数数量/类型错误在编译期暴露边界越界在运行期抛DialogueError。随机函数是 Jenny 叙事系统中最常用的表达式之一正确理解其截断规则、区间闭包与随机源替换机制能显著提升游戏内随机事件的可控性与可测试性。若需自定义更复杂的行为如加权随机、洗牌可参考 functions.md 中关于用户自定义函数的说明结合 function_storage.md 将游戏引擎能力接入 Yarn 脚本。【免费下载链接】flameA Flutter based game engine.项目地址: https://gitcode.com/GitHub_Trending/fl/flame创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表