NarraLeaf

应用

Surface 导航、应用窗口,以及一个 Page 对自身的认知

App 节点是蓝图伸手够到它所运行的那个应用的地方:打开一个 Surface、在它上面再压一层、调整窗口大小、跳转一个链接、退出应用。Surface 模型本身——栈、层叠、游戏舞台——在 Surfaces 中说明

约定

  • 其中三个是尾节点。 Go Page、Replace Page 和 Quit Application 根本没有 next 引脚。它们之后是另一个 Surface,或者根本没有应用了——这张图没有剩下什么可以继续跑
  • type id 并不都写 app。 这一组在长出窗口、叠层和应用节点之前叫 Page。id 是要存进文档的东西,所以从未改名:blueprint.page.*、blueprint.frame.*、blueprint.layer.* 和 blueprint.app.* 全都属于这同一个分类
  • 宿主。 Get Page Props、Get Page Param 和三个 Is Surface … 读取节点可用于 Surface、控件和 Blueprint Value 蓝图;Emit Page Event 可用于 Surface 和控件蓝图。它们都不存在于应用逻辑中——应用逻辑没有当前 Page 可问。导航、叠层、窗口、指针、截图与链接节点在除 Story Action 蓝图之外的所有蓝图中都可用,Keep Window Open 则只用于应用逻辑与 Surface 蓝图
  • 打开页面时传入它的参数。 Page 中所选页面声明了参数时,Go Page、Replace Page、Show Layer 与 Set Frame Page 会为每个参数长出一个以参数名命名的输入,位于 Page props 之前。字符串、文字、数字和布尔值参数可以在卡片上填写,列表参数接收由其行结构组成的数组。未连线且为空的输入不传值,页面读取该参数的默认值。以这种方式传入的值会替换 Page props 中的同名字段。Go Page 或 Replace Page 的 Page 输入连线时不显示参数输入:页面由连线决定,只传入 Page props
  • 编辑器画布没有应用可以对话。 在作者时画布上,这里每个有副作用的节点都会以 该节点仅在游戏运行时可用,需在开发模式中运行 失败;纯读取节点则安静降级,Get Page Props 读到 {},三个 Is Surface … 读到 false。在故事编辑器的游戏界面预览里,导航和退出会打到桩上什么都不做,两个全屏节点则报告在那里不可用。这些都要在开发模式中验证

Go Page

blueprint.page.go · Latent

Go Page(前往页面)打开卡片上选中的 Surface,走的是运行时切换 Page 时的同一条导航路径——同样的进退场动画、同样的 Surface 生命周期、同样的控件蓝图。选择 None 则改为关闭第一个 Page 之上的所有 Page

Go Page 是替换玩家眼前的东西。要把一页压在当前这一页之上并拿到一个句柄,用 Show Layer。要关掉一层,优先用 Go back:导航是一个栈,对着玩家来时的那一页再 Go Page,是在已有的两页上再压上第三页,而不是回到它。带 None 的 Go Page 则关闭第一个 Page 之上的所有 Page

引脚方向类型说明
inin · exec—
surfaceIdin · datastring可选。连线即在运行时决定目标;未连线时用卡片上的 Page 字段
propsin · datajson作为目标 Page 的 Page props 传入,在那边由Get Page Props读取。可选——未连线时为{}

卡片字段

字段说明
Page要打开的 Surface,从项目的 app Surface 中选择。选None则改为关闭第一个 Page 之上的所有 Page

卡片上还有所选页面的每个参数各一个输入,见约定

游戏一旦跑起来,Page 栈会被藏到舞台下面。此时 Go Page 会把目标作为 UI 叠层打开在舞台之上,None 则关闭舞台之上的这些 Page、重新露出舞台。要离开游戏本身,请用 Game 里的 Quit Game——它自己指定要返回的 Page

Replace Page

blueprint.page.replace · Latent

Replace Page(替换页面)打开页面以代替当前页面,并关闭当前页面。用于玩家不会再回来的页面,例如启动画面或声明页面:用 Go Page 时,这个页面会留在下一个页面底下,Go back 和设为 None 的 Go Page 都会再次回到它

引脚方向类型说明
inin · exec—
surfaceIdin · datastring可选。连线即在运行时决定目标;未连线时用卡片上的 Page 字段
propsin · datajson作为目标 Page 的 Page props 传入,在那边由Get Page Props读取。可选,未连线时为{}

卡片字段

字段说明
Page要打开的 Surface,从项目的 app Surface 中选择。没有 None:未选页面也未连线时,节点以 “替换页面”:选择页面 失败

卡片上还有所选页面的每个参数各一个输入,见约定

新页面与 Go Page 走同样的过渡动画出现,过渡完成后被替换的页面离开页面栈。替换入口页面时,新页面成为栈上的第一个页面。游戏运行中且舞台上方没有页面时,没有可替换的页面,该节点像 Go Page 一样把目标打开在舞台之上

Go back

blueprint.page.back · Latent

关闭玩家可以关掉的最上面一层:顶上是用 Show Layer 打开的可关闭叠层时关它,否则关闭顶层 Page,回到它下面的那一层。

游戏在运行中的故事上打开的每一个页面 —— 存档、读档、设置、对话记录 —— 都需要一条出路,而 Go Page 不是:导航是一个栈,“去我来时那一页”会在已有的两页上再压上第三页,游戏依旧埋在底下。

关掉最后一页是空操作而不是错误。从标题界面进来的那一页就是栈底,如果在那里抛错,同一个返回按钮就会因为玩家怎么走过来的而时灵时不灵。

引脚方向类型说明
inin · exec—
nextout · exec—

Clear Page

blueprint.page.clear · Latent

把运行中的游戏身上披着的每一页都摘掉,只留下舞台 —— 在任何页面上按 Escape 都成立,图不必知道玩家现在有多深。

三种“关闭”并不能互换。Go back 只关一层,所以在两页深的游戏上按 Escape 只会落到下面那一页。带 None 的 Go Page 回到第一个 Page,关闭它之上的一切。Clear Page 只清掉一次游戏会话叠上去的那些页面,而在游戏之外它什么也不做 —— 这正是 Clear Page → Go back 能当作一个通用 Escape 处理的原因:读起来就是“在游戏里就离开游戏,否则退一步”。

引脚方向类型说明
inin · exec—
nextout · exec—

Show Layer

blueprint.layer.show · Latent

把一页盖在屏幕上已有的东西之上,而不是替换掉它,并交回一个句柄指代这一次显示。

叠层的深度是没法寻址的。叠放次序就是挂载次序,而唯一能指代某一层的东西就是那个句柄 —— 对任何没拿到它的图来说毫无意义。这是刻意的:一个界面无法被写成依赖于“自己在从上往下第三层”,于是第一个想在暂停菜单之上再加一层的项目,不会把整套合成变成一个谁也改不动的编号方案。

引脚方向类型说明
inin · exec—
nextout · exec—
propsin · datajson这一层的 Page props,在里面由 Get Page Props 读取。可选
modalin · databoolean挡住与下面的交互。可填字面量。未连线读作 false
dismissiblein · databoolean玩家能否不靠图自己把它关掉。可填字面量。未连线读作 true —— 玩家出不来的一层是一个决定,没动过的引脚不该替人做这个决定
groupin · datastring可选。声明同一分组的层会互相排队,而不是叠在一起。可填字面量
layerout · datastring句柄。交给 Hide Layer、Wait For Layer 或 Is Layer Mounted

卡片字段

字段说明
Page要显示的 Surface,与 Go Page 选的是同一份列表。一层就是一页;给两份不同的列表会暗示它们不是一回事

卡片上还有所选页面的每个参数各一个输入,见约定

指向项目里没有的 Page 时,节点会带着宿主自己那句话失败,于是失败落在你能改的那一行上。

Hide Layer

blueprint.layer.hide · Latent

关掉句柄指代的那一层。

引脚方向类型说明
inin · exec—
nextout · exec—
layerin · datastring来自 Show Layer 的句柄

指向不存在的句柄 —— 从未设置过、或者已经关掉了 —— 是空操作而不是错误,和 Go back 在栈底做的是同一笔交易:那一层已经不在了,本来就是这个节点被要求达成的结果。

Wait For Layer

blueprint.layer.wait · Latent

挂起,直到那一层关闭,然后带着它关闭时给出的东西继续。

引脚方向类型说明
inin · exec—
nextout · exec—
layerin · datastring来自 Show Layer 的句柄
resultout · datajsonClose This Layer 收到的东西。被玩家关掉、或者关闭时什么都没给,都是 null

Close This Layer

blueprint.layer.closeSelf · Latent

关掉当前蓝图所在的那一层,并可以给等着它的人一个答复。这是 Wait For Layer 的另一半:被显示出来的那一页自己报告结果,而不是打开它的人伸手进去读。

引脚方向类型说明
inin · exec—
nextout · exec—
resultin · datajson可选。未连线时这一层以 null 关闭

Is Layer Mounted

blueprint.layer.isMounted · Pure

句柄指代的那一层是否还在屏幕上。蓝图值中不提供它。

引脚方向类型说明
layerin · datastring来自 Show Layer 的句柄
mountedout · databoolean空句柄或未知句柄都读作 false

Show Confirm

blueprint.layer.confirm · Latent

用你自己的一页去问一个问题,并从玩家按下的那个按钮走出去。

该节点把选中的 Page 作为模态、可关闭、归在 confirm 分组的叠层显示,因此第二个问题排在第一个之后,而不是压在它上面。它向该页面传入两个参数:问题 message,以及 buttons,即由确认按钮行组成的列表,字段为 id、text、index 和 disabled。骨架模板的确认页面声明了这两个参数:message 是由消息文本显示的文字参数,buttons 是行结构为确认按钮的列表参数,由按钮列表绘制。为 Show Confirm 自建的页面也声明这两个参数

message 按传入的原样显示。要以玩家的语言显示问题,把 Translation Key Text(翻译键文本)连到 message

引脚方向类型说明
inin · exec—
messagein · datastring问题本身。可填字面量
tagin · datastring可选。原样传给页面,供一页回答多个问题时区分。可填字面量
datain · datajson可选。原样传给页面
dismissedout · exec—玩家没有作答就关掉了问题时走这里
indexout · datainteger按钮的位置;走 dismissed 时为 -1
labelout · datastring按钮的文字;走 dismissed 时为 ""

卡片字段

字段说明
Page画出这个问题的 Surface

卡片上的 Add Button 会追加一个 string 输入 button_N_label 和与它配对的 exec 输出 button_N_pressed,插在固定的 dismissed 之上。配对按 id 而不是按位置,所以从中间删掉一个按钮不会让编号错位,也不会把分支接错。页面用 Close This Layer 关闭自己来报告答案 —— 直接给一个下标,或者给一个带 index 字段的对象。其它任何东西,包括关闭时什么都不给,都走 dismissed。

Keep Window Open

blueprint.app.keepWindowOpen

把当前这一次窗口关闭请求挂住,让游戏来决定,而不是窗口在它脚下关掉。想问一句“真的要退出吗”的游戏先跑它,显示自己的询问,玩家确认后再调 Quit Application。

它不阻止任何其它东西。它不是用来吃掉一个按键、关掉一个页面或者阻断某个元素的事件的 —— 而且在关闭请求之外它根本无物可作用,那是一个执行错误而不是静默空操作,因为把它放在那里的图是在索要一个没人给它的保证。

只能用于应用逻辑与 Surface 的图。

引脚方向类型说明
inin · exec—
nextout · exec—

Quit Application

blueprint.page.quit · Latent

Quit Application(退出应用程序)请求运行时干净地关闭应用——给状态保存、崩溃上报留出余地——而不是直接杀进程。在开发模式中,它结束当前开发模式会话并把你送回 Studio;它不会关闭 Studio 本身

引脚方向类型说明
inin · exec—

这是运行时发起的退出,因此它刻意不触发 On Window Close Requested。那个事件是为玩家关闭窗口而存在的,挂在它上面的确认处理不应该被一个已经决定要退出的节点触发

Set Fullscreen

blueprint.app.setFullscreen · Latent

Set Fullscreen(设置全屏状态)让应用窗口进入或退出全屏

引脚方向类型说明
inin · exec—
nextout · exec—

卡片字段

字段说明
ModeEnter Fullscreen、Exit Fullscreen 或 Toggle Fullscreen。下拉未设置时按 toggle 处理——这是全屏按钮最有用的默认值

Toggle Fullscreen 会先读当前窗口状态再取反,所以一个按钮就能覆盖两个方向,你不必自己记录状态

Get Fullscreen

blueprint.app.getFullscreen · Latent

Get Fullscreen(获取全屏状态)读取应用窗口当前是否处于全屏

引脚方向类型说明
inin · exec—
nextout · exec—
isFullscreenout · databoolean

它是 latent 而不是 pure,因为答案来自窗口而不是来自图——这也意味着它无法支撑 Blueprint Value。请改在事件图里驱动全屏指示

Get Window Size

blueprint.app.getWindowSize · Latent

舞台当前的像素尺寸,无论窗口处在什么状态。

引脚方向类型说明
inin · exec—
nextout · exec—
widthout · datafloat
heightout · datafloat

Set Window Size

blueprint.app.setWindowSize · Latent

把舞台调整到指定的像素尺寸。

项目 → 应用 → 窗口里列出的那些尺寸,是给设置界面搭出来用的,而不是图能提出什么要求的上限:尺寸另有来源的游戏 —— 它记住的一个值、它量到的一块显示器、玩家自己填的一个数 —— 就在这里说出来。它做不到的是跑出屏幕:尺寸会被收进显示器的工作区,并受窗口自身最小值约束,因为比桌面还大的窗口谁也用不了。

全屏与最大化会先被退出,理由和 Set Window Scale 一样:两者都是同一个问题的答案,在它们底下改出来的尺寸,玩家一退出就会弹回去。

引脚方向类型说明
inin · exec—
nextout · exec—
widthin · datafloat
heightin · datafloat

Get Window Scale

blueprint.app.getWindowScale · Latent

窗口当前的大小,写成设计尺寸的倍数。

引脚方向类型说明
inin · exec—
nextout · exec—
scaleout · datafloat1 是设计尺寸,0.5 是它的一半

Set Window Scale

blueprint.app.setWindowScale · Latent

把窗口调整到设计尺寸的某个倍数。

项目没有提供的倍数会被换成它提供的最接近的一档,而不是被拒绝:这一档档梯子是作者的,算出 0.8 的图应该把窗口挪一挪而不是失败。全屏与最大化会先被退出。

引脚方向类型说明
inin · exec—
nextout · exec—
scalein · datafloat不是正数的值会回落到 1

Get Window Scale Options

blueprint.app.getWindowScaleOptions · Latent

设置界面可以提供的那些尺寸,就是作者在项目 → 应用 → 窗口里写下的那份。把 List 绑到它上面,提供哪些尺寸就来自项目,而不是来自图里敲进去的数字。

引脚方向类型说明
inin · exec—
nextout · exec—
scalesout · dataarray<float>设计尺寸的倍数

空是一个答案,不是失败。 没有自己窗口可调的宿主 —— Web 导出、开发模式、故事预览 —— 在这里什么也不返回,据此搭出来的那一行在那里也就什么都不画。这正是要的效果:一个动不了窗口的尺寸控件比没有更糟。

Is Window Focused

blueprint.app.isWindowFocused · Latent

游戏窗口是否就是玩家当前正在使用的那个窗口。On Window Focus Changed 在它变化时运行;这个节点读取的是当前状态,供因别的原因运行的图使用,或者供玩家已经切到别的窗口之后才打开的 Page 使用。

引脚方向类型说明
inin · exec—
nextout · exec—
isFocusedout · databoolean在没有自己窗口的宿主里为 true,例如故事编辑器的游戏界面预览

Save Screenshot

blueprint.app.saveScreenshot · Latent

截下玩家眼前的画面,写成一个 PNG 文件。这个节点不接收文件夹或文件名,返回它写入的那个文件的路径。

引脚方向类型说明
inin · exec—
nextout · exec—文件写入成功时走这里
failedout · exec—
pathout · datastring写入的文件。走 failed 时为空
errorout · datastring没有写入的原因

桌面构建写入玩家存档旁边的 screenshots 文件夹。开发模式写入 Studio 自己的数据目录,每个项目一个文件夹,因此测试时截的图不会落进项目目录。每个文件按截图时间命名,精确到毫秒。

failed 同时覆盖“这个平台没有截图功能”(Web 导出、移动端构建、故事编辑器的游戏界面预览)和“截取或写入没有成功”,error 说明是哪一种。与 Move Mouse To 一样,含有这个节点的项目被导出到非桌面目标时,构建控制台会给出警告。

Open Screenshots Folder

blueprint.app.openScreenshotsFolder · Latent

在玩家的文件管理器里打开 Save Screenshot 写入的那个文件夹。文件夹不存在时会先创建,所以设置界面可以在玩家还没截过图时就提供这一项。

引脚方向类型说明
inin · exec—
nextout · exec—文件夹已打开时走这里
failedout · exec—
pathout · datastring打开的文件夹。走 failed 时为空
errorout · datastring没有打开的原因

在 Save Screenshot 走 failed 的那些平台上,它同样走 failed。

blueprint.app.openExternal · Latent

把一个地址交给玩家本来就在用的浏览器:商店页、更新说明、支持表单。

别处没有任何要声明的东西。图是作者写的,所以图里的地址就是作者的决定。宿主检查的是协议,而且只在真正要打开页面的那个进程里检查、绝不在渲染进程里 —— 能到达的只有 http:、https: 与 mailto:。这是一份允许清单,而不是把已知有害的那几个列出来禁掉:shell.openExternal 会把地址交给操作系统为它登记的任何程序,而玩家装的任何软件都能登记一个新协议。清单之外的协议 —— 最常被问到的是 steam: —— 要通过插件到达,那条路上模式写在 manifest 里,安装时逐条具名批准。

引脚方向类型说明
inin · exec—
urlin · datastring连线优先于卡片。空地址是执行错误
nextout · exec—页面已经交出去时走这里
failedout · exec—
errorout · datastring是哪一种拒绝

卡片字段

字段说明
URL直接填写的地址。url 引脚未连线时用它

failed 同时覆盖“这个节点打不开的协议”和“浏览器没能打开这一页”。它们共用一个引脚,因为作者对两者的应对是同一个 —— 玩家没拿到这一页,那就给他看点别的。

它不是网络权限。这里不发请求,也没有任何字节回到游戏里,所以它不受项目网络设置的约束,把网络关掉也不会禁用它。见安全。

Move Mouse To

blueprint.app.movePointerTo · Latent

把玩家真正的光标放到 Surface 上的某个点。这与 Windows 在“自动移到默认按钮”打开时替你做的是同一件事,也是对手柄友好的菜单需要、而只认鼠标的菜单假装不出来的东西。

坐标是 Surface 的设计坐标 —— 和 Get Measured Rect、Get Bounds 以及每个鼠标事件的 x / y 是同一套。作者看不见窗口,也不该被要求去想它。

引脚方向类型说明
inin · exec—
pointin · dataVector2D用 Data 里的 Make Vector2D 造一个
nextout · exec—
failedout · exec—
errorout · datastring

卡片字段

字段说明
Duration (s)0 是立刻放过去;任何正值都会让它走过去
Easinglinear、easeIn、easeOut 或 easeInOut

只在桌面构建与开发模式下有效。 Web 导出无法摆布系统指针,节点在那里走 failed;而当一个含有这些节点的项目被导出到非桌面目标时,构建控制台会给出警告 —— 作者该从构建里知道这件事,而不是从玩家那里。

Move Mouse To Element

blueprint.app.movePointerToElement · Latent

同一件事,但瞄准某个控件的中心,而且是量出来的,不是从文档里算出来的。

它是一个独立的节点,而不是 Get Element Measured Rect 接 Move Mouse To,因为两半必须对“这个控件最后被画在哪个 Surface 上”达成一致。组件实例是被放到哪里就在哪里渲染内容的,那未必是这个元素被写下时所属的 Surface。

引脚方向类型说明
inin · exec—
elementin · dataelement
nextout · exec—
failedout · exec—
errorout · datastring

卡片字段

字段说明
Duration (s)同上
Easing同上

与 Element 里的节点一样,只有当图中已经有一个绑定到同一 Surface(在组件自己的蓝图里则是同一组件)上某个控件的 Element 字面量,或 Element Flush / Element Click 事件 Head 时,创建浮窗才会提供它。这样的绑定恰好只有一个时,放置节点会自动把 element 接到它上面。

Get Page Props

blueprint.page.getProps · Pure

Get Page Props(获取页面属性)读取当前蓝图所在 Page 的完整 props 对象。由 Go Page 打开的 Page 读到的是该节点的 props 输入及其传入的参数;被 nl.frame 嵌入的 Page 读到的是 Frame 的 params。页面声明的每个参数都在其中,未传入的取默认值。它永远不为 null:未声明参数、什么都没传就打开的 Page 读到 {}

引脚方向类型说明
propsout · datajson

读取某个已声明的参数,用 Get Page Param。页面未声明的字段用 Data 里的 Get JSON Field 读取,它接受点分路径

Get Page Param

blueprint.frame.getParam · Pure

Get Page Param(获取页面参数)读取打开页面时为某个参数传入的值,未传入时为该参数的默认值。在卡片的 Parameter 中选择参数,可选的是蓝图所属页面的参数

引脚方向类型说明
valueout · datajson类型随所选参数而定:字符串和文字为 string,数字为 float,布尔值为 boolean,JSON 为 json,列表为由其行结构组成的数组,行结构为不指定时为 array

卡片字段

字段说明
Parameter该蓝图所属页面的一个参数
  • 可用位置。 页面自己的蓝图、页面上控件的蓝图,以及这些控件的 Blueprint Value。在页面控件中显示的页面上,读取的是该控件传入的值
  • 文字参数得到传入的文字,未传入时为源语言的默认值。要以玩家的语言显示文字参数,在文本或按钮上的页面参数中选择它
  • 页面已不再声明的参数在画布上显示警告,项目检查报告缺失的页面参数(blueprint/page-param-missing),节点读不到值
  • 未选择参数时,卡片显示 key 输入(string,可在节点卡上直接填字面量),按该名称读取 Page prop,不存在时为 null。早期版本的图中的该节点是这种形式

Emit Page Event

blueprint.frame.emit · Latent

Emit Page Event(触发页面事件)把事件从被嵌入的 Page 向上发给嵌入它的那个 nl.frame 元素。Frame 自己的蓝图通过 Events 里的 Page Event 事件 Head 接收,并原样拿到同一个名字和载荷

引脚方向类型说明
inin · exec—
nextout · exec—
eventin · datastring事件名。可在节点卡上直接填字面量
datain · datajson交给处理方的载荷

顶层 Page 没有父级 Frame。节点仍会正常完成、执行也会继续从 next 出去——只是这个事件没有任何人能收到,而且没有任何提示。空事件名则相反:那是执行错误,不是静默的 no-op

Is Surface Entering

blueprint.page.isSurfaceEntering · Pure

Is Surface Entering(画面是否进入中):从该 Surface 的 runtime scope 挂载那一刻起,直到它的进入动画结束为止,返回 true

引脚方向类型说明
isEnteringout · databoolean

这个标记会在 After Surface Enter 触发之前被清掉,所以在该事件里读它会得到 false。这是刻意的:事件跑起来的时候,Surface 已经进入完成了

Is Surface Exiting

blueprint.page.isSurfaceExiting · Pure

Is Surface Exiting(画面是否退出中):从 Before Surface Exit 触发、退出动画开始那一刻起,直到 Surface 卸载为止,返回 true。可以用它拒绝一个正在离开的界面上的输入

引脚方向类型说明
isExitingout · databoolean

Is Surface Transitioning

blueprint.page.isSurfaceTransitioning · Pure

Is Surface Transitioning(画面是否过渡中):上面两者任一为 true 时它就为 true——用一次读取回答这个界面是否还没稳定下来

引脚方向类型说明
isTransitioningout · databoolean

本页目录