`、``),只能使用 Valve 提供的 Panel 类型
2. **严格使用 Panorama CSS 属性**:不能使用标准 CSS 中 Panorama 不支持的属性,所有属性值必须符合 Panorama 规范
3. **Label 文本必须使用 text 属性**:❌ 禁止 `` 写法(会导致语法错误),✅ 必须使用 ``。需要本地化时使用 `` 或 `$.Localize('#token')`,并在 `game/resource/addon.csv` 中添加对应 Token
4. **优先使用前端 API 获取数据**:如果 Panorama API 可直接获取数据,优先在前端获取;需要后端提供的数据,使用 Game Events 通信
5. **Net Table 限制**:每个 CustomNetTable 不能超过 2MB,表名必须注册到 `custom_net_tables.txt`;大数据量使用 XNetTable,但原则上优先使用 NetTable
6. **配置与代码分离**:所有可能与后端共享或需要集中管理的配置、数值、常量(包括但不限于技能属性、物品数据、单位属性、游戏规则参数、界面显示配置等),都必须放在 `game/scripts/npc/` 下的 KV 文件中。前端通过编译后的 JSON(`content/panorama/src/json/`)读取,严禁在前端代码中硬编码任何可能变化的配置值。修改配置后需运行 `npx gulp kv_2_js` 刷新 JSON
## 文件结构
| 文件 | 路径 | 用途 |
|---|---|---|
| 布局 XML | `content/panorama/src/<模块名>/layout.xml` | 面板结构定义 |
| 脚本 TSX | `content/panorama/src/<模块名>/script.tsx` | React 组件逻辑 |
| 样式 LESS | `content/panorama/src/<模块名>/styles.less` | CSS 样式 |
| 类型定义 | `shared/gameevents.d.ts` | 前后端通信事件类型 |
| 网络表类型 | `shared/net_tables.d.ts` | CustomNetTable 类型定义 |
| X网络表类型 | `shared/x-net-table.d.ts` | XNetTable 类型定义 |
| 网络表注册 | `game/scripts/custom_net_tables.txt` | 注册 NetTable 表名 |
## 工作流
### 步骤 1:确认 UI 需求
- 明确 UI 的功能、交互、数据来源
- 确定是否需要后端数据(Game Events 还是 Net Table)
- 确定数据获取方式(优先前端 API → Game Events → Net Table → XNetTable)
### 步骤 2:创建布局文件 (layout.xml)
```xml
```
- `` 是根节点
- `` 引入 TSX 脚本
- `` 引入样式,`s2r://panorama/styles/dotastyles.vcss_c` 是 DOTA2 默认样式
- 根 `` 是 React 的挂载点
### 步骤 3:编写 React 组件 (script.tsx)
```tsx
import 'panorama-polyfill-x/lib/console';
import 'panorama-polyfill-x/lib/timers';
import { FC } from 'react';
import { render } from 'react-panorama-x';
const MyComponent: FC = () => {
return (
);
};
render(, $.GetContextPanel());
```
### 步骤 4:编写样式 (styles.less)
```less
.my-container {
width: 100%;
height: 100%;
flow-children: down;
}
.my-label {
font-size: 24px;
color: white;
horizontal-align: center;
vertical-align: center;
}
```
### 步骤 4b:从 JSON 读取 KV 数据(推荐)
游戏数值配置(技能属性、物品数据、单位属性等)存储在 KV 文件中,编译后位于 `content/panorama/src/json/`,前端可导入使用:
```tsx
import abilityData from '../json/npc_abilities_custom.json';
// 读取技能数值
const healData = abilityData.dbg_card_heal?.AbilityValues;
const cooldown = abilityData.dbg_card_heal?.AbilityCooldown;
```
**前后端数据分离原则**:
- 所有游戏数值配置写在 KV 文件中
- 前后端都从编译后的 JSON 文件中读取同一份数据
- 禁止在前端代码中硬编码数值常量,避免数据不一致
### 步骤 5:数据通信(如需要)
- 前端 API 可获取的数据:直接使用 Panorama API
- 需要后端推送的数据:使用 Game Events
- 需要持久同步的数据:使用 Net Table
### 步骤 6:编译验证(必须执行)
**完成代码编写后,必须运行编译命令验证没有错误:**
```bash
# 验证前端 Panorama 编译(非 watch 模式,仅编译一次检查错误)
npx webpack --config content/panorama/webpack.dev.js
```
- 如果编译成功(退出码 0),无错误输出,则验证通过
- 如果编译失败,根据错误信息修复代码后重新编译验证
- **必须确保编译通过后才能告知用户完成**
如果同时修改了后端代码(如 Game Events 声明、NetTable 类型等),还需要验证服务端编译:
```bash
# 验证服务端 TSTL 编译
npx tstl --project game/scripts/tsconfig.json
```
### KV → JSON 编译(如修改了 KV 文件)
如果修改了 `game/scripts/npc/` 下的 KV 文件,在编译前端之前先编译 KV:
```bash
npx gulp kv_2_js
```
### 完整验证命令
```bash
# 先编译 KV,再同时验证前端 + 服务端
npx gulp jssync && npx webpack --config content/panorama/webpack.dev.js && npx tstl --project game/scripts/tsconfig.json
```
## Panel 类型参考
### 基础面板
| Panel 类型 | XML 标签 | 用途 | React 组件 |
|---|---|---|---|
| Panel | `` | 基础容器,类似 HTML div | `` |
| Label | `