# APT / edit.xml 修改指南 > **目标读者**:完全不了解 APT 的人。阅读本指南后应能理解 APT 的结构和内容,并能准确无误地修改 edit.xml 来实现自己的需求。 --- ## 1. APT 概述 ### 1.1 什么是 APT APT 是红色警戒3(RA3)使用的 UI 系统,基于 **Flash 7(ActionScript 2.0)** 修改而来。它的核心文件是 `.apt` 二进制文件,游戏运行时加载这些文件来渲染用户界面。 APT 中的内容与 Flash SWF 高度类似——包含 MovieClip、Button、Shape(矢量图形)、Image、EditText(文本字段)等"角色"(character),以及 ActionScript 代码来驱动 UI 交互逻辑。 ### 1.2 文件生态系统 每个 APT UI 模块由 **多个文件** 组成。以 `fes_m_factionMenu`(阵营选择界面)为例: | 文件 | 说明 | |---|---| | `fes_m_factionMenu.apt` | 二进制 APT 文件,游戏实际加载的文件 | | `fes_m_factionMenu_edit.xml` | **可编辑的 XML 文件**,外部工具(AptToXML)从 .apt 生成,修改后经外部工具(XMLToApt)转回 .apt | | `fes_m_factionMenu.const` | 常量池文件(二进制),存储字符串等常量 | | `fes_m_factionMenu.dat` | 数据映射文件,将 character ID 映射到素材(纹理等) | | `fes_m_factionMenu.tac` | ActionScript 文本表示(可读的汇编格式),**仅作参考、不直接编辑** | | `fes_m_factionMenu.eaf` | EA 修改版的 Flash 汇编格式(二进制) | | `fes_m_factionMenu.flm` | 工具链日志文件(记录编译/反编译信息) | | `fes_m_factionMenu.xml` | 资产声明文件(AssetDeclaration),声明 .apt/.const/.dat 及纹理等依赖 | | `fes_m_factionMenu_geometry/` | 矢量图形几何数据目录,包含 `.ru` 文件(render units,渲染单元) | | `fes_m_factionMenu_textures/` | 纹理素材目录 | | `fes_m_factionMenu_sounds/` | 音效目录 | | `fes_m_factionMenu_videos/` | 视频目录 | | `art/` | 源素材目录 | ### 1.3 工具链流程 ``` .apt (二进制) ←→ AptToXML / XMLToApt 工具 ←→ edit.xml (可编辑) ↕ ↕ 关联文件 (.const, .dat, .eaf 等) 手动编辑 edit.xml 后 + 素材 (.tga 等) 通过工具重新生成 .apt 和所有关联文件 ``` **核心流程**: 1. 用 AptToXML 工具从 `.apt` 生成 `_edit.xml` 2. 修改 `_edit.xml` 的内容 3. 用 XMLToApt 工具从 `_edit.xml` 重新生成 `.apt` 及所有关联文件 4. 生成的 `.apt` 供游戏使用 > **重要**:`.const`、`.dat`、`.eaf`、`.tac`、geometry `.ru` 文件等**均由工具自动生成**,你只需要修改 edit.xml 即可。不需要手动编辑这些文件。 > **格式说明**:edit.xml 中所有 bytecode 内容(``、``、``)均使用 `` 包裹。这是 XML CDATA 段,确保 bytecode 中的特殊字符(如 `<`、`>`、`&`)不被 XML 解析器误解。在 CDATA 段中所有字符按字面处理,无需使用 `&` 等 XML 实体转义。 ### 1.4 与 ActionScript 2.0 SWF 的关系 APT 源自 Flash 7,因此与 AS 2.0 SWF 有大量相似之处: - **概念层面**:MovieClip、Sprite、帧、时间线、depth(深度/层级)、placeobject、removeobject 等概念直接对应 - **代码层面**:使用 ActionScript 2.0 语法和语义(类、继承、函数、变量等) - **差异**:APT 使用 **EA 修改版 bytecode**(自定义的操作码如 `EAPushOne`、`setstrvar` 等),部分标准 Flash API 在游戏中被裁剪 更多细节见 [第9章:AS 2.0 SWF → APT 知识迁移](#9-as-20-swf--apt-知识迁移)。 ### 1.5 RA3 UI 模块命名前缀 | 前缀 | 含义 | |---|---| | `fe` | 公共 UI 组件/资源 | | `feg` | 公共游戏逻辑 | | `fes` | 单人游戏(Singleplayer)专属 | | `fem` | 多人游戏(Multiplayer)专属 | | `AXX` | 盟军战役 | | `SXX` | 苏联战役 | | `JXX` | 旭日(日本)战役 | > **反编译代码参考**:如果需要理解 ActionScript 逻辑的详细实现,可以查阅 `Data/decompiled-as-references/` 目录中的反编译 `.as` 文件。其中 `main_mouse` 是整个 APT 系统的入口模块——它初始化了全局常量(`_global.gSM`、`_global.gFM` 等)、UI 框架(Cafe2)和全局类注册。 ### 1.6 screen APT 与 assets APT 拆分 从 `fem_m_gameSetup` 和 `fe_m_assetsGameSetup` 可以看到一种常见设计模式:一个 APT 是实际的 UI screen(画面),另一个 APT 是组件/素材库(assets)。 `fem_m_gameSetup` 的 imports 中有: ```xml ``` 而 `fe_m_assetsGameSetup` 会导出这些可复用组件: ```xml ``` 这意味着修改 UI 时,**不一定只改该 screen 自己的 `edit.xml`**。如果顶层 screen 只是导入并摆放一个外部组件,真正的子控件布局、实例名(poname)和 content sprite 可能定义在 assets APT 中。修改前要先判断目标在 screen APT 里,还是在被导入的 assets APT 里。 --- ## 2. edit.xml 顶层结构 ### 2.1 整体 XML 树 ``` ... ... ... ... ... ``` ### 2.2 `` — 主时间线 顶层的 `` 表示主时间线,包含以下部分: - **`screensizex` / `screensizey`**:屏幕尺寸(像素),如 1365×768 - **`unknown`**:未知属性,始终为 `"33"`(保持原值即可) - **``**:从其他 APT 导入的角色声明 - **``**:导出给其他 APT 使用的角色声明 - **``**:帧列表,通常只有一个 `frame id="0"`,所有内容(placeobject、action、initaction)都在此帧中 ### 2.3 `` — 导入角色 从其他 APT 文件中导入角色(character)以供本 APT 使用: ```xml ``` 每个 `` 的属性: - **`name`**:导入角色在原 APT 中的名称 - **`movie`**:源 APT 文件路径(相对路径,不含扩展名) - **`character`**:导入后在本 APT 中使用的 **character ID** ### 2.4 `` — 导出角色 将本 APT 中定义的角色导出,供其他 APT 通过 import 引用: ```xml ``` - **`character`**:本 APT 中定义的 character ID - **`name`**:导出的名称。注意 `__Packages.xxx` 是 ActionScript 类的完全限定名,导出后其他 APT 通过 import 可以实例化这些类 > **关键**:每个 APT 通常导出 `__Packages.Cafe2_BaseUIScreen`(基类)和 `__Packages.<本模块名>`(本模块类),这些导出使得其他 APT 可以使用相同的基类和共享 UI 框架。 ### 2.5 `` — 占位符 ```xml ``` `` 元素与 `` 中的 import **严格一一对应**。每个 import 的 character ID 都需要一个对应的 `` 元素来声明占位。 > **注意**:`` 的数量和 ID 必须与 `` 保持一致。添加新 import 时必须添加对应的 ``,删除 import 时必须删除对应的 ``。 --- ## 3. 角色类型详解 APT 中的所有角色(character)都有一个唯一的整数 **ID**(如 `id="7"`)。角色 ID 在整个 APT 文件中必须唯一(包括 import 的角色 ID)。 ### 3.1 movieclip 与 sprite **movieclip** 和 **sprite** 本质上都是 ActionScript 中的 MovieClip——可以包含帧、帧标签、子对象。 在 edit.xml 中: - **``** 特指**顶层主时间线**(每个 APT 只有一个) - **``** 指被定义的子 MovieClip(可以有多个) 两者结构几乎相同,都有 ``: ```xml ``` #### sprite 的帧标签命名约定 许多 sprite 使用特殊命名的帧标签来配合 UI 框架的状态机: | 帧标签 | 含义 | |---|---| | `_disable` | 禁用状态 | | `_selected` | 选中状态 | | `_unselected` | 未选中状态 | | `_selectedRollover` | 选中 + 鼠标悬停 | | `_unselectedRollover` | 未选中 + 鼠标悬停 | | `_downState` | 鼠标按下状态 | | `_intro` | 入场动画 | | `_outtro` | 出场动画 | | `_unChecked` / `_checked` | checkbox 的选中/未选中状态 | 这些帧标签被 Cafe2 UI 框架(RA3 的 UI 框架)通过 `gotoAndPlay("_selected")` 等方式调用,实现按钮、checkbox 等组件的状态切换。 #### sprite 的特殊帧 - **intro 帧**(如 `frame id="60"`)通常带有 `removeobject` 来重置上一状态的显示对象,然后播放过渡动画 - **outtro 帧**(如 `frame id="87"`)通常在最后一帧(如 `frame id="119"`)调用 `this._parent.outtroComplete()` → `stop()` 来通知父级过渡完成,参考反编译代码: ```as // frame 119: this._parent.outtroComplete(); stop(); ``` - **select 帧**(如 `frame id="10"` 有 `_selected` 标签)通常从空帧过渡到含有新 placeobject 的帧 每个状态段的最后一帧通常有一个 `stop()` 动作,防止播放头继续前进。 ### 3.2 button Button 是一个可点击的交互区域,由顶点(vertices)、三角形(triangles)和按钮记录(buttonrecords)组成: ```xml ``` **属性解释**: - **`top` / `left` / `bottom` / `right`**:边界框 - **``**:定义按钮区域的四个角点 - **``**:将四个顶点组合为两个三角形(构成矩形区域) - **``**:按钮在不同状态下的显示内容 - **`flags`**:状态标志,可选值: - `StateUp` — 正常/弹起状态 - `StateOver` — 鼠标悬停状态 - `StateDown` — 鼠标按下状态 - `StateHit` — 点击测试区域(不可见,只用于检测鼠标点击的范围) - **`character`**:该状态下显示的 character ID(通常是一个 shape) - **`depth`**、**旋转/平移矩阵** 等参数与 placeobject 相同 #### button 的 hitRegion 与脚本绑定 在组件 sprite 内部,button 通常被放置在名为 `hitRegion` 的实例上。通用组件脚本(如 `std_mouseRadioButton.as`)通过固定实例名绑定鼠标事件: ```as this.m_contentClip.hitRegion.onRollOver = ...; this.m_contentClip.hitRegion.onPress = ...; this.m_contentClip.hitRegion.onRelease = ...; ``` 因此: - **修改点击范围**时应改 button 的 shape(`buttonrecord` 使用的 character)或放置时的矩阵,而不是只改可见图形 - **扩大可点击区域**要改 hit shape 或 hit button 的 matrix,**不是**改可见 shape - content sprite 中 depth 5 上的 button(`poname="hitRegion"`)使用了很大的缩放矩阵(如 `rotm00="2.820221" rotm11="11.699921"`),这是故意放大 hit 区域的做法 - 如果 content sprite 中没有 `hitRegion` 实例,通用组件脚本将无法绑定鼠标事件,按钮将不可点击 ### 3.3 shape(矢量图形) Shape 是矢量图形,通过 **ru**(render unit,渲染单元)命令定义绘制方式: ```xml c s tc:255:255:255:255:art\Textures\apt_fes_m_factionMenu_1.tga:1:0:0:1:1368:563 t 0:451:0:0:269:0 t 269:0:269:451:0:451 ``` **属性解释**: - **`top` / `left` / `bottom` / `right`**:边界框 - **`geometry`**:外部 `.ru` 文件的路径。**此文件由工具自动生成**,你只需要修改 `` 标签内的内容即可 #### ru 命令详解 | 命令 | 名称 | 格式 | 说明 | |---|---|---|---| | `c` | Clear | `c` | 结束上一段几何图形,开始新的绘制 | | `s` | Style | 见下方 | 设置绘制样式(填充色、线型、纹理等) | | `t` | Triangle | `t x1:y1:x2:y2:x3:y3` | 绘制一个三角形 | | `l` | Line | `l x1:y1:x2:y2` | 绘制一条线段 | #### s(Style)命令的变体 **1. 纯色填充三角形** — `s s:R:G:B:A` ``` s s:0:0:0:0 ``` 绘制纯色填充的三角形,4 个数字分别为 R(红)、G(绿)、B(蓝)、A(透明度),范围 0-255。 示例:`s s:0:0:0:0` = 黑色;`s s:255:0:0:255` = 不透明红色。 **2. 线条样式** — `s l:thickness:R:G:B:A` ``` s l:2:255:0:0:255 ``` 绘制线条,1 个 thickness(线宽) + 4 个 RGBA。 **3. 纹理填充三角形** — `s tc:R:G:B:A:textureId:m00:m01:m10:m11:tx:ty` ``` s tc:255:255:255:255:art\Textures\apt_fes_m_factionMenu_1.tga:1:0:0:1:1368:563 ``` 参数说明: - 4 个 RGBA 值(颜色调制) - 1 个纹理引用(图像路径或 ID) - 4 个 2×2 矩阵值(m00, m01, m10, m11)— 纹理变换 - 2 个平移值(tx, ty)— 纹理偏移 > **纹理映射公式**:`tex_x = m00 × screen_x + m01 × screen_y + tx`,`tex_y = m10 × screen_x + m11 × screen_y + ty`。 > 当矩阵为单位矩阵(`1:0:0:1`)时简化为 `tex = screen + tx`。 > `t` 命令中的坐标为屏幕坐标。要采样纹理中的特定区域,需满足 `tx = tex_left − screen_left`。 #### 内联 ru 与外部 .ru 文件的区别 edit.xml 中同时存在两种方式: **方式一:内联 ru**(如 `fe_m_assetsGameSetup`) ```xml c s tc:255:255:255:255:art\Textures\apt_fe_m_assetsGameSetup_1.tga:1:0:0:1:737:1 t 0:31:0:0:188:0 t 188:0:188:31:0:31 ``` **方式二:仅引用外部**(如 `fes_m_factionMenu` 的部分 shape) ```xml c s tc:255:255:255:255:art\Textures\apt_fes_m_factionMenu_1.tga:1:0:0:1:1368:563 t 0:451:0:0:269:0 t 269:0:269:451:0:451 ``` > **关键**:两种方式下,`` 标签内的内容都会被写入到 `geometry` 属性指定的 `.ru` 文件中。**你只需要修改 edit.xml 中的 `` 内容即可**,外部的 `.ru` 文件会在工具转换时自动生成/更新。不需要手动编辑 `.ru` 文件。 两种方式只是工具链实现细节的差异,修改时操作完全一致——编辑 `` 标签内的命令即可。 #### 组合多个 ru 绘制 一个 shape 可以包含多段 `c ... s ... t` 序列来绘制复杂的图形。例如背景 shape `id="23"` 同时绘制了底层背景和上层纹理: ```xml c s tc:255:255:255:255:art\Textures\apt_fes_m_factionMenu_1.tga:1:0:0:1:1:1 t 0:768:0:0:1365:0 t 1365:0:1365:768:0:768 c s tc:255:255:255:255:art\Textures\apt_fes_m_factionMenu_2.tga:1:0:0:1:-183.45:-73.85 t 184.45:685.85:184.45:74.85:1183.45:74.85 t 1183.45:74.85:1183.45:685.85:184.45:685.85 ``` ### 3.4 image ```xml ``` Image 是最简单的角色类型,仅包含一个 `image` 属性指向纹理文件路径。引用同一个纹理的多个 image 可以有不同 ID,通过不同的 ru 绘制来显示纹理的不同区域。 > **图集注意事项**:在 RA3 APT 中,一个 `.tga` 文件往往是**图集**(atlas),不是一张只服务于单个控件的小图。例如 `apt_fes_m_factionMenu_1.tga` 被多个 shape 的不同区域共享使用(通过 `s tc` 中的 tx/ty 偏移从同一图集中取不同区域)。替换贴图时要确认: > - 其他 shape 是否也采样同一 `.tga` 的其他区域 > - 图集尺寸、坐标偏移、透明通道是否保持工具链可接受 > - 资源声明 XML 中的 `OutputFormat`、`GenerateMipMaps`、`AllowAutomaticResize` 是否符合原样本 > - 新增纹理时,需要在资产声明 XML 中添加 `` 条目 ### 3.5 edittext(文本字段) ```xml ``` **属性解释**: - **`top` / `left` / `bottom` / `right`**:文本边界框 - **`font`**:引用的 `` 的 ID - **`alignment`**:对齐方式(`0` = 左对齐,`1` = 右对齐,`2` = 居中) - **`red` / `green` / `blue` / `alpha`**:文本颜色和透明度 - **`height`**:字体大小 - **`readonly`**:只读(`1`)或可编辑(`0`) - **`multiline`**:多行(`1`)或单行(`0`) - **`wordwrap`**:自动换行(`1`)或不换行(`0`) - **``**:初始文本内容 - **``**:绑定的变量名(通常为空) > **注意**:edittext 中出现的 `$VARIABLE_NAME` 格式的文本(如 `$SOVIET_FACTION_DESCRIPTION`)是 RA3 的本地化字符串引用,游戏运行时会替换为对应语言的实际文本。 #### 文本来源的三种情况 修改 edittext 的显示文本时,需要先判断文本的**运行时来源**,只改 `` 可能无效: **1. 静态 `ettext`**:文本直接写在 `` 中,且不会被 AS 覆盖。修改直接生效。 **2. AS2 运行时覆盖**:text 在脚本中被动态设置。例如 faction menu 中: ```as factionDescriptionTF.text = "$SOVIET_FACTION_DESCRIPTION&Outline"; factionDescriptionShadowTF.text = "$SOVIET_FACTION_DESCRIPTION"; ``` 这种情况下,修改 `` 的默认值没有意义——游戏运行时会被脚本覆盖。 **3. 本地化 key**:`$SOME_KEY` 格式的字符串由游戏引擎查找对应语言的实际文本。`&Outline` 等后缀是游戏 UI 的文本特效标记。新增 key 需要游戏侧支持。 > **阴影/正文双文本框**:RA3 UI 常常成对出现正文和阴影文本框(例如 `factionDescriptionTF` + `factionDescriptionShadowTF`)。只改其中一个会导致阴影与正文内容不一致。修改时必须两个一起改。 #### edittext 属性补充 - **`alignment`**:当前样本出现 `0` 和 `2`,可按左对齐/居中等理解,改前最好对照实际显示 - **`etvar`**:变量绑定字段,当前样本多为空,但遇到非空值时要检查 AS 是否通过变量名驱动文本 - **`height`**:字体高度,修改文本内容后需确认 `right/bottom` 边界是否足够容纳 ### 3.6 font ```xml ``` 定义字体。`name` 指定字体名称,此字体必须在游戏环境中可用。 ### 3.7 empty ```xml ``` `` 是占位符声明,与 `` 中的 import 条目严格一一对应。它声明了被导入角色在本 APT 命名空间中的存在。 > **规则**:每个 import 的 character ID 必须有一个对应的 ``。添加新 import 时要添加 ``,删除 import 时要删除 ``。 --- ## 4. 帧与时间线 ### 4.1 帧的基本结构 每个 `` 有一个整数 `id` 属性。帧按 id **递增顺序**排列。帧中可以包含以下元素(可同时出现多种): ```xml ... ``` ### 4.2 `` ```xml ``` 设置帧的背景色。颜色为 32 位整数 ARGB 格式(`-1` = 0xFFFFFFFF = 白色)。通常只在主时间线帧 0 中出现。 ### 4.3 `` — 放置角色 `` 是最核心的操作——将一个角色(character)放置在当前帧的指定深度(depth)。 ```xml ``` #### placeobject 属性详解 | 属性 | 类型 | 说明 | |---|---|---| | `flags` | 枚举 | 控制哪些属性生效,见下方 flags 表 | | `depth` | 整数 | **深度(层级)**,值越大越靠前。同一 depth 上的 placeobject 会替换之前帧中同 depth 的对象 | | `character` | 整数 | 放置的角色的 ID | | `rotm00` ~ `rotm11` | 浮点数 | 2×2 旋转/缩放矩阵(见下方矩阵说明) | | `tx` / `ty` | 浮点数 | X/Y 平移(像素) | | `red` / `green` / `blue` / `alpha` | 0-255 | 颜色调制和透明度 | | `ratio` | 浮点数 | (未知语义)出现在 `_intro`/`_selectedRollover` 等过渡帧中 | | `unknown` | 整数 | 未知属性,通常为 `"0"` | #### flags 枚举值 | Flag | 含义 | |---|---| | `HasCharacter` | `character` 属性有效 | | `HasMatrix` | 旋转/平移矩阵属性有效(rotm00-rotm11, tx, ty) | | `HasName` | `` 子元素有效(给角色命名,使其可被 ActionScript 访问) | | `HasClipAction` | `` 子元素有效(为角色附加 ActionScript 初始化代码) | | `HasColorTransform` | 颜色调制(red, green, blue, alpha)有效 | | `HasRatio` | `ratio` 属性有效 | #### 2×2 变换矩阵 ``` rotm00 rotm01 rotm10 rotm11 ``` - 恒等变换(无旋转无缩放):`rotm00="1" rotm01="0" rotm10="0" rotm11="1"` - X 轴缩放 s:`rotm00="s"`;Y 轴缩放 s:`rotm11="s"` - 标准缩放 + 平移:对象先缩放/旋转,再平移到 (tx, ty) #### `` — 给角色命名 ```xml ``` 通过 `poname` 给放置的角色赋予一个实例名称(类似 Flash 中的 instance name),该名称可以在 ActionScript 中通过以下方式访问: ```as Cafe2_BaseUIScreen.m_screen.sovietFaction.show(); ``` > **注意**:实例名称在同一层级中必须唯一。 #### `` — 角色初始化代码 ```xml ``` `clipaction` 的含义取决于 `flags`: - **`flags="Construct"`**:构造时执行(角色首次被放置时运行的初始化代码),设置角色的各种属性(如 `m_width`、`m_tabIndex`、`m_label` 等) 该代码在角色实例化时执行,类似于给对象设置初始属性值。详见 [第5章 ActionScript 汇编参考](#5-actionscript-汇编参考)。 ### 4.4 `` — 移除角色 ```xml ``` 从当前帧开始,移除指定 depth 上的角色。常用于 sprite 的状态切换中清除上一状态的显示对象。例如: ```xml ``` ### 4.5 `` — 帧标签 ```xml ``` 给当前帧命名,ActionScript 可以通过 `gotoAndPlay("_disable")` 或 `gotoAndStop("_disable")` 跳转到此帧。AS 中也可以直接使用帧号。 `frame` 属性的值与 `` 一致。 ### 4.6 `` — 帧动作 ```xml ``` 帧动作在当前帧播放时执行。常见用途: - `stop` — 停止播放头 - 其他 EA 修改版 ActionScript 汇编指令 > **统一 CDATA**:``、`` 和 `` **均使用** `` 包裹 bytecode。CDATA 段内所有字符按字面处理。 ### 4.7 `` — 初始化动作(类定义) `` 是最关键的结构之一——它定义了 ActionScript 类(class): ```xml ``` **结构分析**: 1. **检查类是否已定义**:`pushglobal → getnamedmember "ClassName" → ActionLogicalNot × 2`(相当于 `if (_global.ClassName == undefined)`) 2. **用 `function` 定义构造函数**:参数包括 screen、this、super、_global 等寄存器 3. **`extends`**:设置继承关系(如 `fes_m_factionMenu extends Cafe2_BaseUIScreen`) 4. **在 prototype 上定义方法**:`pushreg 2 → pushconst "methodName" → function (...) { ... } → ActionSetMember` 5. **定义实例属性**:`pushreg 2 → pushconst "propName" → pushvalue → ActionSetMember` 6. **`ASSetPropFlags`**:设置属性保护标志(防止属性被枚举/修改) `` 中的 `sprite` 属性指定此 initaction 作用的 sprite ID——这个 sprite 就是此 APT 中 ActionScript 类的宿主角色。 > **重要**:每个 APT 通常有两个 initaction: > - sprite 32:`Cafe2_BaseUIScreen` 基类的 initaction(由工具自动生成,所有 APT 共用相同代码) > - sprite 33:本模块类的 initaction(如 `fes_m_factionMenu`),包含该 UI 界面的具体逻辑 ### 4.8 嵌套实例路径追溯法 复杂界面会大量使用嵌套组件路径。例如 `GameSetupBase.as` 中有: ```as Cafe2_BaseUIScreen.m_screen.gameSettings.mapPanel.mapList Cafe2_BaseUIScreen.m_screen.gameSettings.rulesPanel.resourcesDropdown this.m_playerSlots[i].playerSlotOptions this.m_playerSlots[i].factionOptions ``` 这些路径来自多层 `placeobject` 的 `poname` 嵌套。定位控件时从脚本路径反推: 1. `Cafe2_BaseUIScreen.m_screen` 是顶层 screen movieclip 2. 第一级属性(如 `gameSettings`)是顶层 `placeobject` 的 `poname` 3. 后续属性(如 `mapPanel`、`mapList`)来自被导入 sprite 或本地 sprite 内部的 `poname` 4. 如果某一级 character 是 import,继续去对应 assets APT 的 `edit.xml` 查内部结构 修改任何一层实例名都会破坏 AS 访问路径。因此修改 poname 前必须全局搜索 AS 中对该名称的所有引用。 --- ## 5. ActionScript 汇编参考 APT 使用 **EA 修改版 ActionScript bytecode**,在 edit.xml 中以文本形式表示。以下是最常用的指令。 ### 5.1 数据操作指令 | 指令 | 说明 | 示例 | |---|---|---| | `pushstr "..."` | 压入字符串常量 | `pushstr "m_label"` | | `pushconst "..."` | 压入常量(可以是字符串或数字) | `pushconst "m_width"` | | `pushbyte N` | 压入 1 字节整数(-128 ~ 127) | `pushbyte 3` | | `pushshort N` | 压入 2 字节整数 | `pushshort 200` | | `pushnull` | 压入 null | | | `pushundefined` | 压入 undefined | | | `pushtrue` | 压入 true | | | `pushfalse` | 压入 false | | | `pushthis` | 压入当前 this | | | `pushglobal` | 压入 _global | | | `pushvar "..."` | 压入变量值 | `pushvar "Cafe2_BaseUIScreen"` | | `pushreg N` | 压入寄存器 N 的值 | `pushreg 2` | | `EAPushZero` | 压入 0(EA 特有快捷指令) | | | `EAPushOne` | 压入 1(EA 特有快捷指令) | | | `zerovar` | 压入 0(等价于 EAPushZero) | | ### 5.2 变量操作指令 | 指令 | 说明 | 示例 | |---|---|---| | `ActionSetVariable` | `obj.prop = value` (从栈取 3 个值:obj, prop, value) | | | `ActionSetMember` | `obj[prop] = value` (类似 SetVariable) | | | `ActionDefineLocal` | 定义局部变量(栈顶为值,栈第二为名称) | | | `ActionDelete` | 删除对象的属性 | `pushobj → pushprop → ActionDelete` | | `setstrvar "..."` | 压入字符串并设置变量(快捷组合指令) | `setstrvar "Up/Down"` | | `getnamedmember "..."` | 压入对象的命名成员 | `getnamedmember "prototype"` | | `getstrmember "..."` | 压入对象的字符串成员 | `getstrmember "_parent"` | | `ActionGetMember` | 通过计算获取成员(栈顶为索引,栈第二为 obj) | | ### 5.3 控制流指令 | 指令 | 说明 | |---|---| | `jumptrue L` | 如果栈顶为 true,跳转到标签 L | | `ActionLogicalNot` | 栈顶取反(`!value`) | | `ActionReturn` | 从函数返回 | | `end` | 函数定义结束 | | `stop` | 停止播放头 | | `ActionPop` | 弹出并丢弃栈顶值 | ### 5.4 运算指令 | 指令 | 说明 | |---|---| | `ActionNewAdd` | 字符串拼接:`a + b` | | `ActionNewEquals` | 相等比较:`a == b` | | `ActionNewLessThan` | 小于比较:`a < b` | | `ActionIncrement` | 栈顶值 +1 | | `ActionDecrement` | 栈顶值 -1 | | `gt` | 大于比较(对应 `>` 运算符) | | `stricteq` | 严格相等(`===`) | ### 5.5 对象/函数操作 | 指令 | 说明 | |---|---| | `ActionNew` | 创建对象实例(栈顶为参数个数,栈第二为构造函数) | | `ActionCallMethod` | 调用方法(从栈取 obj, method, argCount, args...) | | `ActionCallFunction` | 调用全局函数 | | `callnamedmethodpop "..."` | 调用命名方法(快捷指令) | | `callmethodpop` | 调用栈上的方法 | | `callnamedpop "..."` | 调用命名全局函数(快捷指令) | | `extends` | 设置继承关系 | | `function (regs:N) ... end` | 定义函数(regs 声明寄存器总数) | | `ActionInitArray` | 初始化数组 | | `setreg N` | 将栈顶值存入寄存器 N | ### 5.6 通信指令 | 指令 | 说明 | |---|---| | `geturl "FSCommand:...", "..."` | 调用游戏引擎函数(同步通信) | | `geturl2` | 获取 URL(参数已在栈上) | | `ActionTrace` | 输出调试信息 | ### 5.7 寄存器约定 APT bytecode 使用寄存器(reg0, reg1, reg2, ...)来存储局部变量。根据反编译代码,常见的寄存器约定是: - **reg0**:controller(事件控制器参数) - **reg1**:`this` - **reg2**:`_global` - **reg3**:`super` 或第一个参数 - **reg4+**:后续参数 函数定义开头的 `(regs:N, reg1:this, reg2:_global)` 表示此函数最多使用 N 个寄存器,并指定了某些寄存器的角色。 ### 5.8 反编译代码对照示例 以下是将 edit.xml 中的 ActionScript bytecode 与反编译的 AS 2.0 代码进行对照的示例: **edit.xml 中的 clipaction(构造初始化):** ``` pushstr "m_width" pushshort 200 ActionSetVariable pushstr "m_textAlign" pushfalse ActionSetVariable pushstr "m_labelPosition" setstrvar "right align" pushstr "m_contentSymbol" pushconst "SovietFactionButtonContent Symbol" ActionSetVariable pushstr "m_controller" pushconst "factionButtonController" ActionSetVariable ``` **对应的反编译 AS 2.0 代码:** ```as on(construct){ m_width = 200; m_textAlign = false; m_labelPosition = "right align"; m_contentSymbol = "SovietFactionButtonContent Symbol"; m_controller = "factionButtonController"; } ``` **对照解读**: - `pushstr "m_width"` + `pushshort 200` + `ActionSetVariable` = `m_width = 200` - `pushstr "m_textAlign"` + `pushfalse` + `ActionSetVariable` = `m_textAlign = false` - `setstrvar "right align"` = 先 `pushstr "m_labelPosition"`,然后 `pushstr "right align"`,然后 `ActionSetVariable` > **重要**:`setstrvar "X"` 是一个快捷指令,等价于 `pushstr` + `ActionSetVariable`,用于设置之前已经推入的属性名为值 X。 --- ## 6. 修改指南 ### 6.1 添加新的 UI 元素(以「添加第四阵营按钮」为例) 这是一个完整的端到端示例,展示在 `fes_m_factionMenu` 中添加第四个阵营按钮所需的 edit.xml 变更。 #### 场景说明 当前 fes_m_factionMenu 有三个阵营按钮:Soviet(苏联)、Allies(盟军)、Japanese(旭日/日本)。现在要添加第四个"Empire of the Rising Sun"之外的阵营。 #### 需要修改的部分(按顺序) **步骤 1:定义新阵营按钮的「内容」角色** 每个阵营按钮的内容(按钮上的图案)是一个 sprite,包含多个状态帧。需要创建: - 新 shape(背景图)→ 例如 `shape id="34"`(使用下一个可用 ID) - 新 shape(选中高亮图)→ 例如 `shape id="35"` - 新 sprite(按钮内容,包含各个状态帧)→ 例如 `sprite id="36"` - 相关 image 引用 参考现有模式(AlliedFactionButtonContent = sprite 20): ```xml c s tc:255:255:255:255:art\Textures\apt_fes_m_factionMenu_1.tga:1:0:0:1:XXX:YYY t 0:451:0:0:269:0 t 269:0:269:451:0:451 ... ``` **步骤 2:添加新阵营按钮的 import(如果需要)** 如果新阵营按钮使用已导入的角色类型(如 `std_mouseRadioButtonSymbol`),无需新增 import。否则添加: ```xml ``` 并添加对应的 ``。 **步骤 3:添加 export** ```xml ... ``` **步骤 4:在主时间线 frame 0 中添加 placeobject** 在主时间线 frame 0 中放置新按钮(参考 `japaneseFaction` 的放置模式): ```xml ``` **关键注意事项**: - **`depth`**:需使用未占用的值。sovietFaction 用 depth 9,alliesFaction 用 depth 11,japaneseFaction 用 depth 13,所以新按钮可以用 depth 14(或其他未使用的奇数 depth) - **`tx`**:X 坐标。参考:sovietFaction tx=256,alliesFaction tx=551,japaneseFaction tx=839,第四个约 tx=1127(间距约 288 像素) - **`m_tabIndex`**:焦点切换顺序。soviet→1, allies→2, japanese→3, 新→4 - **`m_contentSymbol`**:指向步骤 1 中创建的 sprite 的 export 名称 - **`m_controller`**:必须与现有的 `factionButtonController` 一致,使得新按钮也受 RadioButtonController 管理 **步骤 5:修改 initaction(sprite 33)中的 ActionScript 逻辑** 在 initaction 的 `initGUI` 方法中添加新按钮的初始化代码。参照现有模式: 在 japaneseFaction 的初始化代码之后添加: ``` EAPushZero pushvar "Cafe2_BaseUIScreen" getnamedmember "m_screen" getnamedmember "fourthFaction" callnamedmethodpop "show" EAPushZero pushvar "Cafe2_BaseUIScreen" getnamedmember "m_screen" getnamedmember "fourthFaction" callnamedmethodpop "enable" pushreg 1 getnamedmember "fourthFactionButtonClicked" pushreg 1 pushbyte 2 pushreg 2 pushconst "bind0" ActionCallMethod EAPushOne pushvar "Cafe2_BaseUIScreen" getnamedmember "m_screen" getnamedmember "fourthFaction" callnamedmethodpop "setOnMouseDownFunction" EAPushZero pushvar "Cafe2_BaseUIScreen" getnamedmember "m_screen" getnamedmember "fourthFaction" callnamedmethodpop "noIntro" ``` **步骤 6:添加新的事件处理方法** 在 initaction 中添加 `fourthFactionButtonClicked` 方法定义(在 `japaneseFactionButtonClicked` 的 `ActionSetMember` 之后): ``` pushreg 2 pushconst "fourthFactionButtonClicked" function ()(regs:2, reg1:_global) pushconst "fes_m_factionMenu::fourthFactionButtonClicked()" ActionTrace pushreg 1 getnamedmember "GAME" pushconst "MODE" pushreg 1 getnamedmember "ENUM" getnamedmember "GAME_MODE_CAMPAIGN" ActionSetMember pushreg 1 getnamedmember "GAME" pushconst "CAMPAIGN_TYPE" pushreg 1 getnamedmember "ENUM" getnamedmember "CAMPAIGN_TYPE_FOURTH" ActionSetMember pushvar "factionDescriptionTF" pushconst "text" pushconst "$FOURTH_FACTION_DESCRIPTION&Outline" ActionSetMember pushvar "factionDescriptionShadowTF" pushconst "text" pushconst "$FOURTH_FACTION_DESCRIPTION" ActionSetMember end ActionSetMember ``` **步骤 7:更新 `requestNewCampaign` 方法(如需要)** 如果需要处理新阵营,修改对应的 geturl 调用以正确传递 CAMPAIGN_TYPE 值。 > **注意**:上述 bytecode 是示意性的。实际操作中建议先用 AptToXML 工具验证、或从小修改开始逐步积累经验。 #### 添加第四阵营的补充注意事项 以上步骤展示了核心的 edit.xml 修改,但以下细节也需关注: 1. **depth 一致性**:现有三个按钮使用奇数 depth(9→soviet, 11→allies, 13→japanese),新按钮应使用 `depth="15"` 保持此规律。 2. **intro/outtro 回调**:如果新按钮的内容 sprite 需要参与入场/出场动画,其 intro 帧和 outtro 帧的最后必须调用 `this._parent.introComplete()` 和 `this._parent.outtroComplete()`(参考 sprite 10 的 frame 86 和 frame 119)。 3. **RadioButtonController 默认选中**:initGUI 方法末尾有 `SetSelectedRadioButton(Cafe2_BaseUIScreen.m_screen.sovietFaction)`,将苏联阵营设为默认选中。添加新按钮后不需要修改此行(除非你想让新按钮成为默认选中)。 4. **`CAMPAIGN_TYPE_FOURTH` 枚举值**:bytecode 中引用的 `CAMPAIGN_TYPE_FOURTH` 必须在**游戏引擎 C++ 代码**中已有定义。如果引擎不认识此枚举值,`requestNewCampaign` 中的 `geturl` 调用将静默失败——游戏不会启动新战役。 5. **资产声明文件(`.xml`)**:添加新 shape 后,记得在对应的 `.xml` 资产声明文件(如 `fes_m_factionMenu.xml`)中添加 `` 条目。如果使用了新纹理,还需添加 `` 声明。 6. **`noIntro` 调用顺序**:观察现有代码发现,sovietFaction 和 japaneseFaction 的 `noIntro()` 调用在 `setOnMouseDownFunction` **之前**,而 alliesFaction 的 `noIntro()` 在**之后**。这可能是无关紧要的顺序差异,建议保持一致(推荐先 noIntro 再 setOnMouseDownFunction 的模式)。 --- ### 6.2 修改现有元素 #### 改变位置 修改 placeobject 的 `tx` / `ty` 属性: ```xml tx="400.000000" ty="700.000000" ``` #### 改变大小 修改 placeobject 的 `rotm00`(X 缩放)和/或 `rotm11`(Y 缩放): ```xml rotm00="1.500000" ``` #### 改变颜色/透明度 修改 `red` / `green` / `blue` / `alpha`: ```xml red="255" green="255" blue="255" alpha="128" ``` > **注意**:只有当 flags 包含 `HasColorTransform` 时颜色属性才有效。 #### 改变文本 修改 edittext 的 `` 内容或 `` 绑定: ```xml ``` 或修改对应的本地化字符串引用(如 `$NEW_LABEL`)。 --- ### 6.3 添加/修改 ActionScript 逻辑 #### 修改 clipaction 中的初始化参数 clipaction 用于设置 UI 组件(按钮、dropdown、checkbox 等)的初始属性。例如修改按钮宽度: ```xml ActionSetVariable ... ]]> ``` #### 添加新的帧动作 在帧中添加 ``: ```xml ``` #### 修改 initaction 逻辑 这是最复杂的修改,需要对 EA bytecode 有较好理解。建议: 1. 先用反编译工具理解现有逻辑(参考 `decompiled-as-references/` 中的 .as 文件) 2. 找出需要修改的 bytecode 段 3. 按照 bytecode 模式进行修改 如果只是添加新方法(如上文添加 `fourthFactionButtonClicked`),可以复制现有类似方法的 bytecode 并修改关键字符串。 --- ### 6.4 导入和导出角色 #### 添加新 import 假设你需要使用另一个 APT 中定义的角色: 1. 在 `` 中添加: ```xml ``` 2. 在角色定义区域添加对应的 ``: ```xml ``` 3. 然后就可以在 placeobject 中通过 `character="40"` 引用此角色 #### 添加新 export 如果其他 APT 需要引用你的角色: ```xml ``` --- ### 6.5 添加或修改 shape / 图形 **只需修改 edit.xml 中的 `` 内容。** 外部 `.ru` 文件由工具自动生成,不需要手动编辑。 #### 修改现有 shape 直接编辑 `` 标签内的 ru 命令。 #### 创建新 shape 1. 选择下一个可用的 character ID 2. 创建新的 `` 元素: ```xml c s s:255:0:0:255 t 0:100:0:0:200:0 t 200:0:200:100:0:100 ``` 3. 在 `fes_m_factionMenu.xml`(资产声明文件)中添加对应的 geometry 声明: ```xml ``` --- ### 6.6 处理纹理和图片资源 新纹理需要: 1. 将 `.tga` 文件放入 `art/Textures/` 目录 2. 在 `fes_m_factionMenu.xml`(资产声明)中添加 Texture 声明: ```xml ``` 3. 在 edit.xml 中创建 `` 引用新纹理,或在 ru 命令中直接引用纹理路径 --- ### 6.7 修改可用性、显示隐藏和初始状态 APT 组件状态可能由**多处控制**,修改一处可能被其他地方覆盖: - **construct 参数**:`m_bVisible`、`m_bEnabled`、`m_initiallySelected` - **初始化函数**:`initGUI()` 中的 `show()`、`enable()`、`disable()`、`noIntro()` - **刷新函数**:`refreshComponents()` 中按游戏数据设置状态 - **消息处理器**:收到消息后批量启用/禁用(如 `FE_MP_ONLINE_GAME_COUNTDOWN_START`) - **视觉组件**:内部根据 `isEnabled()`、`isChecked()` 等跳转到不同帧标签 如果你改了初始可见性但进游戏后被立刻改回,优先查 `frameDelayedInit()` → `initGUI()` → `refreshComponents()` → message handler 这条链路上的所有状态设置。 ### 6.8 修改列表、下拉框、滑条 在 game setup 类界面中,列表和下拉框的数据多数来自脚本运行时填充,而非 edit.xml 静态定义: ```as mapListComponent.init(mouseListboxArr, 26); mapListComponent.attachScrollbarController(mapListScrollbarComponent); mapListComponent.setColumnData(0, mapList); dropDownMenu.setData(labels, values); dropDownMenu.setSelectedIndex(index); slider.setIndexRange(min, max, current); ``` 修改这类控件时,不要只改 `edittext` 或 visual sprite。要检查: - construct 参数是否配置了宽度、标签、内容 symbol(`m_contentSymbol`、`m_entrySymbol`) - 初始化时是否调用 `init()` 或绑定 scrollbar - 数据是否来自 `loadVariables("QueryGameEngine?...")` - 选中变化是否绑定了 `setOnChange(...)` 回调 - 回调是否向游戏侧发送 `FSCommand` 例如 `fe_m_assetsGameSetup` 的 `playerSlotMC` 内部放置了 `std_mouseDropDownMenuSymbol`(下拉菜单)、`std_MouseCheckBoxSymbol`(复选框)等通用控件。这些控件只有在 screen class 调用 `show/enable/setOnChange/setOnMouseDownFunction` 后才完整可用。 ### 6.9 新增或复制嵌套组件 适用场景:复制一个玩家槽、复制一个规则面板控件、在导入的 assets APT 中新增子控件。 步骤: 1. 确认组件定义在哪个 APT。顶层 screen 可能只是 import 2. 在组件 sprite 内复制 `placeobject`,选择不冲突的 depth 3. 给新实例设置唯一 `poname` 4. 如果组件脚本通过数组管理实例(如 `this.m_playerSlots = [slotMC_1, slotMC_2, ...]`),同步扩展数组 5. 如果控件需要初始化,加入 `show/enable/setOnChange/setOnMouseDownFunction` 等绑定 6. 如果控件需要刷新数据,加入对应 `refresh...()` 分支 7. 如果控件发送命令,确认游戏侧有对应 FSCommand 或 QueryGameEngine 字段 > **注意**:只在 XML 里复制一个下拉菜单或复选框,通常只能让它显示出来;没有 AS 初始化和数据刷新时,它可能没有数据、不能交互,或下一次刷新时状态被覆盖。 --- ## 7. 依赖关系与级联变更 修改 edit.xml 的某一部分时,可能需要同时更新其他部分。以下是最常见的级联变更情况: ### 7.1 添加新角色时 | 修改内容 | 需要级联更新的地方 | |---|---| | 新 shape | `.xml` 资产声明中添加 `` | | 新 image(新纹理) | `.xml` 资产声明中添加 `` | | 新 import | 添加对应的 `` | | 新 export | 确保对应的 character ID 存在 | | 新 sprite(用于代码类) | 对应的 `` 需要引用此 sprite | | 新 placeobject | 确保 character ID 已在 imports 或本文件中定义;确保 depth 不冲突 | ### 7.2 修改 depth 时 - 每个 placeobject/removeobject 的 depth 在同一 frame 中应唯一 - 确保 sprite 中使用的 depth 层次合理(如:背景用 depth 1,按钮图案用 depth 3,文字用 depth 5/7) - 确保 removeobject 的 depth 与目标 placeobject 的 depth 匹配 ### 7.3 修改 tabIndex 时 - 如果使用 RadioButtonController 或 FocusManager,tabIndex 控制焦点切换顺序 - 确保同组 UI 元素的 tabIndex 不冲突 ### 7.4 修改动作逻辑时 - initaction 中定义的类名要和 `` 中的名称一致 - initaction 中引用的 `m_screen.xxx` 必须与 placeobject 的 `` 一致 - 事件回调方法名(如 `sovietFactionButtonClicked`)在 initaction 中必须有对应的函数定义 ### 7.5 修改 shape 时 - `.xml` 资产声明中 `` 的 `AptID` 必须与 shape 的 `id` 一致 - `geometry` 路径必须存在对应的 `.xml` 声明 ### 7.6 修改前关系检查表 修改任何可交互 UI 前,建议逐项检查: - [ ] character ID 是否唯一,引用是否全部更新 - [ ] `exports` 的 name 是否与脚本中的 symbol 字符串一致 - [ ] `imports` 的 character ID 是否与 ``、顶层 `placeobject character` 一致 - [ ] `placeobject depth` 是否在同一 timeline 中冲突 - [ ] `poname` 是否与 AS2 中访问的实例名一致 - [ ] `m_contentSymbol` 是否指向导出的 content sprite - [ ] `m_controller` 是否指向同级 controller 的 `poname` - [ ] content sprite 是否包含组件脚本要求的子实例名(如 `hitRegion`、`checkBoxMC`、`labelMC`) - [ ] 帧标签是否与组件脚本的 `gotoAndPlay` 字符串一致 - [ ] 文本是否由 AS2 覆盖(而非静态 `ettext`) - [ ] 点击逻辑是否还依赖全局对象、枚举、消息码或 FSCommand - [ ] 修改贴图/shape 时,edit.xml 内嵌 ``、`.tga`、资产声明 XML 是否同步 - [ ] 如果目标来自另一个 APT 的 import,是否去那个 assets APT 检查真正的 sprite、子实例和导出名 - [ ] 是否存在 `HasColorTransform`、`HasRatio` 等额外状态字段 - [ ] 是否有 `loadVariables("QueryGameEngine?...")` 数据来源需要同步 - [ ] 是否有消息处理器会在之后刷新、隐藏、禁用或重建该控件状态 - [ ] 是否有基类/子类两层脚本共同初始化该界面 - [ ] 是否有列表、下拉框、滑条等需要脚本填充数据的控件 - [ ] 是否修改了视觉但遗漏了 hit region,或修改了 hit region 但遗漏了视觉 - [ ] 是否修改了一个状态帧,但其他状态帧仍使用旧资源 - [ ] 是否新增了导出符号但没有被任何脚本或 import 使用 - [ ] 是否删除了导出符号但其他 APT 仍在 import --- ## 8. 注意事项与陷阱 ### 8.1 Character ID - **在整个文件中必须唯一**(包括 import 的 ID 和本地定义的 ID) - 下一个可用 ID 是文件中最大 ID + 1 - 注意 `` 也占用 ID,不要与 empty 的 ID 冲突 ### 8.2 Depth 冲突 - 同一帧中,相同 depth 的 placeobject 会替换之前的 - 在 sprite 内部的不同帧之间,通过 removeobject + placeobject(同 depth)实现状态切换 - 开发时确保每个 depth 的用途清晰一致 ### 8.3 帧标签约定 - 帧标签命名是 Cafe2 UI 框架的要求,不要随意更改如 `_disable`、`_selected` 等特殊标签名称 - 帧标签值必须与 frame id 一致(如 `framelabel label="_disable" frame="0"`) ### 8.4 矩阵变换顺序 - 对象先应用 rotm00-rotm11 的旋转/缩放,再平移到 (tx, ty) - 矩阵的默认值(恒等变换)是 `rotm00="1" rotm01="0" rotm10="0" rotm11="1"` ### 8.5 保持 unknown 属性不变 - `movieclip` 的 `unknown="33"`、`placeobject` 的 `unknown="0"` 等属性保持原值不变 - 这些属性的确切含义不明,修改可能导致不可预期的结果 ### 8.6 保持 ratio 属性不变 - `ratio` 属性出现在 `_intro`、`_selectedRollover` 等过渡帧中 - 其确切语义不明(可能与动画混合/插值有关) - 修改时保持原有的 ratio 值 ### 8.7 empty 与 import 严格对应 - 每个 import 必须有对应的 empty,数量必须一致 - 添加 import 时必须添加 empty,删除 import 时必须删除 empty ### 8.8 在 bytecode 字符串中使用十进制 HTML 实体 - bytecode 中的换行符使用 ` ` 而非 `\n`(` ` 是 bytecode 字符串字面量中的十进制字符引用,AS 引擎将其解释为换行;此写法与 CDATA 无关) - 由于所有 bytecode 均在 CDATA 中,`&` 等字符**不需要** XML 实体转义(CDATA 内所有内容按字面处理) - 示例:`pushconst " Cafe2_BaseUIScreen::frameDelayedInitCallback() :"` ### 8.9 工具链限制 - 修改 edit.xml 后必须通过 XMLToApt 工具重新生成 .apt 和相关文件 - 不要手动编辑 .const、.dat、.eaf、.tac、geometry .ru 等自动生成的文件 - 如果修改了 geometry,记得同时更新 .xml 资产声明文件 ### 8.10 常见错误 - **忘记添加 empty**:添加新 import 后忘记添加对应的 ``,导致工具转换失败 - **忘记更新资产声明**:添加新 shape / image 后未在 .xml 中添加对应声明 - **depth 规划不当**:可能导致对象显示层级错误 - **tabIndex 冲突**:两个同组元素使用相同 tabIndex - **CDATA 格式**:所有 bytecode 元素(``、``、``)均使用 `` 包裹,内部无需 XML 实体转义 ### 8.11 旋转与斜切 - 旋转变换涉及 `rotm01` 和 `rotm10`(非对角线元素),不要把它们误删成 0 - 仅改 `rotm00` 和 `rotm11` 做的是缩放,不会旋转对象 - 如果原始 placeobject 有非零的 `rotm01`/`rotm10`,复制到新实例时不要遗漏 ### 8.12 API 大小写不一致 - 反编译代码和 edit.xml 中同时存在 `getURL` 和 `getUrl` 两种写法 - 同样的还有 `gotoAndPlay` / `gotoAndPlay` 等 - 这是历史遗留的大小写不一致,不要在未确认工具链行为前自动"修正"它们 - 修改脚本时保持与原始代码一致的写法 ### 8.13 图集与 shape 边界 - `shape` 的 `top/left/bottom/right` 是包围盒,`` 中的 `t`/`l` 命令才是实际绘制几何 - 改宽高时要同时改两个三角形的相关顶点 - 如果 shape 被父级 `placeobject` 缩放,最终屏幕尺寸还要乘上父级 matrix --- ## 9. AS 2.0 SWF → APT 知识迁移 如果你熟悉 Flash SWF(ActionScript 2.0),以下映射表可以帮助你将 SWF 知识快速迁移到 APT edit.xml。 ### 9.1 直接对应的概念 | Flash SWF 概念 | edit.xml 对应 | 说明 | |---|---|---| | SWF 主时间线 | `` | 顶层 movieclip 元素 | | MovieClip / Sprite | `` / `` | 均包含 `` | | 帧 | `` | 帧按 id 递增排列 | | PlaceObject2 标签 | `` | 将角色放置到指定 depth | | RemoveObject2 标签 | `` | 从指定 depth 移除角色 | | Frame Label(帧标签) | `` | 命名的帧标签 | | DoAction 标签(帧动作) | `` | 帧上的 ActionScript 代码 | | DefineSprite 标签 | `` | 定义子 MovieClip | | DefineShape 标签 | `` + `` | 矢量图形定义 | | DefineButton 标签 | `