事件
启动一张图的事件 Head、它们各自携带的 payload,以及在蓝图之间传递消息的 Broadcast 节点
事件 Head 是一张图的起点。它没有执行入口——运行时从 Head 进入这张图,执行链从then开始。本页其余内容都由这一点展开:Head 的数据输出就是这次事件的 payload,而你能用到哪些 Head,取决于蓝图挂在什么宿主上
约定
- Head 没有执行入口。 没有任何东西可以连进去。一张图里可以放多个 Head,各自的事件到达时各跑各的执行链。监听同一事件的所有 Head,不论在一张图还是多张图里,都在事件到达时同时开始,彼此不等待,其中一个出错也不会让其余的停下。游戏等待某个事件时(例如启动时等待
On Game Ready),等待的是全部 Head - 数据输出就是 payload。 除
then以外的每个输出引脚,都按引脚 id 读取本次运行时事件 payload 的一个字段。派发没有设置的字段读到null——不是0,也不是"" - 坐标是设计坐标。 元素的鼠标 Head 使用该元素自己的本地设计坐标系;因事件从后代元素向上传递而执行的 Head 也是如此,它读到的是这次按下在自己元素框内的位置,而不是在指针下那个元素框内的位置;放在 Surface 蓝图上的
Mouse Click和Right Click则使用该 Surface 的设计坐标系 - Blueprint Value 的 Head。 Blueprint Value 图只接受两个 Head:
Init和On Flush - 不可交互的元素不派发。 控件处于禁用或文本编辑等不可交互状态时,完全不派发它的交互 Head
- 按键 Head 与手柄 Head 在创建浮窗中各合为一组。 浏览创建浮窗时,键盘下的四个 Head 合为一行按键时,手柄下的四个 Head 合为另一行手柄按键时。搜索时每个 Head 按自己的名称列出;已放置的卡片可用标题旁的 ▾ 切换为这四个中的另一个。见同类节点
哪些 Head 可用
创建浮窗按蓝图 owner 过滤 Head。没有为你的 owner 列出的 Head,不是被某个开关藏起来了——在那里它根本不存在
| 宿主 | 提供的 Head |
|---|---|
应用逻辑(globalMain) | App Boot、On Game Ready、On Fullscreen Changed、On Window Focus Changed、On Window Close Requested、On Action、四个键盘 Head、四个手柄 Head、两个 preference Head |
Surface 蓝图(surfaceMain) | Surface Init、Surface Unmount、Before Surface Exit、After Surface Enter、Mouse Click、Right Click、On Fullscreen Changed、On Window Focus Changed、On Window Close Requested、On Action、四个键盘 Head、四个手柄 Head、两个 preference Head、两个 broadcast Head、Element Flush、Element Click |
控件私有蓝图(widgetMain) | 由该控件类型声明的能力决定,见下表 |
组件定义内的控件(componentWidgetMain) | Init、On Flush、Unmount、Before Surface Exit、After Surface Enter,以及下表中该控件类型声明的 Head。没有键盘、手柄、窗口、广播和绑定元素的 Head |
Blueprint Value(widgetValue) | Init与On Flush |
| Story Action 蓝图 | On Call |
对控件来说,owner 只是规则的一半:每种内建控件都声明了一组事件能力,只有该控件自己列出的 Head 才会出现。这就是为什么nl.list没有Mouse Enter,而nl.container没有Item Click
| 控件类型 | Head |
|---|---|
| 所有支持私有蓝图的控件 | Init、On Flush、Unmount、Before Surface Exit、After Surface Enter、四个键盘 Head、四个手柄 Head、On Fullscreen Changed、On Window Focus Changed、On Broadcast、On Any Broadcast、Element Flush、Element Click |
nl.container、nl.text、nl.image、nl.button、nl.dialog.sentence、nl.nvl.texts | 九个鼠标 Head、Focus、Blur、List Item Refresh |
nl.video | 九个鼠标 Head、Focus、Blur、Playback Ended |
nl.puppet | 九个鼠标 Head、Focus、Blur |
nl.switch | 九个鼠标 Head、Focus、Blur、Changed、Turned On、Turned Off |
nl.list、nl.notification.list、nl.choice.list、nl.nvl.list | Scroll、Scroll End、Item Render、Item Click、Item Hover、Selection Changed |
nl.slider | Drag Start、Value Changed、Drag End |
nl.textInput | Value Changed、Submit、Focus、Blur |
nl.frame | Page Event |
控件蓝图内部还有一层收窄:List Item Refresh只有当元素确实位于 List 的 item 模板之内时才提供
事件冒泡
有六个指针事件会沿元素树向上传递:Mouse Click、Mouse Double Click、Mouse Down、Mouse Up、Mouse Wheel和Right Click。事件从指针下的那个元素开始,依次经过每一层父元素,一直到该元素所在 Surface 的根元素。路径上每个拥有该事件 Head 的元素都会执行它,没有 Head 的元素被直接跳过。拥有 Head 的元素不会把事件留下,它的祖先的 Head 同样会执行。这些 Head 各自执行自己的链,所以不要让图依赖它们之中谁先执行
- 不向上传递的事件。
Mouse Enter、Mouse Leave和Mouse Move只留在指针所在的元素上。键盘、焦点、flush 和生命周期事件同样不向上传递,控件自己的事件(Item Click、Value Changed、Changed、Submit等)也不会 - 没有鼠标 Head 的控件。 List、Slider、文本输入框和 Frame 不提供鼠标 Head,会像任何没有 Head 的元素一样把这六个事件交给父元素,所以带
Mouse WheelHead 的容器能收到落在其内部 List 上的滚轮 - 离开 List。 在 item 模板内部,Head 针对指针所在的那一行执行。事件向上离开 List 时,那一行就被留下了:List 之上的元素使用自己的变量,也读不到条目数据
- 离开组件。 事件到达组件的根元素后,从摆放该组件的那个元素继续向上,所以容纳组件的容器能收到组件上的点击
- Surface。 到达 Surface 的点击还会执行 Surface 蓝图自己的
Mouse Click或Right Click - Frame 中的 Page。 落在 Frame 所显示 Page 的元素上的点击,向上传递到该 Page 的根元素为止:Frame 及其周围的元素收不到它。被显示的 Page 先以自己的设计坐标执行自己 Surface 的
Mouse Click或Right Click,随后持有该 Frame 的 Surface 执行它自己的。落在被显示 Page 空白处的点击属于 Frame,并从 Frame 开始向上传递
图无法提前结束这次传递,脚本图层可以,用ctx.stopPropagation():上层元素收不到这次事件,同一元素上的其他 Head 照常执行。同一元素上的 Head 同时开始,全部结束后事件才传到父元素。曾经用来控制冒泡的两个节点Continue Event Bubble与Stop Event Bubble已经被删除。它们真正被稳定用到的那一件事,也就是把窗口关闭挂住,现在由 App 分类下的 Keep Window Open 负责,它只做这一件事
应用逻辑没有鼠标 Head。它通过 On Action 收到指针输入,不论手势落在哪里,包括没有任何界面接收的地方:Page 只在绘有它的元素的地方接收点击,所以点在 Page 的空白处、或点在空无一物的舞台上,仍然会送达应用逻辑的On Action Head
Page 或图层播放退出动画期间,没有任何 Page 或图层接收的点击会被丢弃。它到不了游戏舞台和舞台上的游戏界面,因此不会推进剧情,应用逻辑也收不到。点在取而代之、正在进场的 Page 上的点击照常送达那个 Page。Frame 切换 Page 期间同理:进场 Page 的元素照常响应点击,而任何点击都不会越过 Frame,因此不会变成 Frame、它周围的元素或 Surface 上的点击
焦点位于文本输入元素时,按键不会送达任何蓝图:应用逻辑、Surface 和控件的键盘 Head 都不执行,也不会触发任何输入意图,所以输入名字时按 Space 不会推进对话、按 Escape 不会打开菜单。该输入框自己的Value Changed和Submit仍然照常触发
应用生命周期
应用逻辑的入口,外加三个同样能到达 Surface 的窗口事件
App Boot
blueprint.event.head.appBoot
App Boot(应用启动)在 UI 运行时完成启动、并拥有可执行的应用逻辑蓝图时触发一次
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
On Game Ready
blueprint.event.head.gameReady
On Game Ready(游戏就绪时)在每个被接受的 NarraLeaf session 触发一次:此时LiveGame对象已创建并存入 Studio runtime,但liveGame.newGame()还没有启动第一段剧情
运行时把 NarraLeaf 环境初始化当作一个加载项,放在 Surface 系统启动之前:它以 Story 库默认 Story 的入口场景的 compiled story 挂载 Player,因此LiveGame已经存在、该场景资源开始预热,而玩家仍停留在主菜单。真正「进入游戏」只发生在Start Game或读档时,并且复用同一个已初始化的LiveGame:当Start Game的目标就是已预热的默认场景时为秒开,不会再次触发该 Head;只有Start Game指向不同场景时才会重新挂载环境并第二次触发。项目未配置默认 Story 时改为挂载一个空环境,该 Head 在启动时依然触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
把 NarraLeaf Preference 的初始化放在这里——Set Auto Forward、Set Game Speed、Set Voice Volume、Set Sentence Speed——这样它们在玩家进入游戏之前就已就绪。不要放在App Boot里:名字虽然像最早的钩子,它并不最先执行(On Game Ready先于它触发),而且它不保证此刻存在活动的 live game
On Fullscreen Changed
blueprint.event.head.fullscreenChanged
On Fullscreen Changed(全屏状态变更时)在应用窗口进入或退出全屏时触发,包括在游戏之外切换全屏的情况。它先送达应用逻辑,再送达屏幕上的每个 Surface:从最上层开始的各图层、当前 Page、显示在 Frame 里的 Page,以及舞台上的游戏界面。在其中每个 Surface 上,放了该 Head 的控件蓝图也会执行。所以设置面板里的控件不必拥有整个 Page 也能跟随窗口状态
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
isFullscreen | out · data | boolean | 变化后的状态 |
On Window Focus Changed
blueprint.event.head.windowFocusChanged
On Window Focus Changed(窗口焦点变更时)在应用窗口获得或失去焦点时触发,例如玩家切到别的程序再切回来。桌面产物跟随自己的窗口,包括窗口被游戏之外的东西压到后面的情况;Web 导出跟随页面的可见性与焦点。它送达的蓝图和顺序都与On Fullscreen Changed相同。它与控件的Focus、Blur Head 无关,后两者说的是哪个控件持有键盘焦点
要在因别的原因执行的图里读取当前状态,用 App 节点里的Is Window Focused
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
isFocused | out · data | boolean | 变化后的状态 |
On Window Close Requested
blueprint.event.head.windowCloseRequested
On Window Close Requested(窗口关闭请求时)在玩家请求关闭窗口时触发。这次关闭会被挂起,先等应用逻辑、再按On Fullscreen Changed所述的顺序等屏幕上的每个 Surface 执行。其中任意一个执行了Keep Window Open就取消关闭,排在它之后的 Surface 不再被询问;没有则窗口继续关闭。在 Studio Dev Mode 中它覆盖 Dev Mode 窗口,在预览和正式产物中覆盖游戏窗口
只在应用逻辑和 Surface 蓝图中提供
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
Surface 生命周期与进退场
Surface Init和Surface Unmount属于 Surface 蓝图。两个进退场 Head 也可用于元素私有蓝图,但只有该元素在那一刻已挂载且仍存活时才会收到
Surface Init
blueprint.event.head.surfaceInit
Surface Init(画面初始化)在 Page 或游戏界面 Surface 首次进入当前运行时 scope 时触发。顶层 Surface 用自身 id 作为 scope;由 Page 组件嵌入的子 Page 拥有独立的runtimeScopeId,因此同一个 Page 被两个 Page 组件引用时彼此隔离
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
Surface Unmount
blueprint.event.head.surfaceUnmount
Surface Unmount(画面卸载)在 Page 或游戏界面 Surface 离开当前运行时 scope、被替换,或其宿主 Page 组件实例卸载时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
Before Surface Exit
blueprint.event.head.beforeSurfaceExit
Before Surface Exit(画面退出前)在当前 Page——或嵌入的子 Page——即将开始退出动画时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
After Surface Enter
blueprint.event.head.afterSurfaceEnter
After Surface Enter(画面进入后)在当前 Page 完成进入动画后触发。没有动画或处于 reduced motion 时,它在预绘制完成、Surface 进入稳定显示状态后触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
元素生命周期
Init
blueprint.event.head.init
Init(初始化)在支持私有蓝图的元素完成首次渲染并挂载后触发一次。它不是渲染前的 hook。Dev Mode bundle revision 导致 Surface 或元素 remount 时会再次触发。在 Blueprint Value 中它是初始求值入口;后续求值来自隐藏的 Element 属性依赖、对上次求值读过的任意变量的写入,或来自显式的On Flush
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
Unmount
blueprint.event.head.unmount
Unmount(卸载)在元素从运行时元素树中移除时触发:Surface 关闭或被替换、Frame 切换子 Page、List item 实例被移除,或可见性导致元素完全不渲染。运行时display: none只是隐藏并保持挂载,不会触发它
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
On Flush
blueprint.event.head.flush
On Flush(刷新时)在当前蓝图所属元素被蓝图 Host API 显式改写属性、并因此触发重绘时执行。渲染器自动计算的 CSS 状态样式(hover、focus 变体)不会触发它。在 Blueprint Value 中它兼作显式求值入口:默认 Dialog Nametag 就是靠这个入口在 Dialog 不重新挂载的情况下随对话推进刷新
Flush 是属性提交后的批处理通知。运行时按帧合并同一元素的 flush;flush 处理器内部再次改写该元素时,新的 flush 进入下一帧的批次——不存在同步重入
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
element | out · data | element | 被刷新的元素引用 |
鼠标
这些 Head 可用于 displayable 控件。Mouse Click和Right Click额外可用于 Surface 蓝图,此时表示「当前 Surface 内任意位置」。Page 只在绘有它的元素的地方接收点击,所以在 Page 上,「任意位置」指的是它的元素上的任意位置。Surface 从显示出来的那一刻起接收指针输入,不必等进入动画结束,并在退出动画开始时停止接收。其中六个 Head 也会因后代元素上的输入而执行,见事件冒泡
Mouse Click
blueprint.event.head.mouseClick
Mouse Click(鼠标点击)在元素上完成一次点击时触发。用于 Surface 蓝图时表示当前 Surface 内任意点击,并输出 Surface 设计坐标。Frame 中显示的 Page 在点击落在它的元素上时执行自己的这个 Head,先于持有该 Frame 的 Surface
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
x | out · data | float | |
y | out · data | float |
Mouse Double Click
blueprint.event.head.mouseDoubleClick
Mouse Double Click(鼠标双击)在元素上完成一次双击时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
x | out · data | float | |
y | out · data | float |
Mouse Down
blueprint.event.head.mouseDown
Mouse Down(鼠标按下)在元素上按下鼠标按键时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
x | out · data | float | |
y | out · data | float | |
button | out · data | integer | 鼠标按键编号 |
Mouse Up
blueprint.event.head.mouseUp
Mouse Up(鼠标抬起)在元素上松开鼠标按键时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
x | out · data | float | |
y | out · data | float | |
button | out · data | integer | 鼠标按键编号 |
Mouse Enter
blueprint.event.head.mouseEnter
Mouse Enter(鼠标进入)在指针进入元素区域时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
x | out · data | float | |
y | out · data | float |
Mouse Leave
blueprint.event.head.mouseLeave
Mouse Leave(鼠标离开)在指针离开元素区域时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
x | out · data | float | |
y | out · data | float |
Mouse Move
blueprint.event.head.mouseMove
Mouse Move(鼠标移动)在指针于元素上移动时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
x | out · data | float | |
y | out · data | float |
Mouse Wheel
blueprint.event.head.mouseWheel
Mouse Wheel(鼠标滚轮)在滚轮于元素上滚动时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
x | out · data | float | |
y | out · data | float | |
deltaX | out · data | float | 横向滚动量 |
deltaY | out · data | float | 纵向滚动量 |
Right Click
blueprint.event.head.rightClick
Right Click(右键点击)在元素触发上下文菜单事件时触发。用于 Surface 蓝图时表示当前 Surface 内任意右键点击,输出 Surface 设计坐标;与Mouse Click相同,Frame 中显示的 Page 先执行自己的。事件成功派发时,默认上下文菜单会被阻止
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
x | out · data | float | |
y | out · data | float |
焦点
Focus
blueprint.event.head.focus
Focus(聚焦)在元素获得键盘、鼠标或手柄焦点时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
Blur
blueprint.event.head.blur
Blur(失焦)在元素失去焦点时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
键盘
键盘事件不依赖元素焦点。一次按键先送达应用逻辑,再送达持有键盘的那个 Surface,各自执行自己的链:
- 屏幕上有模态图层时,最上面的模态图层持有键盘,其他 Surface 都收不到按键
- 否则由当前 Page 持有。非模态的图层永远不会拿走键盘,所以它下面的 Page 仍然收得到按键
- 剧情显示在屏幕上、且上面没有 Page 或模态图层时,键盘归舞台所有,舞台上除
notification插槽以外的每个游戏界面都会收到按键
持有键盘的 Surface 上的控件通过自己的键盘 Head 收到按键,其他 Surface 上的控件收不到
Surface 从显示出来的那一刻起、贯穿进入动画都能收到按键,并在退出动画开始时停止。想在 Surface 仍在进场时忽略按键,在图里用 App 分类的Is Surface Entering分支
只想匹配一个绑定时用带过滤的一对,想拿到按键本身时用Any Key一对:On Key Down/On Key Up匹配卡片上配置的绑定且不携带 payload,而Any Key Down/Any Key Up对每个按键都触发,并给出key和各修饰键状态
On Key Down
blueprint.event.head.keyDown
On Key Down(按键按下时)在运行时窗口收到匹配的键盘按下事件时触发。不要求任何元素处于焦点状态
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
卡片字段
| 字段 | 说明 |
|---|---|
Key | 键盘绑定。卡片显示当前绑定;点击后在按钮上方打开捕获浮窗,按下任意按键即可绑定,支持Ctrl/Alt/Shift/Meta组合键。单键绑定按KeyboardEvent.key大小写不敏感匹配;绑定中包含修饰键时,修饰键状态也必须匹配。空值永远不触发——任意键请用Any Key Down |
On Key Up
blueprint.event.head.keyUp
On Key Up(按键抬起时)在运行时窗口收到匹配的键盘抬起事件时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
卡片字段
| 字段 | 说明 |
|---|---|
Key | 与On Key Down相同的绑定控件。空值永远不触发——任意键请用Any Key Up |
Any Key Down
blueprint.event.head.anyKeyDown
Any Key Down(任意按键按下)在运行时窗口收到任意键盘按下事件时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
key | out · data | string | 按键语义值,对应KeyboardEvent.key |
altKey | out · data | boolean | |
ctrlKey | out · data | boolean | |
shiftKey | out · data | boolean | |
metaKey | out · data | boolean | Meta / Command / Windows |
Any Key Up
blueprint.event.head.anyKeyUp
Any Key Up(任意按键抬起)在运行时窗口收到任意键盘抬起事件时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
key | out · data | string | 按键语义值,对应KeyboardEvent.key |
altKey | out · data | boolean | |
ctrlKey | out · data | boolean | |
shiftKey | out · data | boolean | |
metaKey | out · data | boolean | Meta / Command / Windows |
手柄
手柄按键的去向与键盘按键相同:先送达应用逻辑,再送达持有键盘的那个 Surface,然后送达该 Surface 上带手柄 Head 的控件。哪个 Surface 持有键盘见键盘。绑定到输入意图的按键会像键盘按键一样触发该意图,见 On Action。与键盘不同,焦点位于文本输入元素时,手柄按键仍会送达蓝图
按键按 Xbox 手柄的布局命名:
| 按键 | 名称 |
|---|---|
| 正面按键 | A、B、X、Y。PlayStation 手柄上是叉、圆、方、三角 |
| 肩键与扳机 | LB、RB、LT、RT |
| 中间按键 | Back、Start、Home |
| 摇杆按下 | L3、R3 |
| 十字键 | D-pad Up、D-pad Down、D-pad Left、D-pad Right |
| 左摇杆方向 | Left Stick Up、Left Stick Down、Left Stick Left、Left Stick Right |
- 左摇杆方向。 左摇杆向某个方向推过一半时,按下该方向;斜向时算作偏得更多的那个方向。同时绑定了十字键和左摇杆的菜单,用哪个都能移动。
On Gamepad Button Down与On Gamepad Button Up可以选择这四个方向;Any Gamepad Button Down与Any Gamepad Button Up不会因它们触发。要读取摇杆推了多远,用Get Gamepad Axis - 多个手柄。 所有已连接的手柄共用同一组按键。第一个手柄按下某个按键时它算作按下,没有任何手柄再按住它时才算作松开
- 一次按下,不重复。 按住一个按键只在按下时触发一次
Down,松开时触发一次Up - 窗口焦点。 游戏窗口隐藏或没有焦点时不读取按键。窗口重新获得焦点时已经按住的按键不触发
Down,松开时触发Up - 支持的手柄。 系统以标准布局报告的手柄会被读取,其他手柄被忽略
On Gamepad Button Down
blueprint.event.head.gamepadButtonDown
On Gamepad Button Down(手柄按下时)在卡片上选中的按键按下时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
卡片字段
| 字段 | 说明 |
|---|---|
Button | 上表中的一个按键名称,包括左摇杆的四个方向 |
On Gamepad Button Up
blueprint.event.head.gamepadButtonUp
On Gamepad Button Up(手柄松开时)在卡片上选中的按键松开时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
卡片字段
| 字段 | 说明 |
|---|---|
Button | 与 On Gamepad Button Down 的选择相同 |
Any Gamepad Button Down
blueprint.event.head.anyGamepadButtonDown
Any Gamepad Button Down(任意手柄按下)在每次按下按键时触发,左摇杆的四个方向除外
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
button | out · data | string | 按键名称,例如 A 或 D-pad Up |
Any Gamepad Button Up
blueprint.event.head.anyGamepadButtonUp
Any Gamepad Button Up(任意手柄松开)在每次松开按键时触发,左摇杆的四个方向除外
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
button | out · data | string | 按键名称,例如 A 或 D-pad Up |
输入意图
On Action
blueprint.event.head.action
On Action(意图触发时)在卡片上选中的那个输入意图被触发时触发,不论玩家是怎么触发的。
意图是项目给某个手势起的名字——“推进”“打开记录”“取消”——而它背后的绑定写在项目的意图词表上、以及每个 Surface 对它的回应里,从来不写在图上。这正是这个 Head 存在的意义:作者按名字选意图,至于哪个键、哪次点击算数,那是项目的事。卡片存的是意图的 id,所以给意图改名不会让任何图失去指向。
只能用于应用逻辑与 Surface 的图。一个意图按其构造就是整块面板范围的,所以它属于那两个能代表整块面板的宿主:Surface 回答它启用了哪些意图,应用逻辑回答那些在每一页上含义都相同的手势。一个控件说“在我这里点一下就是推进”,正是词表要取代的那种安排——想要原始手势的元素仍然有它自己的鼠标 Head。
应用逻辑会收到词表里的每一个意图,并且先于任何 Surface。不论哪个 Surface 持有键盘,它都会收到按键;不论指针手势落在哪里,它都会收到。Surface 只收到它回应的那些意图:按键在它持有键盘时,指针手势在落到它上面时。两边都回应同一个意图时,两边都会执行。本来是冲着控件去的输入,两边都收不到:落在按钮、开关、滑块、文本输入框或 List 上的指针手势不会触发意图(滚轮落在已经无法朝那个方向继续滚动的 List 上时除外),获得焦点的控件自己会处理的按键也不会,例如焦点在按钮上时按 Enter。按住不放的按键只触发一次意图。
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
source | out · data | string | 是哪一类输入触发的:pointer、key、gamepad 或 touch |
x | out · data | float | 手势落点,与鼠标 Head 用的是同一套坐标 |
y | out · data | float |
卡片字段
| 字段 | 说明 |
|---|---|
Action | 项目的输入意图之一 |
对按键和手柄绑定来说 x / y 没有意义,在那里读作 null。这正是 source 紧挨着它们的原因:它就是那个说明另外两个有没有意义的引脚。同一个意图确实会因来源不同而想要不同的行为:来自一次点击的“推进”有一个屏幕上的位置可以回应,来自空格键的没有。
要问某个意图是否正被按住而不是对它被触发做出反应,用 Input 里的 Is Action Held。
绑定元素的 Head
这两个 Head 先绑定同一 Surface 内的目标控件——和Element节点一样——然后监听那个控件而不是自己。它们同时也是元素绑定来源:Element 派生节点只有在当前图里已经存在兼容的Element、Element Flush或Element Click时才出现在创建浮窗中,而它们的element输出可以直接连到这类节点的目标输入
Element Flush
blueprint.event.head.elementFlush
Element Flush(元素刷新)监听绑定目标的 flush。目标控件被 Host API 显式改写属性并重绘后,当前蓝图中的该 Head 执行
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
element | out · data | element | 被刷新的绑定元素引用 |
Element Click
blueprint.event.head.elementClick
Element Click(元素点击)监听绑定目标的mouseClick。目标控件收到真实点击后该 Head 执行;点击目标内部的任何元素时它也会执行,因为点击会向上经过目标
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
element | out · data | element | 被点击的绑定元素引用 |
x | out · data | float | 使用绑定元素的本地设计坐标 |
y | out · data | float | 使用绑定元素的本地设计坐标 |
button | out · data | integer | 鼠标按键编号 |
List
这些 Head 属于 list-like 控件——nl.list以及游戏界面包装控件nl.notification.list、nl.choice.list、nl.nvl.list。读写运行时内容见 List 节点。List Item Refresh是个例外:它派发到 item 模板内部元素的私有蓝图,而不是 List 本身
Item Render
blueprint.event.head.itemRender
Item Render(项渲染)在 List 根据绑定数据、列表内容里的行或占位行数渲染单个条目实例时触发。payload 来自该条目的 scope
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
index | out · data | integer | |
count | out · data | integer | 本次渲染的条目总数 |
key | out · data | string | 优先来自 List 的itemKeyPath |
item | out · data | json | 条目数据。该 List 结构的一行,从它拖出连线时会列出行的字段 |
Item Click
blueprint.event.head.itemClick
Item Click(项点击)在条目容器收到点击时触发。点击条目模板内的子元素也会归属到对应条目
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
index | out · data | integer | |
count | out · data | integer | 本次渲染的条目总数 |
key | out · data | string | 优先来自 List 的itemKeyPath |
item | out · data | json | 条目数据。该 List 结构的一行,从它拖出连线时会列出行的字段 |
Item Hover
blueprint.event.head.itemHover
Item Hover(项悬停)在指针进入条目容器时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
index | out · data | integer | |
count | out · data | integer | 本次渲染的条目总数 |
key | out · data | string | 优先来自 List 的itemKeyPath |
item | out · data | json | 条目数据。该 List 结构的一行,从它拖出连线时会列出行的字段 |
Selection Changed
blueprint.event.head.selectionChanged
Selection Changed(选择变更)在条目点击导致运行时选中索引变化时触发。List 以selectedIndex属性作为初始选中值;同一运行时实例内重复点击当前选中条目不会再次触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
index | out · data | integer | 新选中的条目 |
previousIndex | out · data | integer | 之前没有选中项时为-1 |
count | out · data | integer | 本次渲染的条目总数 |
key | out · data | string | 新选中条目的 key |
item | out · data | json | 新选中条目的数据。该 List 结构的一行,从它拖出连线时会列出行的字段 |
Scroll
blueprint.event.head.scroll
Scroll(滚动)在 List 的滚动容器发生滚动时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
offset | out · data | float | 当前滚动位置 |
maxOffset | out · data | float | 最大滚动位置 |
progress | out · data | float | 通常为0到1 |
Scroll End
blueprint.event.head.scrollEnd
Scroll End(滚动结束)在滚动容器从非末端滚动到末端时触发。已经停留在末端时,后续相同滚动事件不会重复触发;离开末端后再次到达末端会重新触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
offset | out · data | float | 当前滚动位置 |
maxOffset | out · data | float | 最大滚动位置 |
progress | out · data | float | 通常为0到1 |
List Item Refresh
blueprint.event.head.listItemRefresh
List Item Refresh(列表项刷新)在nl.list渲染或刷新某个条目时,派发到该条目 item 模板后代元素的私有蓝图。模板子元素靠它读取当前条目的数据。每个重复条目实例使用独立的 instance key 和 item scope,因此 element id 相同的两个条目不会共享 locals
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
props | out · data | json | item是 object 时为item本身,否则为{ value: item } |
item | out · data | json | 条目数据。该 List 结构的一行,从它拖出连线时会列出行的字段 |
index | out · data | integer | |
count | out · data | integer | 本次渲染的条目总数 |
key | out · data | string |
Slider
这些 Head 属于nl.slider。这里的所有值都是映射后的值,和props.value以及 Slider 节点一致——0–1 normalized 值只能通过专用读节点取得
Drag Start
blueprint.event.head.sliderDragStart
Drag Start(拖拽开始)在滑块拖拽开始时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
value | out · data | float | 映射值 |
Value Changed
blueprint.event.head.sliderValueChanged
Value Changed(值变更)在运行时值发生变化时触发。派发按动画帧合并,并且在一条链还在执行时,后续变化会塌缩成一次待发派发——所以拖动不会每像素触发一次,previousValue覆盖被合并掉的全部变化。用Set Slider Value写入值不会派发该 Head
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
value | out · data | float | 映射值 |
previousValue | out · data | float | 映射值 |
Drag End
blueprint.event.head.sliderDragEnd
Drag End(拖拽结束)在滑块拖拽结束时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
value | out · data | float | 映射值 |
Switch
nl.switch 上的三个 Head,讲的都是玩家自己去拨它。从图里写状态——Switch 里的 Set Checked、Turn On、Turn Off、Toggle——不会派发其中任何一个,因此一张既写又听同一个开关的图不会把自己触发起来。
一次拨动先触发 Changed,然后触发 Turned On / Turned Off 之中恰好一个,顺序如此,且后一个要等前一条链跑完。拨到它本来就在的那个状态则什么也不触发。
Changed
blueprint.event.head.switchChanged
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
checked | out · data | boolean | 新状态 |
previousChecked | out · data | boolean |
Turned On
blueprint.event.head.switchTurnedOn
Changed 中不需要分支的那一半——给只在一个方向上有事可做的处理用。
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
Turned Off
blueprint.event.head.switchTurnedOff
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
文本输入
这些 Head 属于nl.textInput。读写字段内容见 Text input 节点
Value Changed
blueprint.event.head.textInputValueChanged
Value Changed 在输入框的值发生变化时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
value | out · data | string | |
previousValue | out · data | string |
Submit
blueprint.event.head.textInputSubmit
Submit(提交)在输入框内按下Enter时触发。确认输入法转换的那次Enter不算提交。在输入框里按下的键不会送达任何键盘 Head,所以绑定了Enter的控件On Key Down,无论在这个输入框上还是在别的控件上,都不会随Submit一起触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
value | out · data | string | 提交时的值 |
Page
Page Event
blueprint.event.head.pageEvent
Page Event(页面事件)在嵌入某个 Page 组件的子 Page 调用Emit Page Event时,于父级nl.frame的私有蓝图中触发。只有nl.frame提供该 Head;发送侧位于 App 节点
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
event | out · data | string | 子 Page 发出的事件名 |
data | out · data | json | 子 Page 发出的事件数据 |
视频
Playback Ended
blueprint.event.head.videoEnded
Playback Ended(播放结束)在 nl.video 的私有蓝图中触发:视频每播放到结尾一次,触发一次。开启循环时、更换视频文件时、以及页面在播完前关闭时不触发。用 Seek Video(跳转视频播放位置)把正在播放的视频移到结尾,同样会触发。在编辑画布上预览播放不会触发
要在图中开始、停止或移动视频,使用 Video 节点
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |
Game Preference
两个 Head 都订阅当前活动LiveGame的 preference 派发器。它们在应用逻辑和 Surface 蓝图中提供,一次变化先送达应用逻辑,再按 On Fullscreen Changed 所述的顺序送达屏幕上的每个 Surface。没有活动 game runtime 时不会订阅,也不会触发;On Game Ready之后运行时在新的LiveGame上重新订阅。Preference Setter(如Set BGM Volume)和 NarraLeaf 内部写入都会触发它们,并且写入相同值不保证被去重——所以不要在监听某个偏好的图里再写入同一个偏好,那会构成自触发循环。Getter 和 Setter 见 Game 节点
On Preference Changed
blueprint.event.head.preferenceChanged
On Preference Changed(偏好变更时)在所选偏好键变化时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
value | out · data | json | 新值,类型随所选键而定:autoForward / skip / skipping / skipReadText / showDialog / muteOnWindowBlur 为 boolean,voiceEndMode 为 string,其余为 float。可以直接连到 Set Slider Value 这类有类型的输入 |
previousValue | out · data | json | 运行时缓存的旧值,类型同value。本次会话对该键没有更早快照时为null |
卡片字段
| 字段 | 说明 |
|---|---|
Preference | 要监听的偏好键,共十八个:autoForward、autoForwardDelay、skip、skipping、skipReadText、muteOnWindowBlur、showDialog、gameSpeed、cps、textRevealDuration、voiceVolume、voiceFadeDuration、voiceEndMode、bgmVolume、soundVolume、globalVolume、skipDelay、skipInterval。空值不订阅任何键;任意键请用On Any Preference Changed |
On Any Preference Changed
blueprint.event.head.anyPreferenceChanged
On Any Preference Changed(任意偏好变更时)在任意偏好键变化时触发。用于集中处理设置变更——统一持久化当前设置,或一次性刷新整个设置面板
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
key | out · data | string | 发生变化的规范键名,例如bgmVolume |
value | out · data | json | 变化后的新值;实际类型由key决定 |
previousValue | out · data | json | 类型同value;没有更早快照时为null |
Broadcast
Broadcast 在发送它的那个 Surface 的各蓝图之间传递具名消息:该 Surface 自己的蓝图,加上它上面的所有控件蓝图。被 List 重复的控件在屏幕上的每一行各收到一次。它不会到达应用逻辑,也不跨 Surface;发送者自己如果也在监听,同样会收到自己发出的广播。目标来自文档而不是已挂载的 React 树。四个节点都只在 Surface 蓝图和控件蓝图中提供
On Broadcast
blueprint.event.head.onBroadcast
On Broadcast(广播时)在收到指定名称的广播时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
data | out · data | json | 广播数据 |
sender | out · data | string | 发送方元素 ID;没有发送者时为空字符串 |
卡片字段
| 字段 | 说明 |
|---|---|
Event | 要监听的广播事件名。去除首尾空白后精确匹配 |
On Any Broadcast
blueprint.event.head.onAnyBroadcast
On Any Broadcast(任意广播时)在本蓝图收到任意名称的广播时触发
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — | |
event | out · data | string | 广播事件名 |
data | out · data | json | 广播数据 |
sender | out · data | string | 发送方元素 ID;没有发送者时为空字符串 |
Send Broadcast
blueprint.broadcast.send · Latent
Send Broadcast(发送广播)向运行中的 UI 发送一个广播事件,等所有监听链跑完后从next继续。事件名为空或只有空白是执行错误,不是静默 no-op。发送者 id 由正在执行的蓝图所属元素填入
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
in | in · exec | — | |
next | out · exec | — | |
event | in · data | string | 可在卡片上直接填字面量 |
data | in · data | json |
Get Listener Count
blueprint.broadcast.getListenerCount · Pure
Get Listener Count(获取监听器数量)读取当前 Surface 上某个广播事件名已注册的监听器数量
统计的是 Head 而不是蓝图:一张图里有两个匹配 Head 就计两次,一个On Any Broadcast对每个事件名都计数。Head 从文档中统计,因此当前未挂载的元素同样计入
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
event | in · data | string | 可在卡片上直接填字面量 |
count | out · data | integer |
Story Action
On Call
blueprint.event.head.onCall
On Call(被调用时)是 Story Action Blueprint——与某个故事动作一一绑定的蓝图——的唯一入口,在其他任何地方都不提供。Story Action 图会被编译成 NarraLeaf 的Script动作并经剧情编译器执行,永远不走 UI 事件派发路径,因此本页其余 Head 在该 owner 中都不出现
| 引脚 | 方向 | 类型 | 说明 |
|---|---|---|---|
then | out · exec | — |