声明式 UI模块
脚本入口: eve.UI()
构建并挂载保留式控件树,通过稳定 ID 消费点击和更改事件。
基本用法
对象关系与调用时机
UI 管理多个命名 Host;Host 保存控件树;稳定 ID 标识控件;UIComponent.build() 可封装可复用树。事件返回完整 host/id 路径,修改接口在当前 selected host 中查找。
自定义组件
脚本组件拥有实例级 props 与 state 表;setProps(table) / setState(table) 会合并 变更并标记脏状态。已挂载的根组件会进入帧调度队列,在下一次渲染前通过稳定 ID 自动协调旧树;同一帧的多次变更只重建一次,通常不需要手动调用 updateIfDirty()。 持久化的子组件实例可用 renderChild(child, props) 嵌入,子组件 markDirty() 会自动向 父组件传播。首次挂载和后续重建分别调用 onMount()、onUpdated();显式 unmount() 会递归卸载子组件并调用 onUnmount(),同时隐藏对应 Host。
renderChild(child, props, replaceProps=false) 默认与 setProps() 一样合并属性;第三个 参数为 true 时先清空旧属性表再应用新属性。updateIfDirty() 仍可用于测试或确实需要 同步刷新的工具代码。
组件可用 bindClick(id, fn) / bindChange(id, fn) 声明事件处理器;同一组件内相同 类型与 ID 的绑定会被替换而不是累加,组件卸载时自动注销。动态列表使用 renderKeyed(key, factory, props):稳定 key 会复用原组件实例,移除 key 会触发子组件 onUnmount() 并释放其事件处理器。
componentOnClick、componentOnChange 与 componentClearHandlers 是 UIComponent 实现事件所有权的内部桥接函数;游戏脚本应使用上述 bindClick / bindChange,不要直接调用这些桥接函数。
布局
beginRow() / beginColumn() 支持 setItemFlexGrow()、setItemFlexShrink()、 setItemFlexBasis()、setItemAlignSelf() 与 setFlexWrap()。独立的横纵间距使用 setLayoutGaps(columnGap, rowGap);setLayoutOverflow("visible" | "clip" | "scroll") 控制超出内容的显示方式。beginGrid(columns, id, columnGap, rowGap) 创建等宽列网格, setItemGridColumnSpan() 设置最后一个子项的跨列数,setItemAspectRatio() 可让 Flex 或 Grid 根据宽高比推导另一轴尺寸。
saveTreeJson() 写出 schema: "eve.ui.tree" 与当前 version: 4。加载器把无版本文档 视为版本 1,接受版本 1–4,拒绝未知 schema 和未来版本;未知字段会被忽略,新增字段 均有向后兼容默认值。
.9.png 九宫格资源
支持未经 Android 编译的标准 .9.png:顶边和左边各一段连续黑色像素定义可拉伸区, 底边和右边可选黑线定义内容区。加载时会移除四周 1px 标记框并缓存纹理。
ninePatch(path,id,w,h) 创建叶子图片;beginNinePatch(path,id,w,h) 创建可容纳子控件的 面板;setImageNinePatchFile(id,path) 可替换已挂载图片。当前解析器有意只接受单段拉伸 区(与引擎单中心九宫格模型一致),多个不连续拉伸段会返回 false 并输出诊断。
目标导向指南
创建可交互 HUD
beginBuild() 后用 Window/Flex/Group/List 组织控件,给每个交互控件稳定 ID,完成后 mountBuildAs("hud")。更新阶段循环 consumeClick() / consumeChange(),渲染阶段调用 beginFrameAndRender()。
使用弹性布局(Flex)
beginRow / beginColumn / beginFlex("row"|"column", id, gap) 自动排列子控件,无需手写 sameLine。spacer() 吸收剩余空间;setItemFlexGrow / setItemSize 作用在刚添加的子项上;setFlexAlign / setFlexJustify 配置当前 Flex 容器。
桌面框架可用 beginToolbar、beginToolbox、beginSidebar、beginStatusBar 和 beginSplitPane("row"|"column", ratio, id) 直接组合。SplitPane 必须包含两个直接子项, 拖拽分隔条会产生普通 value change;比例可通过 getValue / setValue 读取和恢复。 独立面板可用 setHostMovable(true) / setHostResizable(true) 允许用户调整;ImGui 会按 稳定 Host ID 自动写入和恢复 ini 布局。无标题 Overlay 的不透明度由 setHostOverlayAlpha(0..1) 控制。
使用内置编辑器图标
icon(name, id) 显示语义图标,iconButton(name, label, id) 创建图标按钮。图标字体随 引擎构建和 SDK 安装,不依赖游戏工作目录。名称不区分大小写,空格和下划线会转成 连字符;常用名称包括 search、settings、save、undo、redo、folder-open、 pointer、move、paint-brush、database、layers、play 和 warning。
setItemSelected(true) 为刚加入的普通按钮或图标按钮设置选中外观;工具切换后用既有的 setChecked(id, selected) 更新已挂载控件。选中态只是 UI 投影,工具的权威状态仍由 EditorToolbar 或具体编辑器 session 持有。
组合桌面编辑器控件
Editor 常用控件已内置:searchField、switch、badge、colorPalette、sectionHeader、beginCard、 beginMenuBar / beginMenu / menuItem。setItemTooltip 为刚添加的控件设置悬停说明。 菜单栏应作为 Window 的直接子项;所有可交互控件都应使用稳定 ID。
colorPalette(label, r, g, b, a, id) 显示颜色编辑器与色板网格。当前颜色存在节点 tint 上,用 getColorR/G/B/A / setColor 读写;拖动或点选色块走普通 consumeChange()。 setValueText(id, "#ff0000;#00ff00") 可换成自定义色板(#rrggbb / #rrggbbaa 或 r,g,b[,a], 分号或换行分隔);留空则使用内置 16 色。
用 Theme 统一布局
Theme 除颜色和 ImGui 基础几何外,还提供类似 CSS design tokens 的语义布局默认值: toolbarHeight、statusBarHeight、sidebarWidth、toolboxCellSize、 splitterSize、minPaneSize、panelPadding、cardPadding、barPadding、 sectionSpacingY、searchMinWidth 和 searchIconGap。暗色与亮色主题共享这些布局 参数,因此切换配色不会改变界面结构。
Toolbar、Sidebar、Toolbox、Card、StatusBar、SplitPane 和 SearchField 在没有显式尺寸时自动继承 Theme。局部差异继续通过 setItemSize、setItemPadding、setItemMargin、setItemMinSize 等接口覆盖, 其优先级高于 Theme 默认值。SplitPane 的直接子节点总是填满获分配的 pane,内部控件 再依据可用宽度响应式重排;Toolbox 的列数是上限,空间不足时会自动减少列数。
更新而不重建整个 UI
文本变化用 setText(id, value),进度用 setValue(),显示隐藏用 setVisible(), 启用状态用 setEnabled();结构变化才重新 build 并 remountBuildAs()。多宿主时先 select(host) 再按局部 ID 操作。
配置焦点、输入策略与无障碍语义
构建树时,setItemFocusMode("none"|"click"|"all")、 setItemMouseFilter("stop"|"pass"|"ignore") 与 setItemTabIndex(index) 作用于刚添加的 控件。setItemFocusOrder(previous, next) 配置顺序导航, setItemFocusNeighbors(left, right, up, down) 配置方向导航;空字符串表示继续使用稳定的 tabIndex/树顺序。setItemEnabled(false) 会同时禁止交互与焦点。
指针点击在保留树中按 MouseFilter 路由:stop 在当前控件处理后截断,pass 处理后继续 向祖先冒泡,ignore 不处理但允许继续向祖先传递。轮询队列仍只记录原始目标一次,祖先 通过各自的 onClick 处理器观察同一事件,避免脚本收到重复目标。
setItemAccessibility(role, name, description) 为控件附加语义角色、可读名称和说明。 角色可用 button、checkbox、combobox、textbox、slider、menuitem、 progressbar、image、heading、status、region 或 generic。
挂载后可用 requestFocus(id) 请求焦点,或用 moveFocus("next"|"previous"|"left"|"right"|"up"|"down") 导航; getFocusedId() 返回当前焦点的稳定 ID。键盘 Tab、Shift+Tab 和方向键会走同一套显式邻居 与顺序规则,游戏手柄或自定义输入层可直接调用 moveFocus()。
切换统一主题
内置 dark / light 预设共享圆角、边框、间距与字体缩放,只切换配色。推荐 setTheme("dark") / setTheme("light"),用 getTheme() 读取当前名;也可用 setThemeDark() / setThemeLight()。DPI 用 setScale(),主题几何会按比例缩放。
需要在同一界面中区分编辑器面板、游戏 HUD 或嵌入工具时,在添加容器后调用 setItemTheme("dark"|"light"|"inherit")。主题覆盖作用于该容器及其整个子树,嵌套容器 可以再次覆盖;离开子树后自动恢复外层主题。该设置随 UI JSON 资产保存和加载。 对于刚通过 beginWindow、beginCard、beginGroup 等打开且仍在构建的当前容器,使用 setThemeScope();这样根 Window 也能拥有独立于全局预设的主题。
可复用的局部视觉样式使用命名 StyleClass。先用 defineStyleClass(name, parent) 声明类, 再用 setStyleClassColor(name, "text"|"background"|"border"|"accent", r, g, b, a) 和 setStyleClassMetric(name, "padding"|"rounding"|"alpha", x, y) 配置稀疏 token。三个修改 接口返回稳定状态字符串(如 applied、unknown-parent、inheritance-cycle),不会静默 接受无效定义。setItemStyleClass() 设置最近控件,setStyleScope() 设置当前容器并通过 ImGui 样式栈自然作用于子树;子节点只覆盖自己声明的 token。命名继承在建树或 patch 时 解析为节点快照,因此逐帧渲染不做字符串注册表查询。类名随 version 4 JSON 保存;注册表 由应用启动脚本重新声明,加载旧版资产时缺失字段按无样式处理,未知字段仍忽略。
保留树和 MCP EditorHost 的 Button、Checkbox、单值 Slider、Combo 与文本输入共享同一组 原生控件原语,因此尺寸、禁用态和编辑结果语义一致。文本输入持有 UTF-8 std::string 并在 ImGui 请求扩容时增长,不再使用 1 KiB/512 字节固定缓冲;SDL/ImGui 后端继续负责平台 IME。
轻量动效由 UiTweenDriver 驱动,在 beginFrameAndRender() / tickTweens(nowMs) 中推进:
animateHostPos(x, y, durationMs, ease = "smoothstep", delayMs = 0)— 宿主窗口位置animateHostSize(w, h, durationMs, ease, delayMs)— 宿主显式尺寸animateHostOverlayAlpha(alpha, durationMs, ease, delayMs)— 叠加层背景透明度animateItemOpacity(id, opacity, durationMs, ease, delayMs)— 节点瞬态 opacity(不写 JSON / StyleClass)animateItemPos(id, x, y, durationMs, ease, delayMs)— 绝对定位节点偏移(自动absolute)
durationMs <= 0 且无 delay 时立即跳到终点;再次对同一属性调用会替换未完成的补间。 cancelHostTweens() / cancelItemTweens(id) / cancelAllTweens() 取消选中宿主上的待定补间。 缓动名与 Motion/Math 对齐(smoothstep 为默认,另有 linear、Quad/Cubic/Sine/Expo、Back/Elastic/Bounce); 未知名称按 linear 处理。
需要 loops / Sequence / Punch,或与 SimulationStep 同拍时,用 eve.Animation() 的 Motion 经 UI 侧 sink 推送写回(animation 模块启用时编译 UiMotionSinks):
无 animation 的裁剪构建仍保留上方 animate* 便利路径;sink 桥接 TU 会被排除。
getLayoutDiagnostics() 输出各节点测量尺寸和横纵溢出, getAccessibilitySnapshot() 输出语义节点的 role/name/description、enabled 与 focused 状态, 供调试器和自动化读取而不触碰 ImGui 内部对象。
常见问题
- 每帧重新 mount,丢失输入焦点与控件状态。
- 多个控件使用同一 ID。
- 未循环消费全部 click/change,队列在后续帧才清空。
API 快查
下列方法名来自当前 Squirrel 绑定;同一模块创建的辅助对象(例如 World、Body、Source)的方法也列在这里。
beginBuild()、beginCard()、beginChild()、beginCollapsing()、beginColumn()、beginFlex()、beginFrameAndRender()、beginGroup()、beginList()、beginMenu()、beginMenuBar()、beginNinePatch()、beginRow()、beginSidebar()、beginSplitPane()、beginStatusBar()、beginScrollList()、beginToolbar()、beginToolbox()、beginWindow()、bindOwner()animateHostPos()、animateHostSize()、animateHostOverlayAlpha()、animateItemOpacity()、animateItemPos()、cancelHostTweens()、cancelItemTweens()、cancelAllTweens()、tickTweens()、getHostTweenCount()、getItemTweenCount()、badge()、button()、checkbox()、colorPalette()、combo()、consumeChange()、consumeClick()、consumeDrop()、dispatchEvents()、dragDropSupport()、end()、getChecked()、getColorA()、getColorB()、getColorG()、getColorR()、getDropOrigin()、getDropSource()、getDropText()、getDropType()、getName()getFocusedId()、getScale()、getTheme()、getValue()、getValueText()、icon()、iconButton()、initBackend()、inputText()、isBackendReady()、listItem()、mountBuild()、moveFocus()menuItem()、mountBuildAs()、mountSimple()、progress()、remountBuildAs()、sameLine()、searchField()、sectionHeader()、select()、separator()、setChecked()requestFocus()、setEnabled()、setFlexAlign()、setFlexJustify()、setHostAnchor()、setHostLayer()、setHostModal()、setHostMovable()、setHostOverlay()、setHostOverlayAlpha()、setHostPercent()、setHostPos()、setHostResizable()、setHostSize()、setHostVisible()、setHostWorldAnchor()、clearHostWorldAnchor()、setHostWorldEdgePolicy()、setHostWorldDistanceScale()、setHostWorldOverlap()、getHostWorldState()、getHostWorldScreenX()、getHostWorldScreenY()、setImageCornerRadius()、setImageNinePatch()、setImageTint()、setImageUv()、setItemAbsolute()、setItemAccessibility()、setItemAlignSelf()、setItemDragSource()、setItemDropTarget()、setItemEnabled()、setItemSelected()、setItemFlexBasis()、setItemFlexGrow()、setItemFlexShrink()、setItemFocusMode()、setItemFocusNeighbors()、setItemFocusOrder()、setItemMargin()、setItemMaxSize()、setItemMinSize()、setItemMouseFilter()、setItemPadding()、setItemPercent()、setItemSize()、setItemTabIndex()、setItemTheme()、setItemTooltip()、setNavGamepad()、setNavKeyboard()、setScale()、setText()setTextWrap()、setTheme()、setThemeDark()、setThemeLight()、setThemeScope()、defineStyleClass()、setStyleClassColor()、setStyleClassMetric()、clearStyleClasses()、setItemStyleClass()、setStyleScope()、setColor()、setValue()、setValueText()、setVisible()、slider()、spacer()、switch()、text()、textWrapped()、wantCaptureKeyboard()wantCaptureMouse()、registerTexture()、unregisterTexture()、setImageTextureId()、setImageNinePatchFile()image()、imageButton()、ninePatch()、onClick()、onChange()、saveTreeJson()、loadTreeJson()、getStats()、getLayoutDiagnostics()、getAccessibilitySnapshot()viewport()、viewportCanvas()、viewportHovered()、viewportActive()、viewportMouseX()、viewportMouseY()、viewportDragDX()、viewportDragDY()、viewportWheel()inspect()、inspectClose()、inspectRefresh()、inspectSelectClass()、inspectObject()、inspectSetPickHandler()、inspectPickScene()、inspectAddInstance()、propertySchema()dbOpen()、dbClose()、dbRefresh()、dbSelectClass()、dbRegister()、dbCreateInstance()、dbUnregister()editorOpen()、editorClose()、editorSelectPanel()、sceneOpen()、sceneClose()、sceneSelectNode()、sceneSetPickHandler()
propertySchema(instance) 返回脚本实例经反射派生的属性 schema 表(typeId / version / properties[],含 path、kind、displayName、readOnly、数值约束与 choices)。editor="color"|"vec2"|"vec3"|"vec4"|slider|combo 等 attribute 决定 kind; 可编辑面板请用 inspectObject(instance),不要把 schema 表当成第二权威状态。
引擎纹理控件
image() 和 imageButton() 可以显示任意引擎 Texture。先用 textureId = ui.registerTexture(texture) 获得后端中立句柄,再用 ui.setImageTextureId(id, textureId) 绑定到当前 Host 的控件。纹理不再使用时,先把相关 控件设为 0,再调用 ui.unregisterTexture(textureId);这样 Vulkan 与 WebGPU 后端都能 释放对应资源。UV、色调、九宫格与圆角仍通过 setImageUv/Tint/NinePatch/CornerRadius 组合。
桌面 Drag & Drop
Drag & Drop 仅在 Windows、macOS、Linux 桌面构建启用;Android、iOS 和 Web/WASM 的 dragDropSupport() 返回 unsupported-platform,不会伪装成功。setItemDragSource(type,
text) 和 setItemDropTarget(type) 作用于刚添加且会产生交互 item 的控件;目标类型可用 * 接受全部内部 payload,file 接受操作系统文件拖入。
事件保存 owning UTF-8 文本快照,源节点或目标节点在消费前销毁不会留下悬空引用。
内嵌渲染视口(Viewport)
ui.viewport(id, w, h) 声明一个内嵌渲染目标控件:它维护一个离屏 Canvas (尺寸跟随控件矩形),游戏在 eve_render 里先把 2D(gfx.setCanvas + 立即模式绘制) 或 3D(gfx.renderScene3DToCanvas(canvas, camera))渲染进去,然后 ui.beginFrameAndRender() 会把该 Canvas 纹理显示在控件中。视口交互输入 (悬停、按住、控件本地鼠标坐标、拖拽增量、滚轮)通过 viewportHovered/Active/MouseX/ MouseY/DragDX/DragDY/Wheel(id) 每帧读取。完整示例见 examples/terrain-editor(高度图地形 + orbit 相机 + 抬高/压低笔刷)。
3D 世界锚点(World Anchor)
世界锚点把整个 UI Host 投影到当前活动 Camera3D 的屏幕位置,适合单位名牌、交互提示、 任务标记和编辑器 3D gizmo 标签。先选择 Host,再调用 setHostWorldAnchor(x, y, z);clearHostWorldAnchor() 恢复普通屏幕布局。
setHostWorldEdgePolicy("hide"|"clamp") 控制目标离开视口后的行为; setHostWorldDistanceScale(enabled, referenceDistance, minScale, maxScale) 配置按相机距离缩放。 相机不可用或目标在相机背面时 Host 自动隐藏,不会沿用上一帧坐标。世界坐标由游戏场景 持有者负责更新;UI 只保存投影输入和逐帧派生的屏幕状态。当前版本不做深度遮挡判断, 需要被场景几何遮挡的面板应继续使用 3D mesh/material。
大量名牌同时出现时,可用 setHostWorldOverlap(true, priority, padding,
maxDisplacement) 开启确定性的屏幕矩形避让。高优先级、近距离 Host 先占位,其余 Host 在限定距离内上下寻找空位;没有可用位置时状态变为 crowded 并暂停渲染。 getHostWorldState() 返回 visible、behind-camera、outside-viewport、no-camera、 crowded 或 disabled;最终屏幕坐标可通过 getHostWorldScreenX/Y() 查询。
MCP EditorHost 脚本接口
eve mcp 会在根表建立 eve.host,供项目脚本创建和维护 AI 编辑器。它与 eve.UI() 的游戏内 retained UI 相互独立,但都运行在同一个 Squirrel VM 中。
- 窗口与状态:
status()、openWindow()、closeWindow()、windowState()。 - View 与交互:
applyEditor()、removeEditor()、setValue()、events()、widgetRect()、capture()、save()。 - ViewModel:
registerVM()、unregisterVM()、runScript()。 - 热更新:
reloadResource(path)手动重载项目内的mcp.nut、mcp/*.nut、editors/*.vm.nut或editors/*.editor.json;hotReloadStatus()返回 watcher、 成功/失败计数与最近诊断。正常保存会由 MCP 主机自动触发,无需重启。
Canvas 高度驱动的父布局
ParentScalerSettings、ParentScalerInput、ParentScalerState、ParentScalerOutput 和 evaluateParentScaler(state,output,settings,input) 对应 Pcg ParentScaler。scaleWithCanvas=true 且存在 Canvas 和目标时,FullScreen 输出原始 canvasHeight;partScreen=true 时按 Pcg 公式将其钳制到 [0.1,maxHeight]。只有 Canvas 高度变化或 lastScaleHeight==0 时才令 applyHeight=true,调用者再把 height 应用到每个仍有效的目标 Rect,保留各自宽度。
脚本字段包括 scaleWithCanvas、partScreen、maxHeight、hasCanvas、targetCount、canvasHeight、 lastScaleHeight、applyHeight 和 height。状态与输出由调用者持有;零目标不会消耗高度变化,后续加入目标 仍能获得写入。无效目标由调用者跳过,负目标数或非有限高度在修改 state/output 前失败。
使用要点
- 模块对象和它创建的资源对象应保存在全局或实体状态中,不要在每帧重复创建。
- 带
update(dt)的系统应在eve_update调用;绘制方法应在eve_render调用。 - 参数约束、默认值和返回类型以对应模块头文件及
addFunc绑定为准;本文 API 快查与当前源码同步生成。
源码: src/modules/ui/ 相关测试: 在 test/ 中搜索 ui。
PhotoMode 的 Photo 域由 PcgPhotoModePhotoAuthority 保存并统一发布。它覆盖场景身份、 截图分辨率、EXR/JPG/PNG/TGA 格式、加载保存设置、关闭时恢复,以及 FPS、准星和三分法 叠层开关。分辨率索引 0 使用当前 drawable 尺寸,1..9 精确映射 Pcg 的 640×480 到 7680×4320 档位;screenshotSize() 与 screenshotExtension() 供捕获和 UI 调用方读取。 authority 只保存稳定值,不持有 widget;实际叠层仍由 UIHost 使用现有 UI API 构建。
Pcg 截图按钮与颜色预览同步
AutoAssignTakePhotoEvent 的启动期按钮接线使用现有 ui.onClick(id, callback):回调中调用 gfx.saveFramePng(path);首次启用 readback 后若尚无已呈现帧会返回 false,下一帧再次调用。该组合对应 Unity Button.onClick -> ScreenShotter.TakeHiResShot,并允许调用者显式选择输出路径。
eve.PcgColorPreviewSync() 对应 ColorPreviewSync.OnEnable。调用 sync(r,g,b,a) 后,通过 getRed()、getGreen()、getBlue()、getAlpha() 取得按钮 highlighted color:纯黑源色映射到 (0.5,0.5,0.5,1),其余颜色严格乘以原脚本硬编码的 2.5。非有限输入返回结构化错误且保留原状态。
PcgDraggableWindow
eve.PcgDraggableWindow() 对应照片模式的 DraggableUIWindow。configure(x,y,width,height,screenWidth,screenHeight,canvasScale) 保存初始 anchored position;drag(deltaX,deltaY) 按 canvas scale 换算并复现 Pcg 的 X 负向、Y 正向边界钳制。pointerDown(middleButtonDown) 总是产生置顶请求,中键同时复位;用 consumeBringToFront() 消费请求,并通过 getX() / getY() 将结果交给 ui.setHostPos。
PcgPhotoModePanels
eve.PcgPhotoModePanels() 保留七个 Pcg 照片模式页签的固定编号:Camera=0、Unity=1、Terrain=2、Lighting=3、Water=4、PostFX=5、PhotoMode=6。select(panel) 原子关闭其余六个面板并选择目标;getSelected()、isActive(panel)、getRevision() 提供快照,consumeScrollReset() 通知调用者重建 scroll content 并把滚动条置顶。
eve.PcgPhotoModePanelButton() 通过 configure(nonSelectedRGBA,selectedRGBA) 保存两组颜色,setSelected() 同时控制按钮 normal/selected 色和面板布局开关。读取接口为 getNormalR()、getNormalG()、getNormalB()、getNormalA()、getHighlightR()、getLayoutEnabled()、getSelected();highlight 始终来自选中色,与 Pcg 注册逻辑一致。
PcgPhotoModeColorPicker
eve.PcgPhotoModeColorPicker() 实现照片模式颜色选择器。open(r,g,b,a,hdr)、setLast(...)、setFocusedName(name)、close() 管理打开、旧值和标题;setRed()、setGreen()、setBlue()、setHdr() 对应四条滑杆,reset() 恢复旧值。读取接口包括 getRed/Green/Blue/Alpha、getPreviewRed/Green/Blue、getHdrSwatch()、getHdrEnabled()、getOpen()、getFocusedName()、getChangeRevision() 和 consumeBringToFront()。HDR alpha 大于 1 或小于 0 时严格保留 Pcg 打开阶段和预览阶段的两次 RGB 乘法。 预览通道的完整方法名为 getPreviewRed()、getPreviewGreen()、getPreviewBlue()。
PcgScreenshotSavedNotice
eve.PcgScreenshotSavedNotice() 对应截图保存提示。configure(enabled,showSeconds) 设置开关和持续时间;截图成功后调用 request(path),在该帧渲染结束调用 endFrame(unscaledNow) 才显示,再用 tick(unscaledNow) 到期隐藏。getVisible()、getPathVisible()、getPath()、getPending() 返回 UI 快照。重复 request 会取消旧显示并从新的 end-of-frame 重新计时。
PcgTooltipManager
eve.PcgTooltipManager() 合并移植 Tooltip、TooltipManager、TooltipProfile 和 TooltipTrigger。configure(interactionMode,theme,delay,bottomOffset,topOffset,wrapLimit) 中 interactionMode 为 UI=0、SceneObjects=1、Both=2,theme 为 Light=0、Dark=1。addTooltip()、removeTooltip() 管理 profile;enter(source,id,now) 做 Pcg 的首个 substring 匹配,enterText(source,content,header,now) 对应 Trigger 自带文本,exit(source)、hide() 和 tick(now,mouseX,mouseY,screenWidth,screenHeight,anyInput) 管理延迟、位置与隐藏。
状态通过 getVisible()、getPending()、getHeader()、getText()、getHeaderVisible()、getWrapEnabled()、getPivotX()、getPivotY()、getBackgroundR()、getForeground()、getCount() 读取。
PcgControllerSelection
eve.PcgControllerSelection() 对应 UIControllerSelection。用 add(name,controllerType,widgetId) 按 Inspector 顺序登记说明项,refresh(currentController) 选择第一个类型匹配项并生成互斥可见快照;没有匹配时返回成功但 value=false,保留之前状态。通过 getCount()、getSelectedIndex()、getVisible(index)、getWidgetId(index) 读取,再用 ui.setVisible 应用到稳定 widget id。
PcgPhotoModeRuntimeUI
eve.PcgPhotoModeRuntimeUI() 对应 PhotoModeUtils 与 PhotoModeUIHelper 的运行时控件状态。configure(kind,name,value,current,min,max,imageFound) 的 kind 从 Field=0 到 Vector3=13。各 get*Visible() 返回原 prefab 的互斥可见快照,getInitialCallbackRevision() 记录配置后主动派发初值的次数。
PcgLoadingScreen
eve.PcgLoadingScreen() 接收 TerrainLoader 的四类加载事件。configure(fadeOutSpeed) 设置背景每秒 衰减的 alpha;onLoadProgressStarted() 显示画布并把背景和进度复位, onLoadProgressUpdated(progress) 更新进度且在值达到 1 时自动执行 onLoadProgressEnded()。 结束会隐藏进度条和文本并开始淡出,调用方每帧用 tick(deltaTime) 推进;alpha 到零后画布关闭。
超时诊断使用 beginTimeout() 开始事务,addMissingScene(terrainName,impostorName) 返回索引,随后用 addRegularReference(index,name) 和 addImpostorReference(index,name) 收集仍引用该场景的对象,最后 endTimeout() 原子发布与 Pcg 相同结构的诊断并开始淡出。getCanvasVisible、getProgressVisible、 getTextVisible、getFading、getProgress、getBackgroundAlpha 和 getTimeoutMessage 返回 UI 快照。
markSliderUsed()、setSliderValue()、applyFloatInput() 保留滑杆与文本框同步规则;updateWrap(text) 复现 68 字符 metrics 换行,包括无空格硬切时跳过切点字符的原始行为。
状态查询包括 getKind()、getName()、getValueText()、getValue()、getMinimum()、getMaximum()、 getWholeNumbers()、getUsingSlider()、getInputRefresh()、getLabelVisible()、 getSecondLabelVisible()、getSliderVisible()、getInputVisible()、getToggleVisible()、 getButtonVisible()、getDropdownVisible()、getImageVisible()、getHeaderVisible()、getColorVisible()、 getVector2Visible()、getVector3Visible() 和 getInitialCallbackRevision()。
PcgPhotoModeValues
eve.PcgPhotoModeValues() 完整保存 Pcg PhotoModeValues 的 102 个字段及原始默认值。字段名沿用 m_* 配置名;getFieldCount/getFieldName/getFieldType 提供稳定 schema,类型编号为 Bool=0、Int=1、Float=2、String=3、Color=4。使用对应 setBool/setInt/setFloat/setString/setColor 与 getBool/getInt/getFloat/getString;颜色先 selectColor(name),再读取 RGBA。resetDefaults() 原子恢复原始配置。
snapshotJson() 输出 eve.ui.pcg-photo-mode-values schema version 1,并包含全部 102 字段;restoreJson(json) 要求精确 schema、版本和字段集合,拒绝未知或缺失字段,解析到临时候选对象后一次性交换,因此任何错误都保留当前配置。
PcgPhotoModeSession
eve.PcgPhotoModeSession() 对应 PhotoMode.cs 的进入、加载和退出事务。begin(capturedJson,savedJson,loadSaved,savedEver,currentPipeline,savedPipeline,sceneName,lightingProfile) 保存进入前快照,仅在管线、场景和光照配置均匹配时加载保存值。getLoadDecision() 返回 Current=0、Saved=1、SaveCurrent=2。replaceWorkingJson() 原子更新运行值;end(resetOnDisable,applicationPlaying) 选择工作值或进入前快照作为 outputJson()。
退出后 getRemovePhotoCameraRequested() 和 getUnfreezePlayerRequested() 始终为真;只有两个退出参数均为真时 getRestoreRequested() 为真。调用者按这些请求恢复各权威模块并清理临时对象。
脚本方法:begin()、replaceWorkingJson()、end()、workingJson()、outputJson()、getActive()、getRestoreRequested()、getRemovePhotoCameraRequested()、getUnfreezePlayerRequested()、getLoadDecision()、getRevision()。
PcgPhotoModeRanges
eve.PcgPhotoModeRanges() 完整保存 PhotoModeMinAndMaxValues 的 57 组范围。getCount/getName 按原声明顺序枚举;select(name) 后用 getMinimum/getMaximum/getIntegral 读取。setRange() 原子替换自定义范围,整数范围要求整数端点;clamp(name,value) 返回钳制结果。m_densityVolumeFogDistance 的默认上限保留正无穷。
Pcg PhotoMode 跨域应用事务
PcgPhotoModeApplyPlan 比较两个完整 PcgPhotoModeValues 快照,按 Pcg 源字段声明顺序输出变更,并把字段路由到 Photo、System、Graphics、Camera、Streaming、Weather、Lighting、Water、PostFx、Terrain、Grass 或 Audio 权威域。executeRegistered() 通过 eve.photo-mode.apply capability 同步执行;未安装提供者会返回可观察的 Unsupported 错误。任一字段失败时,先前字段按逆序恢复,getRollbackComplete() 报告恢复是否完整。脚本可用 compileJson、命令枚举和执行状态来驱动照片模式。
脚本方法:compileJson、executeRegistered、getCommandCount、getCommandField、getCommandDomain、getAppliedCount、getRollbackComplete、getRevision。
照片模式注册执行器会把 m_globalVolume 通过现有 IAudioQuery 写入真实 Audio master volume。其他字段由实现 IPhotoModeFieldSink 的模块 listener 接收;每个字段必须恰有一个提供者,缺失或重复所有者都会使事务失败并触发逆序回滚。
Pcg Lighting profile 身份
PhotoMode 的 m_isUsingPcgLighting 与 m_selectedPcgLightingProfile 由 PcgPhotoModePhotoAuthority 作为持久化身份联合保存。lightingProfileMatches(currentProfile) 同时比较 profile 索引和 Pcg/custom 模式;PcgPhotoModeSession.begin() 也使用这两个条件与场景名、渲染管线共同决定是否允许加载旧设置。