Appearance
配置文件
引擎启动时自动读取 unison.xml,该文件位于引擎可执行文件所在目录。XML 根元素为 <ppg3d>。
完整示例
xml
<ppg3d>
<pymodules>
<module name="exio3">
<function name="add_cpk_to_cpkhub" type="vfs.mount"/>
<function name="get_file" type="vfs.get_file"/>
<function name="find_file" type="vfs.find_file"/>
<function name="list_dir" type="vfs.list_dir"/>
<function name="get_doc_dir" type="vfs.get_doc_dir"/>
<function name="get_real_path" type="vfs.get_real_path"/>
</module>
</pymodules>
<client ShowLogWindow="False"
WindowClientWidth="1600"
WindowClientHeight="900"
SysPath="%WORK_DIR%/script/Scripts;%WORK_DIR%/Lib"/>
<python ThreadEnable="True" />
<log FileName="%WORK_DIR%/log_inst_%INST%.txt"
OldFileName="%WORK_DIR%/log_old_%ROTATE%.txt"
NormalFileName="%WORK_DIR%/log.txt"
MaxOldNumber="5"/>
<filesystems>
<filesystem name="scene" type="scene">
<loader root="%WORK_DIR%/scene"/>
</filesystem>
<filesystem name="Scripts" type="script">
<loader root="%WORK_DIR%/script"/>
</filesystem>
<filesystem name="Lib" type="Lib" fsapi="exio3" key="-231">
<loader root="%WORK_DIR%/Lib"/>
</filesystem>
<filesystem name="fonts" type="res" fsapi="exio3">
<loader root="%WORK_DIR%/builtin_res_0"/>
</filesystem>
</filesystems>
</ppg3d><pymodules> — Python 模块映射
定义哪些 Python 第三方模块的函数需要映射到 unison.vfs 供引擎内部使用。
元素结构
xml
<pymodules>
<module name="exio3"> <!-- 要 import 的 Python 模块 -->
<function name="add_cpk_to_cpkhub" <!-- 模块中的函数名 -->
type="vfs.mount"/> <!-- 映射到 unison.vfs 的目标名 -->
<function name="get_file" type="vfs.get_file"/>
<function name="find_file" type="vfs.find_file"/>
<function name="list_dir" type="vfs.list_dir"/>
<function name="get_doc_dir" type="vfs.get_doc_dir"/>
<function name="get_real_path" type="vfs.get_real_path"/>
</module>
</pymodules>属性详解
module 子元素
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | str | 是 | Python 模块名。引擎执行 import <name> 导入该模块 |
function 子元素
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | str | 是 | 源模块中的函数名。如 add_cpk_to_cpkhub |
type | str | 是 | 函数在 unison.vfs 上的目标名,格式 vfs.xxx。解析时取 . 之后的部分作为绑定名 |
解析规则
- 引擎按
<module>顺序逐个导入,import失败则跳过该模块,继续处理下一个 - 每个
<function>检查源模块中是否存在对应函数,存在才绑定到unison.vfs type属性格式为vfs.<target_name>,.之后的部分成为unison.vfs的属性名
示例效果
以下配置:
xml
<function name="add_cpk_to_cpkhub" type="vfs.mount"/>等价于 Python:
python
import exio3
unison.vfs.mount = exio3.add_cpk_to_cpkhub典型配置
引擎通常只需配置一个 exio3 模块。exio3 是 CEFS 文件系统的 Python 绑定,负责 CPK 加密包的读取。
各函数签名与参数
下表中的"源函数"指 exio3 模块的函数,"目标名"指映射到 unison.vfs 后的属性名。
| 源函数 | 目标名 | 引擎内部用途 | 函数签名 |
|---|---|---|---|
add_cpk_to_cpkhub | mount | 挂载 CPK 文件到虚拟文件系统 | add_cpk_to_cpkhub(cpk_path: str, mount_point: str, encryption_key: int = 0) |
find_file | find_file | 在 VFS 中查找文件是否存在 | find_file(vfs_path: str) -> bool |
get_file | get_file | 从 VFS 读取文件内容 | get_file(vfs_path: str) -> bytes |
list_dir | list_dir | 列出 VFS 目录内容 | list_dir(vfs_path: str) -> list[str] |
get_doc_dir | get_doc_dir | 获取文档目录路径 | get_doc_dir() -> str |
get_real_path | get_real_path | 虚拟路径转真实路径 | get_real_path(vfs_path: str) -> str |
add_cpk_to_cpkhub / mount 参数详解:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
cpk_path | str | 是 | CPK 文件的绝对路径 |
mount_point | str | 是 | 挂载点目录名,以 / 结尾。如 "Lib/" |
encryption_key | int | 否 | AES 解密密钥,正整数或负整数。默认为 0(不加密)。密钥通过 PBKDF2 派生 |
示例:
python
# 引擎内部调用(由配置自动生成)
exio3.add_cpk_to_cpkhub("C:/game/Lib.cpk", "Lib/", encryption_key=-231)
exio3.add_cpk_to_cpkhub("C:/game/fonts.cpk", "fonts/")get_file 返回值:
- 返回
bytes,调用方自行解码为str或直接处理二进制数据
find_file 返回值:
- 返回
bool,只检测文件是否存在,不读取内容
无 <pymodules> 时的行为
如果配置文件中没有 <pymodules>(或为空),引擎仍然会创建 unison.vfs 模块并注册混合 API(如 file_exists、read_file_bytes),但 mount、find_file、get_file 等基础 VFS 函数不会可用,CPK 将无法挂载。
<client> — 客户端设置
控制引擎窗口和运行时参数。
属性列表
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
WindowClientWidth | int | 1280 | 游戏窗口客户区宽度(像素)。值必须 > 0 |
WindowClientHeight | int | 720 | 游戏窗口客户区高度(像素)。值必须 > 0 |
ShowLogWindow | bool | false | 是否显示调试日志窗口。值: True/False/true/false/1/0 |
SysPath | str | 无 | Python 的 sys.path 额外搜索路径,多个路径用 ; 分隔。支持 %WORK_DIR% 等宏 |
SysPath 示例
xml
<client SysPath="%WORK_DIR%/extra;%WORK_DIR%/tools"/>注意:
SysPath在 CPK 挂载之前生效。CPK 挂载完成后,sys.path会被引擎清理并仅保留以下类型的文件系统路径:script、Lib、tools、scene。
<python> — Python 运行时
控制内嵌 NEPython 3.13.14 运行时的行为。
属性列表
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ThreadEnable | bool | true | 是否启用 Python 子线程支持。值: True/False/true/false/1/0 |
行为说明
True:允许 Python 脚本创建子线程,适合有异步需求的游戏逻辑False:禁用多线程,所有 Python 代码在引擎主线程执行。略微减少开销,适合纯同步游戏逻辑
<log> — 日志配置
控制引擎日志文件的命名和轮转。如果未配置 <log> 节点,日志仅输出到控制台,不写入文件。
属性列表
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
FileName | str | 无 | 首次启动的日志文件名。支持 %INST% 宏 |
OldFileName | str | 无 | 日志轮转时的旧文件名。支持 %ROTATE% 宏 |
NormalFileName | str | 无 | 正常运行的日志文件名 |
MaxOldNumber | int | 5 | 最多保留的旧日志文件数量 |
日志轮转流程
- 引擎启动时,如果
NormalFileName文件已存在,将其重命名为OldFileName格式(%ROTATE%依次递增) MaxOldNumber限制最多保留的文件数(0 表示不限制)FileName用于实例化启动(如配置%INST%为不同值以区分多个进程)
示例配置
xml
<log FileName="%WORK_DIR%/log_inst_%INST%.txt"
OldFileName="%WORK_DIR%/log_old_%ROTATE%.txt"
NormalFileName="%WORK_DIR%/log.txt"
MaxOldNumber="5"/>轮转效果(假设 MaxOldNumber=3):
log.txt → log_old_0.txt → log_old_1.txt → log_old_2.txt → 删除
(当前) (旧日志) (更旧) (最旧) (超出限制)<filesystems> — 文件系统
定义引擎需要挂载的虚拟文件系统。这是配置中最核心的部分。
元素结构
xml
<filesystems>
<filesystem name="名称"
type="类型"
[key="加密密钥"]
[fsapi="后端模块"]>
<loader root="%WORK_DIR%/路径"
[type="directory"]/>
</filesystem>
</filesystems>filesystem 属性
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | str | 是 | 文件系统名称,同时作为挂载点目录名 |
type | str | 是 | 文件系统类型,决定挂载后如何处理 |
key | int | 否 | CPK 加密密钥。不设置或设为 0 表示不加密。注意:负数也是有效密钥 |
fsapi | str | 否 | 强制使用的 VFS 后端模块名(如 exio3)。有值时引擎直接通过该模块挂载 CPK |
loader 子元素
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
root | str | 是 | 文件系统根路径,支持 %WORK_DIR% 和 %DOC_DIR% 宏 |
type | str | 否 | 设为 directory 时,以目录模式挂载(直接读取真实文件夹)。不设置或留空时,以CPK 模式挂载 |
CPK 文件查找策略
引擎按以下优先级查找文件系统对应的 CPK 文件:
<root>.cpk— 优先尝试(如%WORK_DIR%/script→C:\game\script.cpk)<root>本身 — 如果.cpk文件不存在,则尝试直接使用 root 路径(适用于文件本身就带扩展名的情况)
xml
<!-- 情况 A:root="script" → 查找 script.cpk → 如果不存在 → 再查 script -->
<filesystem name="Scripts" type="script" key="241">
<loader root="%WORK_DIR%/script"/>
</filesystem>
<!-- 情况 B:root="data.apd" → 查找 data.apd.cpk → 如果不存在 → 再查 data.apd -->
<filesystem name="Data" type="res">
<loader root="%WORK_DIR%/data.apd"/>
</filesystem>只有两次尝试都失败时,才输出警告日志并跳过该文件系统。
type 取值与行为
type | 对应的 sys.path | 场景挂载点 | 说明 |
|---|---|---|---|
scene | 是 | 是 | 场景资源目录。添加到 sys.path 并注册为脚本搜索路径 |
script | 是 | 是 | 游戏脚本目录。添加到 sys.path 和脚本搜索路径 |
Lib | 是 | 否 | 引擎库目录(Python 标准库扩展)。添加到 sys.path;优先于其他文件系统挂载 |
tools | 是 | 否 | 工具脚本目录。添加到 sys.path |
res | 否 | 否 | 纯资源目录(字体/纹理等)。不添加到 sys.path |
| 其他自定义值 | 否 | 否 | 不添加到 sys.path,仅文件系统挂载 |
关键行为:
scene— 不仅挂载文件,还作为场景管理系统的基础搜索路径script— 挂载后,其中的.py文件可被import直接导入(通过 CpkImporter)Lib— 优先挂载顺序:在preMountLibOnly阶段最先挂载,确保 CpkImporter 等基础模块先可用res和自定义类型 — 仅文件访问,不参与 Python 导入
key 加密密钥
xml
<!-- 不加密 -->
<filesystem name="data" type="res">
<loader root="%WORK_DIR%/data"/>
</filesystem>
<!-- AES 加密,密钥 241 -->
<filesystem name="Scripts" type="script" key="241">
<loader root="%WORK_DIR%/script"/>
</filesystem>
<!-- AES 加密,密钥 -231 -->
<filesystem name="Lib" type="Lib" key="-231">
<loader root="%WORK_DIR%/Lib"/>
</filesystem>- 密钥为 有符号整数,正负均可
- 密钥通过 PBKDF2 从数值派生 AES 密钥
- CPK 文件
<root>.cpk中的资源使用对应密钥加密后才能正确读取
fsapi 后端模块
xml
<!-- 默认:通过 unison.vfs (pymodules 映射) 挂载 -->
<filesystem name="Scripts" type="script" key="241">
<loader root="%WORK_DIR%/script"/>
</filesystem>
<!-- fsapi="exio3":直接通过 exio3 模块挂载,不经过 pymodules 映射 -->
<filesystem name="Lib" type="Lib" fsapi="exio3" key="-231">
<loader root="%WORK_DIR%/Lib"/>
</filesystem>区别:
| 情况 | 挂载调用 |
|---|---|
无 fsapi | vfs.mount(path, mount_point, encryption_key=key) — 使用 pymodules 映射 |
fsapi="exio3" | exio3.add_cpk_to_cpkhub(path, mount_point, encryption_key=key) — 直接调用 |
使用 fsapi 的场景:当某些文件系统需绕过 unison.vfs 的 pymodules 映射链路,直接由指定后端处理。
loader.type="directory" — 目录模式
xml
<!-- 目录模式:直接挂载真实文件夹 -->
<filesystem name="scene" type="scene">
<loader root="%WORK_DIR%/scene" type="directory"/>
</filesystem>目录模式下:
- 不查找
.cpk文件,直接读取真实磁盘目录 - 通过
DirectoryVFS挂载,不依赖 exio3 - 适合开发调试阶段(资源未打包为 CPK)
sys.path 最终列表
引擎在挂载所有文件系统后,重置 sys.path 为仅包含 ['.'],然后重新添加以下类型的文件系统路径:
sys.path = ['.', 'Scripts/', 'Lib/', 'scene/']只有 type 为 script、Lib、tools、scene 的文件系统才进入 sys.path。
挂载顺序
Lib类型优先挂载(preMountLibOnly阶段)— 确保基础模块先可用- 其他类型按 XML 配置顺序挂载
- 引擎脚本搜索路径按配置顺序排列(先配的优先查找)
完整配置示例
xml
<filesystems>
<!-- 1. 场景资源(目录模式,开发调试用) -->
<filesystem name="scene" type="scene">
<loader root="%WORK_DIR%/scene" type="directory"/>
</filesystem>
<!-- 2. 游戏脚本(加密 CPK) -->
<filesystem name="Scripts" type="script" key="241">
<loader root="%WORK_DIR%/script"/>
</filesystem>
<!-- 3. 引擎库(加密 CPK,直接使用 exio3 挂载) -->
<filesystem name="Lib" type="Lib" fsapi="exio3" key="-231">
<loader root="%WORK_DIR%/Lib"/>
</filesystem>
<!-- 4. 字体资源(CPK,exio3 挂载) -->
<filesystem name="fonts" type="res" fsapi="exio3">
<loader root="%WORK_DIR%/builtin_res_0"/>
</filesystem>
<!-- 5. 自定义资源(CPK,无加密) -->
<filesystem name="data" type="res">
<loader root="%WORK_DIR%/data"/>
</filesystem>
</filesystems>最终效果:
scene/— 目录访问 + sys.path + 脚本搜索路径Scripts/— CPK 加密访问 + sys.path + 脚本搜索路径Lib/— CPK 加密访问 + sys.path,优先挂载fonts/— CPK 访问,仅文件读取data/— CPK 访问,仅文件读取
路径宏
| 宏 | 替换为 | 示例 |
|---|---|---|
%WORK_DIR% | 引擎工作目录(exe 所在目录) | C:\MyGame\ |
%DOC_DIR% | 用户 Documents 文件夹 | C:\Users\xxx\Documents\ |
%INST% | 进程实例号(递增) | 0, 1, 2... |
%ROTATE% | 日志轮转编号(递增) | 0, 1, 2... |
路径解析
所有路径宏在引擎初始化时被替换为绝对路径:
%WORK_DIR%/script→C:\MyGame\script- CPK 文件路径:
<root>.cpk,如C:\MyGame\script.cpk - 挂载点:以
/结尾,如Scripts/
配置优先级
- 引擎首先搜索
unison.xml - 如果找不到,使用全部默认值(窗口 1280×720,无加密,无 VFS)
- 配置文件中未写的属性全部使用默认值
- XML 解析错误会输出警告但不阻断启动