> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/all-out-docs/docs-zh/ui/uidoc-quick-start.md).

# UIDoc 快速入门

***

UIDoc 让你可以用 HTML 和 CSS 描述游戏 UI，然后在 CSL 中提供其变化数据并处理其事件。它适用于菜单、物品栏、技能树以及其他结构化的屏幕空间界面。

UIDoc 类似浏览器，但它不是嵌入式网页浏览器。请使用下文所述的受支持子集 [UIDoc HTML 和 CSS 支持](/all-out-docs/docs-zh/ui/uidoc-html-css-support.md).

## 创建一个 UIDoc 资源

在你游戏的 `res` 目录下创建一个文件夹。该文件夹名称必须以 `.uidoc` 结尾，并包含 `index.html` 和 `index.css`:

```
res/
└── UI/
    └── settings.uidoc/
        ├── index.html
        └── index.css
```

CSL 使用的资源路径相对于 `res`，因此本文档会以 `UI/settings.uidoc`.

## 可视化编辑并生成 CSL

在 All Out 编辑器中选择一个 UIDoc 资源即可打开其可视化创作视图。你可以在画布上选择并拖拽元素，调整大小，在图层中重新排序或重新设父，并在检查器中编辑常见的 HTML 和 CSS 属性。预览由 UIDoc 运行时本身渲染，因此它使用与游戏相同的受支持布局和渲染行为。

界面检查器会根据文档标记推断动态字段、重复列表、输入项和动作。保存诸如 `ui/shop.uidoc` 这样的资源时，也会维护 `scripts/generated/uidoc/ui/shop.csl`。其带类型的包装器提供 `default_data`, `decode_event`、输入读取器，以及 `draw`，因此游戏代码无需重复本指南后面展示的底层绑定：

```csl
data := UiShop_UIDoc.default_data();
data.title = "Store";
UiShop_UIDoc.draw(data, player, shop_event);
```

生成的代码使用下面文档中所述相同的公共 `UI.uidoc_*` 调用；它不会添加单独的运行时系统，也不会改变 UIDoc 运行时 API。

使用自动化工具时， `uidoc_create_asset` 会返回其模板版本和精确生成的 `index.html`/`index.css` 内容。UIDoc 资源在运行中的游戏里不会热重载，因此在修改这些文件后请重启游戏。请使用 `uidoc_diagnostics` 进行编译和视口检查，然后使用 `uidoc_runtime_inspect` 检查实时节点、绑定、样式、点击负载和矩形。

## 编写文档

在 `index.html`:

```html
<div class="screen">
  <div class="panel">
    <span class="title">设置</span>
    <span class="message">{{message}}</span>
    <button class="close" data-on-click="event:close">关闭</button>
  </div>
</div>
```

`{{message}}` 是一个文本绑定。 `data-on-click` 会发送一个 `UIDoc_Event` 到 CSL。

在 `index.css`:

```css
.screen {
  position: fixed;
  inset: env(safe-area-inset-top) env(safe-area-inset-right)
         env(safe-area-inset-bottom) env(safe-area-inset-left);
  display: flex;
  align-items: center;
  justify-content: center;
}

.panel {
  display: flex;
  flex-direction: column;
  width: 420px;
  padding: 24px;
  gap: 16px;
  color: white;
  background: #172033;
  border: 2px solid #52627d;
  border-radius: 12px;
  box-shadow: 0 12px 28px 0 #00000066;
}

.title {
  font-size: 32px;
  text-align: center;
}

.close {
  height: 48px;
  background: #3559a8;
  border-radius: 8px;
}

.close:hover {
  background: #4770ca;
}

.close:pressed {
  background: #29447f;
}
```

安全区域内边距可使全屏 UI 避开设备切口和游戏顶部栏区域。

## 在 CSL 中绑定并绘制它

从该玩家的 `ao_late_update` 调用栈中绘制玩家 UI。每帧在绘制文档前清空并重建 UIDoc 绑定：

```go
settings_uidoc_event :: proc(event: UIDoc_Event, userdata: Object) {
    player := userdata.(Player);
    if player == null return;

    if event.handler == "event:close" {
        player.settings_open = false;
    }
}

draw_settings :: proc(player: Player) {
    UI.uidoc_clear_bindings();
    UI.uidoc_bind_text("message", "更改会自动保存。");

    document := get_asset(UIDoc_Asset, "UI/settings.uidoc");
    if document == null return;

    UI.uidoc(document, false, player, settings_uidoc_event);
}

Player :: class : Player_Base {
    settings_open: bool;
    pet_name: string;

    ao_late_update :: method(dt: float) {
        if this.is_local_or_server() && this.settings_open {
            draw_settings(this);
        }
    }
}
```

回调会接收到来自 `data-on-click`的精确处理器字符串。非重复控件可以提供静态 `data-key` 用于 `event.key`；重复控件使用其解析后的 `data-for-key` 或列表项键。

`userdata` 只会传给该回调；它不会标识文档。每个 UIDoc 资源在每次模拟提交中最多可绘制一次。当某个文档停止被绘制时，其当前激活会关闭；再次绘制时会自动分配新的激活代次，因此过期的复制布局无法附着到重新打开的文档上。最多可同时激活四个不同的 UIDoc 资源。

可用的顶层绑定有：

```go
bind_player_fields :: proc(player: Player) {
    UI.uidoc_bind_bool("visible", true);
    UI.uidoc_bind_text("name", player.get_username());
    UI.uidoc_bind_float("cameraSize", player.camera.size);
}
```

## 条件、绘制绑定和列表

使用 `data-if` 仅在顶层布尔绑定为真时包含某个节点。可选的前导 `!` 会对其取反。 `data-if` 不会解析诸如 `item.visible`之类的列表局部表达式；重复的视觉状态应改用列表局部绘制绑定，而重复的结构或交互可见性应在构建 CSL 列表时决定。对于其他动态视觉状态，请保持类为静态，并绑定受支持的颜色或不透明度：

```html
<div
  class="notice"
  data-if="showNotice"
  data-style-color="noticeColor">
  {{noticeText}}
</div>
```

绑定 `noticeColor` 使用 `UI.uidoc_bind_text` 并提供受支持的 CSS 颜色字符串。类属性会编译为静态类标记，不支持 `{{...}}` 插值。

使用 `data-for` 用于重复数据：

```html
<div class="inventory">
  <button
    class="item"
    data-for="item in items"
    data-for-key="item.id"
    data-key="inventory-item"
    data-style-color="item.rarityColor"
    data-on-click="event:item">
    {{item.name}}
  </button>
</div>
```

在调用 `UI.uidoc`:

```go
Inventory_Row :: struct {
    id: string;
    name: string;
    rarity_color: string;
}

bind_inventory_rows :: proc(items: []Inventory_Row) {
    UI.uidoc_begin_list("items");
    for item: items {
        UI.uidoc_list_item(item.id);
        UI.uidoc_list_bind_text("id", item.id);
        UI.uidoc_list_bind_text("name", item.name);
        UI.uidoc_list_bind_text("rarityColor", item.rarity_color);
    }
    UI.uidoc_end_list();
}
```

请为每个重复项使用稳定、唯一的直接表达式，例如 `data-for-key="item.id"` 。它会以 `event.key`返回。给 `UI.uidoc_list_item(...)` 提供相同的稳定值，这样交互、输入和滚动身份就能在列表变化时保持不变。静态 `data-key` 用于命名控件角色；UIDoc 会将该角色与每个外层列表项的绑定键及当前索引组合，作为其注册运行时身份。

在上面的示例中，实时测试名称会包含一个后缀，例如 `inventory-item#potion-42:7/__widget`。请在 `client_ui_tree`中检查精确名称，然后使用完整名称或足够精确的后缀来定位该实例，例如 `Test.click_button("inventory-item#potion-42:7")`。仅按角色查找，例如 `Test.click_button("inventory-item")` 在显示多行时会产生歧义。 `data-on-click` 以及可见文本都不是测试选择器。嵌套列表会为每次循环追加一个 `#key:index` 对。

## 输入

使用 `data-bind-value`:

```html
<input
  id="pet-name"
  class="name-input"
  placeholder="Pet name"
  data-bind-value="petName"
  data-on-click="event:name-input">
```

在文档绘制完成后读取其当前值：

```go
draw_pet_name_input :: proc(player: Player, document: UIDoc_Asset) {
    UI.uidoc_bind_text("petName", player.pet_name);
    UI.uidoc(document, false, player, settings_uidoc_event);
    player.pet_name = UI.uidoc_text_value(document, "pet-name", player.pet_name);
}
```

`UI.uidoc_text_value` 会按其 `id` 或 `data-key`查找输入框，然后返回其 `data-bind-value` 绑定的当前值。

## 滚动和缩放

通过 CSS overflow 启用滚动。两个轴都可以在同一个视口上启用：

```html
<div class="viewport" data-scroll-zoom="zoom">
  <div class="canvas">
    <button
      class="node"
      data-for="node in nodes"
      data-for-key="node.id"
      data-key="canvas-node"
      data-style-transform-x="node.x"
      data-style-transform-y="node.y"
      data-on-click="event:node">
      {{node.name}}
    </button>
  </div>
</div>
```

```css
.viewport {
  width: 100%;
  height: 100%;
  overflow-x: auto;
  overflow-y: auto;
}

.canvas {
  position: relative;
  width: 1600px;
  height: 1000px;
}

.node {
  position: absolute;
  width: 160px;
  height: 64px;
}
```

绑定 `zoom` 使用 `UI.uidoc_bind_float`. `data-scroll-zoom` 会围绕视口中心缩放内容的位置、大小、文本、图像、命中区域和滚动范围。请将固定缩放控件放在缩放后的视口之外。

{% hint style="warning" %}
绑定表达式属性使用直接表达式，例如 `data-for-key="node.id"`, `data-style-transform-x="node.x"`，或者 `data-scroll-zoom="zoom"`。不要把这些表达式放进 `{{...}}`中。Mustache 插值仅用于文本和图像 `src`；类和 `data-key` 值是静态的。
{% endhint %}

对于普通滚动面板，请省略 `data-scroll-zoom`。拖动会平移所有已启用的轴；鼠标滚轮会垂直滚动，或者在仅启用水平溢出时水平滚动。

## 常见错误

* 使用相对于 `res`的路径加载文档，包括 `.uidoc` 后缀。
* 在本地玩家 UI 代码中于 `is_local_or_server()`.
* 下绘制它。 `在每次绘制前调用` UI.uidoc\_clear\_bindings()
* 每次更新只绘制每个 UIDoc 资源一次。仅将 `userdata` 用于回调数据。
* 使用 `data-if` 仅与顶层布尔值一起使用。通过 `data-style-opacity`/`data-style-color`绑定列表局部视觉状态，或从绑定列表中省略结构性/交互性行。
* 为重复节点提供稳定的直接 `data-for-key` 表达式；在命名控件角色时保持 `data-key` 静态。
* 请将 CSS 支持视为按属性/值具体而定。 `auto` 和 `none` 仅在生成的参考文档列出的地方被接受，诸如 `border-bottom` 这样的边框侧属性不受支持，而 `calc(...)` 仅限于带有简单加减运算的文档化长度字段。
* 为滚动内容提供真实尺寸。不要仅将变换作为其唯一的布局尺寸。
* 请使用 UIDoc 自身的响应式布局和缩放行为，而不是在 CSL 中再手动应用第二层 UI 缩放。
