基础格式与文件结构

1. 概述

本游戏采用 XML 文件作为数据定义的主要形式,允许开发者和模组作者通过结构化的文本文件创建、修改和扩展游戏内容。游戏启动时将扫描特定目录下的 XML 文件,并根据其类型和内容载入游戏。

2. 文件系统结构

游戏会从两个主要目录读取数据:Data/Mods/。这两个目录下包含一系列“包”(Packages),每个包代表一个独立的数据集合,可以是一个核心游戏模块,也可以是一个用户创建的模组。

文件目录结构示例:

├── Data/
│   ├── Pack1/                # 数据包示例 1
│   │   ├── About/            # 包信息目录
│   │   ├── Define/           # 数据定义目录
│   │   └── Translation/      # 翻译文件目录 (当前版本未实装)
│   └── Pack2/                # 数据包示例 2
│       ├── About/
│       ├── Define/
│       └── Translation/
└── Mods/
    ├── Mod1/                 # 模组包示例 1
    │   ├── About/
    │   ├── Define/
    │   └── Translation/
    └── Mod2/                 # 模组包示例 2
        ├── About/
        ├── Define/
        └── Translation/

目录说明:

  • Data/: 包含游戏的核心内容和官方扩展数据包。
  • Mods/: 包含用户自定义的模组数据包。
  • PackN/, ModN/: 每个子文件夹被视为一个独立的“包”。包名应当具有唯一性。
  • About/: 存放包的元数据(Metaproperties)。
  • Define/: 存放各种游戏对象的具体定义。
  • Translation/: 存放多语言翻译数据(当前版本未实装)。

3. XML 文件类型

当前游戏版本主要识别并处理以下三种类型的 XML 文件:

  • About 文件: 包含包的基本信息和加载依赖关系。
  • Define 文件: 包含游戏内的各种数据定义,如物品、角色、属性等。
  • Translation 文件: 用于多语言本地化(当前版本未实装)。

4. About XML 文件

About 文件是每个包的门户,它提供了包的基本信息以及游戏加载时所需的依赖和排序数据。

  • 文件位置: 每个包的 About/ 文件夹下。
  • 文件命名: 建议命名为 About.xml
  • 根元素: 必须以 <About> 为根。
  • 数量限制: 一个包中只能包含一个 About XML 文件。

典型 About 文件示例:

<?xml version="1.0" encoding="utf-8"?>
<About>
    <!-- 包的显示名称,用于用户界面 -->
    <name>核心</name>
    <!-- 包的详细描述,解释其功能或内容 -->
    <description>这是一个基础核心模块,除非有完整的代替,否则应该永远作为启动项</description>
    <!-- 包的版本号 -->
    <version>0.1</version>
    <!-- 包的唯一标识符,必须在所有包中全局唯一 -->
    <packID>core</packID>
    <!-- 加载排序和依赖关系定义 -->
    <sort>
        <!-- 定义本包加载前必须加载的包的packID列表 -->
        <necessary></necessary>
        <!-- 定义本包应该优先于哪些包加载,填写那些包的packID列表 -->
        <before></before>
        <!-- 定义本包应该在哪些包之后加载,填写那些包的packID列表 -->
        <after></after>
    </sort>
</About>

元素说明:

  • <name>: (String) 包的显示名称,会显示在游戏的用户界面中。
  • <description>: (String) 包的详细描述,用以解释其功能或内容。
  • <version>: (String) 包的版本号,遵循语义化版本控制(如 1.0.0)或简单版本号(如 0.1)。
  • <packID>: (String, 必填) 包的唯一标识符。这个 ID 在整个游戏的数据包和模组中必须是全局唯一的,用于识别包及其依赖关系。
  • <sort>: (Object) 包含加载排序和依赖关系的定义。
    • <necessary>: (Array of String) 定义了本包加载前必须加载的包的 packID 列表。如果这些包未能加载,本包将无法加载。
    • <before>: (Array of String) 定义了本包应该优先于哪些包加载。列表中填写其他包的 packID
    • <after>: (Array of String) 定义了本包应该在哪些包之后加载。列表中填写其他包的 packID

加载机制:

游戏启动时,会首先读取所有 About.xml 文件,构建一个包的依赖图。然后根据 necessary, before, after 标签中定义的规则,确定包的加载顺序。

  • 如果多个包定义了相同的 packID,游戏启动时会报错或仅加载其中一个。
  • 如果 necessary 的包缺失,则当前包无法加载。
  • beforeafter 标签用于调整加载顺序,以解决同名 Define 数据覆盖问题或确保特定功能的前提条件。

5. Define XML 文件

Define 文件是游戏内容的核心,用于定义游戏中的各种对象、属性和规则。

  • 文件位置: 每个包的 Define/ 文件夹下。
  • 文件命名: 可以根据内容自由命名,例如 Items.xml, Characters.xml 等。
  • 根元素: 其中的每个 XML 文件必须以 <Define> 为根。
  • 内容: 可以包含任意数量和类别的各种数据定义。

核心约定:

  • 基本属性: 所有的顶级定义都必须包含以下三个属性:
    • defName: (String, 必填) 定义的唯一名称。这个名称在 该类型的数据定义中 必须是全局唯一的。例如,所有 ImageDef 都有唯一的 defName,所有 WeaponDef 也有唯一的 defName,但 ImageDefWeaponDef 可以有相同的 defName(如 TestGunItemTestGun)。
    • label: (String) 定义的显示名称,用于游戏用户界面。
    • description: (String) 定义的详细描述,用于游戏用户界面或工具提示。
  • 覆盖机制: 如果不同包(或同一包内不同文件)中同一类型的数据定义了相同的 defName,则后加载的定义会覆盖先加载的定义。这允许模组轻松地修改或替换原版游戏内容。
  • 引用机制: 当一个定义的数据字段需要引用其他数据定义时,可以通过以下两种方式:
    • 引用 defName: 直接填写被引用定义的 defName。游戏会在加载时解析这些引用。
    • 嵌套定义(匿名定义): 直接在引用位置定义一个新的对象,该对象通常没有 defName,是当前定义的私有或匿名部分。

典型 Define 文件示例:

<?xml version="1.0" encoding="utf-8"?>
<Define>
    <!-- 图片定义 -->
    <ImageDef>
        <defName>TestGunItem</defName> <!-- 唯一标识符 -->
        <path>Resources\Item\TestGun.png</path> <!-- 图片路径 -->
    </ImageDef>

    <!-- 属性定义 -->
    <AttributesDef>
        <defName>TestGun</defName> <!-- 唯一标识符 -->
        <attack>10</attack>
        <defense>3</defense>
        <attackSpeed>3</attackSpeed>
        <attackRange>10</attackRange>
        <attackTargetCount>1</attackTargetCount>
    </AttributesDef>

    <!-- 武器定义 -->
    <WeaponDef>
        <defName>TestGun</defName> <!-- 全局唯一标识符 -->
        <label>测试枪</label> <!-- 显示名称 -->
        <description>一把测试用的枪</description> <!-- 详细描述 -->
        <!-- 引用属性定义,通过defName="TestGun"引用AttributesDef中定义的属性 -->
        <attributes>TestGun</attributes>
        <type>Ranged</type>
        <textures>
            <!-- 引用ImageDef中的图片定义 -->
            <li>TestGunItem</li>
        </textures>
        <!-- 引用子弹定义,通过defName="yellowBullet"引用BulletDef -->
        <bullet>yellowBullet</bullet>
    </WeaponDef>

    <!-- 子弹定义 -->
    <BulletDef>
        <defName>yellowBullet</defName>
        <label>黄色子弹</label>
        <description>一颗黄色的子弹</description>
        <!-- 嵌套定义属性,此处的属性是当前子弹特有的匿名属性,没有defName -->
        <attributes>
            <health>1</health>
            <moveSpeed>20</moveSpeed>
        </attributes>
        <!-- 嵌套定义纹理绘制顺序,包含多个匿名绘制状态定义 -->
        <drawingOrder>
            <idle_down>
                <textures>
                    <li>yellowBullet</li>
                </textures>
            </idle_down>
            <walk_down>
                <textures>
                    <li>yellowBullet</li>
                </textures>
            </walk_down>
        </drawingOrder>
    </BulletDef>
</Define>

示例解析:

  • ImageDef: 定义了一个图像资源。
    • defNameTestGunItem,表示这是图像定义的唯一标识。
    • path 指定了图像文件在文件系统中的相对路径。
  • AttributesDef: 定义了一组属性集。
    • defNameTestGun
    • 包含了攻击、防御等具体数值。
  • WeaponDef: 定义了一个武器对象。
    • defNameTestGun
    • labeldescription 提供显示文本。
    • <attributes>TestGun</attributes>: 这里通过 defName 引用了 AttributesDef 中名为 TestGun 的属性集。
    • <textures><li>TestGunItem</li></textures>: 引用了 ImageDef 中名为 TestGunItem 的图像定义。<textures> 标签下的 <li> 元素表示一个列表项。
    • <bullet>yellowBullet</bullet>: 引用了 BulletDef 中名为 yellowBullet 的子弹定义。
  • BulletDef: 定义了一个子弹对象。
    • defNameyellowBullet
    • <attributes>: 这里没有引用已有的 AttributesDef,而是直接在此嵌套定义了一组属性。这些属性是 yellowBullet 特有的,不与其他定义共享 defName
    • <drawingOrder>: 同样采用嵌套定义的方式,为不同的动画状态(如 idle_down, walk_down)定义了各自的纹理列表。

6. Translation XML 文件 (当前版本未实装)

此类型文件未来将用于实现游戏的多语言本地化。

  • 文件位置: 每个包的 Translation/ 文件夹下。
  • 根元素: 预期的根元素将是 <Translation> 或类似的元素。
  • 内容: 届时将包含键值对形式的翻译文本,用于替换游戏中硬编码的字符串或 Define 文件中的 labeldescription