Skip to content
On this page

3D 场景编辑器

ChaUI 2.0.0 把 3D 菜单作为页面编辑器的一部分:scene 是带独立深度缓冲和相机的场景视口,scene_model 是其中的 ChaEngine 模型节点。前景、后景和普通 2D 组件仍按页面层级组合,场景内部的模型则按真实深度互相遮挡。

支持范围

3D 场景编辑器、场景后处理、阴影编辑和视口交互已经同步到当前全部 ChaUIMod 客户端适配。使用 ChaEngine 模型时还需要安装同版本的 ChaEngineMod;当前可用组合为 Forge 1.16.5、Forge 1.20.1,以及 NeoForge 1.21.11.21.41.21.81.21.11。这些组合统一使用场景专用 Shader 和 V4 provider。其他 ChaUIMod 版本仍可编辑、保存场景配置,但没有对应 ChaEngineMod 时模型节点会按不可用状态安全降级。

准备模型目录

客户端需要同时安装版本匹配的 ChaUIMod 与 ChaEngineMod,并取得服务器下发的 ChaEngine 模型资源。编辑器通过可选的 ChaEngine provider 读取模型目录,不会创建伪实体,也不会靠实体名称或 NBT 猜模型。

目录模型由两个字段唯一确定:

  • catalog: entity:实体模型目录;
  • catalog: block:方块模型目录;
  • catalog: item:物品模型目录,只列出配置了三维模型的物品;
  • modelId:所选目录中的真实模型 ID。

编辑器的模型来源使用下拉框选择:

  • entity / block / item:从 ChaEngine 的目录中选择模型。首次显示相关属性时会自动读取目录,也可以搜索和手动刷新;
  • custom:直接填写 ChaEngine 资源的相对路径;
  • custom_entity:直接填写资源路径,并额外填写一个实体类型标识。

直接资源路径都相对于 ChaEngine 模型资源根目录,只能使用 / 分隔,不能以 / 开头,也不能包含 ... 或 Windows 盘符。模型文件和材质文件必填,动画文件可留空。例如:

yaml
source: custom
model: models/menu/guardian.geo.json
animation: animations/menu/guardian.animation.json
texture: textures/menu/guardian.png

custom_entityentityType 只用于标识该自定义模型对应的生物类型(例如 some_mod:creature),不会在世界中额外生成实体、AI 或碰撞体;模型仍由填写的资源路径直接渲染。

调整场景光照

选中 scene 后,属性面板会把“场景光照”作为独立分区,依次显示光照开关、环境亮度、曝光、主光方向、补光方向、阴影开关、阴影强度、柔和度、偏移和质量。内置与外置编辑器使用相同顺序;连续数值支持悬停后左右拖动,也可以单击输入精确值,修改会立即刷新场景预览。阴影质量使用下拉框选择。

yaml
scene:
  lightingEnabled: true
  ambientLight: 0.55
  exposure: 1.25
  keyLightX: 0.2
  keyLightY: 1.0
  keyLightZ: -0.7
  fillLightX: -0.2
  fillLightY: 1.0
  fillLightZ: 0.7
  shadowEnabled: true
  shadowStrength: 0.55
  shadowSoftness: 1.5
  shadowBias: 0.0015
  shadowMapSize: 1024

ambientLight 默认 0.55,范围是 0.02.0,用来抬高背光面和暗部;exposure 默认 1.25,范围是 0.14.0,用来调整整体明暗。主光和补光的三个坐标组成方向向量,不是光源的世界坐标,渲染时会自动归一化;主光方向表示模型表面指向主光的方向。

shadowStrength 默认 0.55、范围为 0.01.0shadowSoftness 默认 1.5、范围为 0.04.0shadowBias 默认 0.0015、范围为 0.00.02。这三个字段可以左右拖动,步长依次为 0.010.10.0001。质量下拉框提供 51210242048,默认 1024;分辨率越高越清晰,也会增加显存和渲染开销。

只有主光会投影,环境光和补光不会。同一场景里已成功生成有效范围的可见模型会互相投射和接收阴影;透明纹理使用二值阴影,透明空洞不投影,半透明部分不会形成渐变透明影子。新建场景会显式默认开启阴影,旧页面缺少 shadowEnabled 时默认关闭,打开或保存其他字段不会偷偷补写该开关。

lightingEnabled: false 时,模型光照阶段直接使用纹理原色、顶点 tint、overlay、原 alpha 与自发光,环境亮度、曝光、法线、两个方向和阴影都不再参与该阶段;单独启用的场景后处理仍会作用于最终画面。阴影范围尚未准备好或阴影资源失败时,会继续显示无阴影工作室光照。专用 Shader 只影响 ChaUI 3D 场景模型,不影响普通世界中的 ChaEngine 模型。当前不包含环境贴图、Bloom、SSAO、真实次表面散射、光源颜色或距离衰减。

这一功能要求对应版本的 ChaEngineMod 提供 ChaUISceneModelProviderV4 / API_VERSION=4。旧 V1、V2、V3 provider 不会被探测或作为固定光照回退;版本不匹配时只禁用对应模型节点,页面其他内容仍正常工作。

调整模型材质和场景后处理

选中 scene_model 后,“模型材质”分区可以调整金属度、粗糙度、自发光颜色和自发光强度。选中 scene 后,“场景后处理”分区可以选择色调映射,并调整亮度、对比度、饱和度、暗角、色差、锐化和色彩平衡。内置与外置编辑器使用同一组字段;连续数值可以左右拖动,也可以单击输入精确值。

yaml
scene:
  toneMappingEnabled: true
  toneMapping: aces
  brightness: 0.0
  contrast: 0.0
  saturation: 1.0
  vignetteEnabled: false
  vignetteAmount: 0.5
  vignetteHardness: 1.5
  chromaticAberrationEnabled: false
  chromaticAberration: 0.003
  sharpenEnabled: false
  sharpenStrength: 0.3
  colorBalanceEnabled: false
  colorBalanceShadowsR: 0.0
  colorBalanceShadowsG: 0.0
  colorBalanceShadowsB: 0.0
  colorBalanceMidtonesR: 0.0
  colorBalanceMidtonesG: 0.0
  colorBalanceMidtonesB: 0.0
  colorBalanceHighlightsR: 0.0
  colorBalanceHighlightsG: 0.0
  colorBalanceHighlightsB: 0.0
yaml
sceneModel:
  metalness: 0.15
  roughness: 0.65
  emissiveColor: "#FFFFFF"
  emissiveStrength: 0.0

金属度、粗糙度范围都是 0.0..1.0;自发光强度范围为 0.0..2.0。亮度范围 -0.5..0.5,对比度 -1.0..1.0,饱和度 0.0..2.0,暗角程度 0.0..1.5、硬度 0.5..3.0,色差 0.0..0.02,锐化 0.0..1.0,色彩平衡的每个 RGB 值为 -0.3..0.3。建议先保持中性默认值,再一次只打开一个效果观察变化。

创建顺序

  1. 使用 /chaui editor <页面ID> 打开页面编辑器。
  2. 在组件库中先添加一个 scene,并拖动它确定 3D 视口在页面中的位置和大小。
  3. 选中场景后,在模型来源下拉框中选择 entityblockitemcustomcustom_entity。目录来源可以搜索并刷新后从下拉框选择 modelId;直接来源则填写模型、动画(可选)和材质相对路径,custom_entity 还要填写 entityType
  4. 新模型会创建为该 scene 的直接子组件,类型是 scene_modelscene_model 不能放在页面根级,也不能挂到普通布局组件下。
  5. 继续加入图片、文字或按钮作为 2D 前景与后景;用页面组件顺序和 z 控制它们与整个 scene 视口之间的层级。

选择与变换

直接点击模型身体即可选择整体模型,不需要手工配置碰撞盒。ChaEngineMod 会在模型首次成功渲染时,根据实际提交的几何生成固定局部 AABB;在该范围尚未生成前,模型可以显示但暂时不能点选。

选中模型后可以使用以下工具:

工具快捷键用途
选择Q只选择模型,不开始变换
移动V沿红、绿、蓝轴修改 x/y/z
旋转R修改 rotationX/rotationY/rotationZ
缩放E修改统一的 scale

拖动开始时会捕获原始变换;松开后整次拖动只形成一条撤销记录。按 Esc 可以取消当前拖动并恢复原值。属性栏直接输入数值也会写入同一页面草稿,并可使用撤销、重做恢复。

选中场景或场景模型时,视口左上角会显示这组快捷键提示。属性栏中的连续数值(布局 x/y/width/height、实体 yaw/pitch/roll/scale/offset、模型位置/旋转/缩放、镜头与光照参数)悬停会显示左右拖动标记;单击仍进入文本编辑,按住左键横向拖动才会改值。布局尺寸每像素调整 1,位置和镜头坐标每像素调整 0.1,角度每像素调整 1 度,缩放每像素调整 0.05;按住 Shift 精调,按住 Ctrl 粗调。拖动只在当前字段第一次产生撤销记录,公式、文本、枚举和布尔字段不启用拖动。

普通页面元素的画布拖动需要左键按住 0.5 秒。若过早移动,编辑器会在屏幕上方显示红色“请长按 0.5 秒后拖拽”提示并从上方淡入;松开鼠标后提示向上淡出,页面布局不会被提前移动。

外置 Swing 编辑器遵循同一门槛和提示动画;提示固定在外置窗口顶部居中,且不会拦截页面树和属性栏的鼠标事件。

第一版只选择整个 scene_model,不支持骨骼、part 或单个三角形点击。需要多个点击目标时,应拆成多个模型节点或叠加普通 ChaUI 组件。

调整编辑相机

鼠标位于场景视口内时:

  • 按住中键拖动:围绕目标点旋转观察;
  • 按住 Shift + 中键 拖动:平移观察目标;
  • 滚轮:拉近或拉远。

这些手势只改变当前标签页的临时预览,不会在浏览模型时不断改写页面 YAML。切换选中组件不会重置镜头,每个场景分别保留自己的临时镜头。切换标签后,每个标签会恢复自己的选择、工具和临时镜头。

内置编辑器右侧属性栏支持拖拽滚动条,也可以点击轨道跳转。外置编辑器的镜头记录中,点击“加载”会立即应用到当前临时镜头,“复制”按钮会复制对应脚本。

编辑模式中的显隐优先级是:临时显隐 > 条件显隐 > 显示。临时状态只影响编辑器,预览与实际页面仍按配置运行;父级隐藏仍会隐藏其子树。当临时状态与配置计算结果相反时,即使切换选中组件,也会保留特殊边框和左上角标签:青绿色表示“临时显示”,粉色表示“临时隐藏”。这些临时状态不写入页面 YAML。

记录镜头状态

想把某个观察角度留给菜单按钮或脚本使用时,不需要手抄坐标:

  1. 先用中键、Shift + 中键 和滚轮调整到想要的画面。
  2. 选中对应的 scene。属性栏的“当前临时镜头”会显示相机、目标和上方向的九个实际值。
  3. 点击“打开镜头记录”(记录当前镜头),在二级对话框的“记录名称”中输入一个 ID,例如 menu_focus开场boss-zoom
  4. 点击“记录当前”。同名状态已经存在时,编辑器会先询问是否覆盖。

记录后的状态会保存在这个场景自己的 scene.cameraStates 中。状态 ID 可以使用中英文、数字、_-;不要使用空格、斜杠或点号。记录列表也集中在这个二级对话框中:

  • 加载/应用只把状态放回当前临时镜头,便于继续检查画面,不会立即改写 YAML;
  • 删除移除该状态,并进入正常撤销/重做记录;
  • 复制按钮会按场景组件 ID 和状态 ID 生成 component("场景ID").scene.moveTo("状态ID", 800) 并复制到系统剪贴板;对话框不再提供时长输入,复制结果固定使用 800 毫秒,需要其他时长时直接修改脚本第二个参数;
  • 点击“还原镜头”只把临时镜头恢复到场景默认的九个相机值,不删除任何记录;
  • 只有点击编辑器顶栏“保存”后,记录/覆盖/删除才会写入页面文件。

下面是一个可直接放在 scene 数据块里的镜头状态。每个状态必须完整保留九个坐标:

yaml
scene:
  cameraStates:
    menu_focus:
      cameraX: 0
      cameraY: 0
      cameraZ: 8
      targetX: 0
      targetY: 0
      targetZ: 0
      upX: 0
      upY: 1
      upZ: 0

镜头状态只保存这九个观察值,不保存投影方式、FOV、正交高度、裁剪距离或场景光照;主光、补光和 lightingEnabled 始终在“场景光照”区域独立配置。

用脚本切换镜头

在页面运行时,可以让场景从当前镜头平滑移动到已记录的状态。把下面一行放进按钮或其他组件的事件脚本中:

js
component("stage").scene.moveTo("menu_focus", 800)

这会让 ID 为 stage 的场景在 800 毫秒内使用五次 smoothstep 缓入缓出曲线移动到 menu_focus。第二个参数单位固定为毫秒:0 会立即切换;非零值可以是 13600000。客户端在每个渲染帧重新采样相机,不受 20Hz 游戏 tick 限制。再次调用时会从镜头当时已经移动到的位置继续,不会先跳回旧起点。

例如,下面这个按钮被点击后会回到上面记录的菜单镜头:

yaml
- id: return_to_menu
  type: button
  layout:
    x: 20
    y: 20
    width: 120
    height: 20
  button:
    label: 返回菜单镜头
  events:
    leftClick: |
      component("stage").scene.moveTo("menu_focus", 800)

镜头移动只属于当前玩家这一次打开页面的运行状态,不会被脚本写回页面 YAML。若脚本随后直接修改 cameraX/Y/ZtargetX/Y/ZupX/Y/Z 中任一个普通相机值,当前的平滑移动会停止并使用新值。

常见错误是状态 ID 写错、删除后仍调用旧名称,或只填写了部分九坐标。保存前会提示这类配置问题;脚本调用时则应确认目标场景和状态都存在。

动画预览

选中 scene_model 后,动画面板会向 provider 查询该模型已有的动画。可以选择动画、播放或暂停、切换预览循环,并拖动时间位置检查姿态。

  • 选择动画会更新持久字段 sceneModel.animation
  • sceneModel.animationLoop 是页面保存的运行时循环设置;
  • 播放/暂停状态、预览循环开关和拖动到的预览时间只属于当前编辑会话,不写入页面;
  • 切换页面标签、切换模型或关闭编辑器时会释放旧动画预览实例。

第一版不提供关键帧时间线、动画曲线或骨骼编辑。

完整 YAML 示例

下面的页面包含一个透视场景和一个循环动画模型。sceneModel 是数据块名称,组件类型仍写作 scene_model

yaml
id: scene_menu_demo
version: 1
title: 3D 场景菜单
size:
  width: 640
  height: 360
coordinateMode: absolute
display:
  mode: screen
elements:
  - id: stage
    type: scene
    z: 10
    layout:
      x: 80
      y: 40
      width: 480
      height: 280
    scene:
      projection: perspective
      fov: 45
      near: 0.01
      far: 100
      cameraX: 0
      cameraY: 0
      cameraZ: 8
      targetX: 0
      targetY: 0
      targetZ: 0
      upX: 0
      upY: 1
      upZ: 0
      lightingEnabled: true
      ambientLight: 0.55
      exposure: 1.25
      keyLightX: 0.2
      keyLightY: 1.0
      keyLightZ: -0.7
      fillLightX: -0.2
      fillLightY: 1.0
      fillLightZ: 0.7
      shadowEnabled: true
      shadowStrength: 0.55
      shadowSoftness: 1.5
      shadowBias: 0.0015
      shadowMapSize: 1024
      toneMappingEnabled: true
      toneMapping: aces
      brightness: 0.0
      contrast: 0.0
      saturation: 1.0
      vignetteEnabled: false
      vignetteAmount: 0.5
      vignetteHardness: 1.5
      chromaticAberrationEnabled: false
      chromaticAberration: 0.003
      sharpenEnabled: false
      sharpenStrength: 0.3
      colorBalanceEnabled: false
      colorBalanceShadowsR: 0.0
      colorBalanceShadowsG: 0.0
      colorBalanceShadowsB: 0.0
      colorBalanceMidtonesR: 0.0
      colorBalanceMidtonesG: 0.0
      colorBalanceMidtonesB: 0.0
      colorBalanceHighlightsR: 0.0
      colorBalanceHighlightsG: 0.0
      colorBalanceHighlightsB: 0.0
      cameraStates:
        menu_focus:
          cameraX: 0
          cameraY: 0
          cameraZ: 8
          targetX: 0
          targetY: 0
          targetZ: 0
          upX: 0
          upY: 1
          upZ: 0

  - id: guardian
    type: scene_model
    parent: stage
    layout:
      x: 0
      y: 0
      width: 1
      height: 1
    sceneModel:
      catalog: entity
      modelId: crystal_guardian
      x: 0
      y: 0
      z: 0
      rotationX: 0
      rotationY: 25
      rotationZ: 0
      scale: 1
      animation: idle
      animationLoop: true
      metalness: 0.15
      roughness: 0.65
      emissiveColor: "#FFFFFF"
      emissiveStrength: 0.0

如果不使用 provider 目录,也可以把上例中的 sceneModel 改成直接资源模型:

yaml
sceneModel:
      source: custom
      model: models/menu/guardian.geo.json
      animation: animations/menu/guardian.animation.json
      texture: textures/menu/guardian.png
      x: 0
      y: 0
      z: 0
      rotationY: 25
      scale: 1
      animationLoop: true

保存时,编辑器提交当前页面草稿;成功回执到达后才更新保存基线。保存后关闭并重新打开页面,scenescene_model、模型变换、材质、后处理、显式相机字段、cameraStatesanimationanimationLoop 都应恢复。编辑时未记录的观察角度、播放暂停状态和时间拖动位置不会恢复,这是预期行为。

provider 不可用时

ChaEngineMod 缺失、provider API 不兼容、模型资源尚未同步或目录读取失败时,编辑器会显示不可用状态并安全降级:不会自动改写当前草稿,也不会影响页面中的 2D 组件。运行时只隐藏无法提交的模型节点,页面其他内容继续绘制和交互。

排查时依次确认 ChaUIMod 与 ChaEngineMod 的 Minecraft 版本和加载器完全一致,并确认该组合位于本页顶部列出的 ChaEngineMod 支持范围;目录模型要确认 catalog + modelId 与目录完全一致,直接模型要确认模型和材质路径都相对于 ChaEngine 资源根目录且文件确实存在。修改后重新打开编辑器并刷新目录。