NarraLeaf

不兼容的变更

NarraLeaf-React 各版本间的破坏性变更与不兼容改动,以及各自的迁移方式

完整变更见 CHANGELOG

0.39.2

  • 宿主收到的脚本错误(RuntimeScriptError,通过 GameConfig.onError 拿到)的 message 现在只是那一句话。以前它后面还接着出错动作的 id、类型以及那个动作的整个构造栈,宿主把它显在面板里时,折叠线上方全是打包器的栈帧。现在这些各自成字段 —— error.actionidtype)、error.actionStack,以及仍然给出单字符串形式的 error.composedMessage靠解析 message 字符串来拆这几样东西的宿主要改成读字段。 把错误对象整个打进日志的不会丢任何东西:error.stack 依旧带着完整拼接

0.36.0

  • ThroughColorExposurehold 已弃用,改用 holdMs,单位是毫秒而不是占总时长的比例。没有 holdMshold 仍然被读取,所以不会编译不过
  • 同一个 hold 值现在会比以前保持得久。 那个比例从来就不是它自称的比例:在默认缓动下,标称 30% 的保持实际只占墙上时间的 17.8%,所以全默认的 ThroughColor 跑在 300ms 上时纯色只保持了 53 毫秒。按旧行为调过节奏的转场会明显变慢;改用 holdMs 直接写你想要的长度

0.35.0

  • 对话文本缩放默认开启。 一行先按 fontSize 排,能装下就不动;一旦顶到盒子边缘,每多一个字都会重新测量并把字号降到刚好能装下整行,下限是 autoFitMinFontSize。如果一个游戏的对话框尺寸当初就是按“会溢出”调的,那些行现在会变小而不是溢出。单个框用 autoFit={false} 关掉,全局用 GameConfig.disableTextScaling

0.29.1

  • Camera.resetCamera 在第一帧清除滤镜,不再让滤镜随时长一起缓动退出。姿态仍然缓动。原先靠这段时长让调色随平移一起淡出的场景,现在调色会立即消失

0.29.0

  • 对话正文在横排与竖排下都按严格禁则排版,CJK 行内的拉丁词不再被拆开。断行位置与此前不同:原先会被拆开的词现在整体移到下一行,因此按旧断行量过的对话框可能多出一行。剧本不需要任何改动
  • 严格禁则只在文档声明了语言时生效。承载播放器的页面若没有 lang,得到的是浏览器的默认规则,参见 Dialog

0.27.0

  • 传给 Scene.jumpTo 的过渡跨整个舞台播放,而不再作用于离场场景的背景图。立绘、文字以及其他所有层现在都参与跳转,不会在跳转结束时凭空消失。过渡本身的写法没有任何变化,但它的几何依据变成了舞台:Push 位移的距离是舞台宽度,Mask 铺在舞台矩形上,刻意小于舞台的背景会被一起扫过,而不再是被扫的那一张图。参见 JumpConfig
  • JumpConfig.transition 的类型从 ImageTransition 放宽为 Transition。所有内置过渡两者都满足,因此已有调用不受影响
  • allowSkipBackgroundTransitionallowSkipSceneTransition 取代,默认值为 true。旧的开关从来没有被读取过:背景过渡和其他图像一样受 allowSkipImageTransition 管辖。新开关管的是跳转所播放的舞台过渡。请在 game.configure 调用里改名,参见 GameConfig

0.26.0

  • Camera.reset 现在叫 Camera.resetCamera。每个元素都自带一个内部的 reset()——新游戏开始或读档时引擎运行的生命周期钩子——而 Camera 却把这个名字给了可链式调用的创作辅助方法,于是 newGame() 从不恢复镜头姿态,平移或缩放过的故事会把那个取景带进下一次游玩。请把剧本里所有 camera.reset(...) 改名。 现在在 Camera 上调用 reset() 触及的是生命周期钩子:它立即恢复到配置的姿态,不做任何动画,返回的是 Camera 本身而不是可链式调用的动作
  • LiveGame.undo 不再接受动作 id,并且在无处可退时返回 false 而不再抛出异常。一行由它的令牌通过 restoreToHistory 指定。不带参数的 undo() 照常可用,而且现在读档之后同样有效——这是它此前从未做到过的
  • LiveGame.getHistory 不再包含播放头之前方的行。正常游玩期间没有任何变化;回退之后,当前行之外的那些行改由新的 getFuture 返回。因此用 getHistory 搭起来的回溯界面不会再列出尚未发生的内容
  • LiveGame.restoreToHistory 两个方向都够得着,并且不再丢弃目标行之后的那些行。此前依赖它裁剪回溯的代码,现在会发现那些行仍然可以抵达——移动的是播放头,而不是切断时间线
  • 存档格式 v3。 elementStates 只列出状态与剧本所写不同的元素,读档会在应用存档之前重置每一个元素。v1 与 v2 存档在 0.26.0 中照常读取,但更旧的引擎无法正确读取 v3 存档:它会跳过那次重置,于是存档没有列出的元素会保留运行中的那一局塞给它们的状态。在引擎的动作派发之外写入元素状态的宿主应当调用 element.markDirty(),否则存档不会带上它——在 app.debug: true 时,引擎会对发现的未标记状态发出警告。参见 SavedGame
  • Character 的名字现在会随存档保存。setName 的改动此前会在读档时被静默丢失;在角色揭晓之后存档,读回来仍然是「???」。更早版本写出的存档不含角色条目,仍可照常读取,读回后保留剧本所写的名字。由于角色现在也会占用存档条目,需要恢复存档的应用应当通过 DevTools.setElementStaticId 给自己的角色阵容命名
  • Layer 不再把自己的姿态带出声明它的那个场景。离开场景现在会重置该场景摆上舞台的图层——配置的 z-index 与姿态——而不只是重置站在图层上的可显示元素;newGame() 同样如此。此前依赖图层在后续场景中保持移开或淡出状态的剧情,现在需要重新设置。故事镜头是例外
  • 点击舞台会推进 ADV 对话,此前只有跳过键才会。点击会把正在打字的一行补完,并推进已经打完的一行;它从不「强制」,因此不会像按住跳过键那样一路跑完整个场景。此前自行基于 event:state.player.stageClick 实现点击推进的应用,现在一次点击会推进两行

0.23.0

  • Soundtype 现在是 SoundBusIdSoundType | (string & {})),不再是 SoundTypeSoundType 没有改动,依然导出,三个取值的含义也完全一致,因此只用这三个值的代码无需改动——但如果代码对音频的 type 做穷举 switch、依赖它只有三个成员,现在需要补上 default 分支
  • Sound.voice()Sound.bgm()Sound.sound() 现在为 type 提供默认值,而不是强制覆盖。Sound.voice({src, type: "alice"}) 此前会悄悄产生一段 voice 音频,现在产生的是 alice 总线上的音频。此前向工厂方法传入 type 并依赖它被忽略的代码,现在会真正生效
  • Preference 会复制传给它的默认值对象,而不是直接写入其中。同一进程中的两个 Game 不再共用同一个设置对象,拖动滑条也不再改写此后整个进程的 Game.DefaultPreference。刻意去修改那个共享对象的宿主,现在必须改走 game.preference

0.22.0

  • LiveGame.playSound 以及对话行的语音,现在以 Sound 配置的音量开始播放,而不是满音量。Sound.voice({src, volume: 0.4}) 经这两条路径播放时,此前听起来是 1,现在是 0.4。显式传入目标音量的调用方不受影响
  • bgm 类型的音频调用 Sound.play() 不再抛出 StaticScriptWarning,而是打印一条 console.warn 并正常播放。此前依靠这次抛出在构建链时明确失败的剧情,现在会照常运行

0.20.0

  • Chained* 系列类型别名——ChainedControlChainedPersistent 及其同类——已被移除。它们本就是 @internal,从未出现在产出的声明文件中;此前使用它们的公开签名现在直接写出所代表的链式类型:Control.do() 返回 Proxied<Control, Chained<LogicAction.Actions>>Persistent.set() 返回 Proxied<Persistent<T>, Chained<LogicAction.Actions>>。没有任何签名的形状发生变化,运行时行为也没有变动

0.19.1

  • StackFrameSnapshot.branches 现在是 StackSnapshot[],不再是「帧数组的数组」,因此一个分支会完整地到达——连同它的 looptag——而不是被削减成它的帧。branches[i][0] 变成 branches[i].frames[0]。这个类型是实验性且只读的,所以选择直接纠正它,而不是另开一个平行字段

0.17.1

  • fastForwardreason 新增了 "stalled"。既有取值的运行时行为不变,但返回类型变宽了:对 reason 做穷举 switch 的代码需要补上这个分支才能通过编译。忽略返回值的宿主不受影响
  • 快进现在可能因为一个无法跳过的步骤而提前结束。每个挂起的行有 options.stepTimeout 毫秒(默认 10000)的时间去结算;超出该时限的步骤会以 "stalled" 结束本次快进,而不是继续越过它。此前快进会停在那里、返回的 Promise 两边都不结算,因此现在能正常工作的用法不会失效——但把「非 "menu" 即成功」当依据的宿主,现在需要把 "stalled" 区分出来。如果剧情会快进穿过较长的、无法跳过的媒体,请调高 stepTimeout

0.17.0

  • onPreloadCompleteoncePreloadCompletewhenPreloadComplete()event:preloaded.complete 现在都在进入游戏之前触发——此时菜单可能还在屏幕上——而不是在 newGame() 挂载出场景之后。入口场景在故事被加载时就已被注册为预加载场景,这正是本次发布的目的。这不会导致任何编译失败;只是回调运行的时刻变了
    • 如果你用它来为加载步骤把关,它现在只会把这件事做得更好,无需改动
    • 如果你用它来表示**「游戏内容已经上屏」**,请改用 onFirstSceneReady / whenFirstSceneReady()。这两者没有变化,仍然要求存在一个真正挂载的场景
    • 详见预加载

0.16.0

过渡 API 围绕两个理念进行了重构:转场方式(引擎)使用 new 实例化并只接受一个选项对象,而它们播放的遮罩图案Mask 上的静态工厂方法构建,通过 pattern 选项传入

  • DissolveFadeIn 不再接受位置参数
    • new Dissolve(1000, easing)new Dissolve({duration: 1000, easing})
    • new FadeIn(500, [x, y], easing)new FadeIn({duration: 500, offset: [x, y], easing}) —— 注意参数从 startPos 改名为 offset
  • SoftWipeSoftIrisBlinds 已移除,请使用 Reveal 搭配对应的图案
    • new SoftWipe({duration, direction, feather})new Reveal({duration, pattern: Mask.wipe({direction, feather})})
    • new SoftIris({duration, center, feather})new Reveal({duration, pattern: Mask.iris({center, feather})})
    • new Blinds({duration, orientation, slats})new Reveal({duration, pattern: Mask.blinds({orientation, slats})})
  • MaskTransition 已移除,其硬边裁剪等价于使用零羽化图案的 Reveal
    • MaskTransition.circle({duration, center})new Reveal({duration, pattern: Mask.iris({center, feather: 0})})
    • MaskTransition.wipe({duration, direction})new Reveal({duration, pattern: Mask.wipe({direction, feather: 0})})
  • ThroughColor 的静态工厂方法已移除,现在直接用 new 构造,几何形状通过 pattern 传入
    • ThroughColor.fade({duration, color, hold})new ThroughColor({duration, color, hold})
    • ThroughColor.wipe({direction, feather, ...rest})new ThroughColor({...rest, pattern: Mask.wipe({direction, feather})})
    • ThroughColor.blinds({orientation, slats, ...rest})new ThroughColor({...rest, pattern: Mask.blinds({orientation, slats})})
    • ThroughColor.iris({center, feather, ...rest})new ThroughColor({...rest, pattern: Mask.iris({center, feather}), inverted: true}) —— 注意 inverted: true:经典的圈入到黑是从四周向中心收拢,即该图案的反转方向
  • 移除的选项类型:SoftWipeOptionsSoftIrisOptionsBlindsOptionsMaskTransitionCircleOptionsMaskTransitionWipeOptionsThroughColorFadeOptionsThroughColorWipeOptionsThroughColorBlindsOptionsThroughColorIrisOptions。新导出的类型:DissolveOptionsFadeInOptionsRevealOptionsThroughColorOptionsThroughColorUncoverMaskPattern,以及各工厂方法对应的 *PatternOptions 类型

0.13.0

  • SavedGamestore 现声明为 SerializedNamespaceData,不再是 StorableData。它携带的一直是带标签的形式,错的只是声明。存档格式本身没有变化,已有存档仍可正常读取 —— 但如果你的代码直接读取 savedGame.game.store 并依赖旧的声明,现在会看到它一直以来实际拿到的带标签形状
  • 0.12.x 及更早版本写出的存档重新可以正确读取,但有一个例外:在受影响的版本上读档后再存档产生的存档,其中的值在磁盘上被打了两层标签,而这无法与游戏真正存入的值区分开。此类存档不会被修复
  • image.darken(darkness, duration) 现在会在 duration 内播放动画,而不再立即生效。此前只有同时传入缓动函数时动画才会运行,否则 duration 会被静默丢弃。若需要旧的瞬间生效行为,请传入 0 作为 duration

0.9.0

  • JumpConfig.unloadScene 已移除

0.8.0

  • Displayable.scale 方法已变更。现在接受 scaleXscaleY 两个参数,不再使用单个 scale 参数

0.7.0

  • Router 已弃用,请使用更完整的 LayoutRouter
  • Page 已重构
  • game.config.skipKeygame.config.nextKey 已弃用,请使用 game.keyMap

0.6.0

  • 游戏执行模型从单节点 currentAction 切换为 StackModel。它支持:
    • 显式处理 Awaitable
    • 子栈递归调用
    • 完整序列化与反序列化
    • 在场景操作中中断调用栈
    • 更好的分支与合并
    • 反序列化和撤销时减少状态混乱
    • 更完整地处理嵌套操作
  • game.config.skipInterval 已弃用,请使用 GamePreference.skipInterval

0.5.0

  • game.config.cps 已弃用,请使用 GamePreference.cps
  • 菜单的 GameElementHistory.selected 可能为 null

0.4.0

  • game.config.elements.say.textInterval 已弃用,请使用 game.config.elements.say.cps
  • game.config 已重构,详情见 GameConfig
    • game.config.player 已弃用,请使用 game.config
    • game.config.elements 已弃用,请使用 game.config
    • game.config.elementStyles 已弃用,请使用 game.config

0.3.0

  • NarraLeaf-React 现在要求 React 19 或更高版本
  • Image 配置已变更:
    • config.src 应为标签定义或字符串
    • 标签式图片配置中,作为解析器函数的 config.src 移到 config.src.resolve
    • 图片不能再标记为 wearable,请使用 image.wearimage.asWearableOf
  • Image 方法变更:
    • setAppearancesetTagssetSrc 改为 char
    • applyTransform 改为 transform
    • wearaddWearable 的新别名
    • asWearableOfbindWearable 的新别名
    • initsetPositiondisposecopy 已移除
    • IImageTransition 已移除,请使用 ImageTransition
    • Fade 已移除,请使用 Dissolve,它接受相同的持续时间和缓动参数
  • Text 方法变更:
    • applyTransform 改为 transform
    • applyTransition 已移除
    • ITextTransition 已移除,请使用 TextTransition
  • Transform 方法变更:
    • overwrite 已移除
    • Transformer API 已完全弃用
  • Scene 方法和属性变更:
    • activatedeactivate 已移除,场景生命周期由游戏自动管理
    • applyTransform 已移除,请使用 scene.background.transform
    • inherit 已移除
    • requestImagePreload 改为 preloadImage
  • Sound 方法变更:
    • 使用 copy 创建新的声音实例
    • playstopsetVolume 可接收 duration 参数
    • fade 已移除,请使用 setVolume
  • 可显示元素中,变换状态已从元素状态中分离
  • Sound 配置变更:
    • synctype 已移除
    • 使用 preload 启用 Howler.js 的预加载
    • 使用 seek 设置初始播放位置
  • Scene 配置不能再指定 invertYinvertX,请使用 story 配置中的 origin
  • TopCenterBottomHBoxVBox 已弃用,请使用 PageRouter API
  • 所有 ITransition 已弃用,请使用 Transition API
    • FontSizeTransition 改为 FontSize
    • BaseImageTransition 改为 ImageTransition
    • BaseTextTransition 改为 TextTransition

0.2.2

  • SceneConfig.invertY 默认值现在是 true

0.2.0

  • Image 构造函数签名已变更。第一个参数必须是配置对象

0.1.2

  • game.config.player.widthgame.config.player.height 不能再是字符串

本页目录