Skip to content

程序化生成(调色板 / 子结构变体)

上一页:自然生成与预加载(生成总控 / 密度 / 再生) · 下一页:秘境与钥石 相关:遗迹模板 · 结构文件 · 蓝图与目标

前面讲的是「整块结构整体落地」。本页讲两套让遗迹「拼装出来、每次不一样」的程序化能力:

  1. 子结构变体(variants) —— 在主蓝图的槽位上贴一个加权随机的小模板,做「同主体、不同房 / 不同塔尖」。
  2. 调色板拼接(Tile Palette) —— 把多个小结构(瓦片)按连接规则自动拼成一座大遗迹,整体由程序生成。

两者都是「同一座遗迹每次长得不太一样」的来源,但定位不同:变体是主结构 + 局部随机点缀,调色板是从零按规则拼整体

🖼️ [图片占位] 左:主塔 + 随机塔尖(变体);右:由若干房间瓦片自动拼成的地牢(调色板) · 建议 assets/procedural-overview.png


一、子结构变体(variants)

1.1 是什么

主结构生成后,QR 会在蓝图声明的每个变体槽位上,从一组候选模板里加权随机抽一个贴上去(NONE 朝向)。用来做:

  • 同一座塔,塔尖随机是 A / B / C 三种之一;
  • 同一座地宫,某个房间随机是宝库 / 陷阱 / 空房;
  • 「塔基随地形起伏」「子结构高低错落」等地形自适应点缀。

变体写在该遗迹的 blueprint.yml 里(坐标都是相对结构原点,结构最小角 = 0,0,0),蓝图全貌见 蓝图与目标

1.2 字段

yaml
# blueprint.yml 片段
variants:
  - id: top                      # 槽位标识(仅作辨识)
    x: 8
    y: 20
    z: 8                         # 相对结构原点的槽位坐标
    surface: false              # true=贴该 x/z 的真实地表高度(忽略 y),做"塔基随地形起伏"
    y-min: 0                    # 垂直随机重定位:最终 Y 上再随机加 [y-min, y-max] 格
    y-max: 0                    #   都为 0=不抖动;如 y-min:-2 y-max:3 做"高低错落"
    options:                    # 按 weight 加权随机抽一个贴上去(NONE 朝向)
      - { template: spire_a, weight: 30 }
      - { template: spire_b, weight: 20 }
字段默认说明
id槽位标识,仅作辨识。
x / y / z槽位坐标(相对结构原点)。
surfacefalsetrue = 忽略 y,把变体贴到该 x/z真实地表高度(做「塔基 / 立柱随地形起伏」)。地表太低(接近世界底)则跳过该槽。
y-min / y-max0 / 0垂直随机重定位:在最终 Y 上再随机加 [y-min, y-max] 格。都为 0 = 不抖动;如 y-min:-2 y-max:3 做高低错落(对标其它结构插件的 VerticalRepositioning)。
options候选模板列表,{ template, weight }。按 weight 加权轮盘抽一个贴上。

抽取与放置:每个槽位按 optionsweight 之和做轮盘抽一个候选;surface:true 时落点取该 x/z 真实地表 Y、否则取 原点 + (x,y,z);再叠加 [y-min,y-max] 随机偏移;最后以 NONE 朝向粘贴。weight 之和 ≤0 的槽位跳过。

1.3 候选模板的要求

  • 候选 template 必须存在(如 templates/spire_a/structure.nbt)。
  • 建议候选模板自身 generation.enabled: false只当变体用,别让它自己也参与自然生成。
  • 变体应落在主结构范围内——超出主结构包围盒的部分,/qr remove / 消退快照还原还原不到(快照只覆盖主结构体积)。

1.4 递归嵌套(主堡 → 角楼 → 旗)

变体子模板自己的 blueprint.variants 会被递归处理:主堡贴角楼、角楼再贴旗帜……

text
ancient_keep (主堡)
 └─ variants 槽 → corner_tower (角楼)         ← 第 1 层
      └─ corner_tower 的 variants 槽 → banner  ← 第 2 层

防爆栈 / 防卡服双保险:

限制行为
最大嵌套深度3 层超过 3 层的更深变体不再展开。
单次生成变体总数256 个一座遗迹一次生成最多贴 256 个变体,超出截断 + 控制台告警(互相引用导致膨胀时保命)。

注意

两个模板互相在变体里引用对方,会被深度 3 / 总数 256 的上限截断并告警——不会无限膨胀冻服,但这是配置错误,应避免环引用。

🖼️ [图片占位] 一座主堡 + 四角随机角楼 + 角楼顶随机旗帜的递归变体效果图 · 建议 assets/variants-nested.png


二、调色板拼接(Tile Palette)

2.1 是什么

调色板是把若干小结构(瓦片 Tile)按连接器规则自动拼成一座大遗迹的程序化生成器。每块瓦片是一个普通模板(有 .nbt),瓦片之间通过**连接器(connector)**对接——从起始瓦片开始,沿开放的连接器不断挂上能对上口的瓦片,直到房间数达上限,最后用「封口瓦片」堵住剩下的开口。

适合做地牢 / 矿洞 / 走廊网络这类「房间 + 走廊拼出来、每次布局不同」的遗迹。

说明

调色板放在 plugins/QinhRuins/palettes/ 下,一个 .yml = 一份调色板,文件名(去扩展名)= 调色板 ID。瓦片引用的模板须先存在于 templates/

2.2 调色板字段

yaml
# palettes/dungeon.yml —— 文件名 dungeon 即调色板 ID
max-rooms: 12          # 最多拼几块瓦片(≥1)
start: hub_room        # 起始瓦片的模板 ID(留空=从 tiles 里按 weight 随机起手)
tiles:
  - template: hub_room          # 瓦片对应的模板 ID(templates/hub_room/)
    weight: 10                  # 加权随机权重(默认 1)
    max-count: 1                # 这种瓦片最多用几次(默认 99)
    role: room                  # 角色:room(默认) / cap(封口瓦片)
    connectors:                 # 连接器:朝向 + 相对该瓦片原点的对接口坐标
      - { side: NORTH, x: 3, y: 0, z: 0 }
      - { side: SOUTH, x: 3, y: 0, z: 6 }
      - { side: EAST,  x: 6, y: 0, z: 3 }
      - { side: WEST,  x: 0, y: 0, z: 3 }
  - template: corridor
    weight: 20
    connectors:
      - { side: WEST, x: 0, y: 0, z: 1 }
      - { side: EAST, x: 3, y: 0, z: 1 }
  - template: dead_end          # 封口瓦片:单连接器,role=cap
    role: cap
    connectors:
      - { side: WEST, x: 0, y: 0, z: 1 }
字段默认说明
max-rooms12整座遗迹最多拼几块瓦片(≥1)。
start起始瓦片的模板 ID;留空 = 从 tiles 里按 weight 随机起手。
tiles[].template瓦片对应的模板 ID(须存在,否则该瓦片跳过 + 告警)。
tiles[].weight1加权随机权重。
tiles[].max-count99这种瓦片最多用几次。
tiles[].roleroomroom = 普通房间;cap = 封口瓦片(单连接器,用来堵住拼接结束后剩下的开口)。
tiles[].connectors连接器列表:{ side, x, y, z }sideNORTH/SOUTH/EAST/WESTx/y/z 是对接口相对该瓦片原点的坐标。
tiles[].repetition-penalty0邻接重复惩罚:当该瓦片旁边已有同类瓦片时,按相邻数量降低它被抽中的权重(越大越不爱扎堆)。避免「一长串相同房间」。
tiles[].no-repeatfalsetrue 则该瓦片禁止与同类相邻(连接 / 封口都遵守),比惩罚更硬。

瓦片尺寸自动读取:每块瓦片的占地尺寸由 QR 直接读对应模板的 .nbt 结构尺寸算出,无需在调色板里手填——所以瓦片模板的 .nbt 必须能正常加载。

2.3 拼接算法(怎么长出来)

1. 起手     用 start 指定的瓦片,没指定就按 weight 随机抽一块,放在原点(0,0,0)
2. 开口入队  把起始瓦片所有连接器登记为「开放开口」
3. 反复对接  从开放开口里随机取一个 → 找一块「有反向连接器、未达 max-count、不与已放瓦片重叠」的瓦片对上去
            → 新瓦片其余连接器继续入队,直到瓦片数达 max-rooms 或没有可对接的开口
4. 封口     剩下的开放开口,用 role=cap 的单连接器瓦片逐个堵上(对得上口且不重叠才堵)
  • 不重叠:拼接时做 外接包围盒检测,对接会造成与已放瓦片重叠的候选直接跳过。
  • 自动回溯:走入死路(某开口找不到能对上的瓦片)时,规划器会退一步换方案再继续,连通性更好、断头路更少。回溯有步数预算(随 max-rooms 自动伸缩),不会无限兜圈。无需配置。
  • 可复现:同一个调色板 + 同一个种子(seed)= 同一份布局(用同一随机种子驱动)。

🔑 调色板拼出的是「整座结构本体」。它和子结构变体不同:变体是「主结构 + 局部点缀」,调色板是「从零按连接规则拼出整体」。

2.4 命令:预览与生成

命令用途
/qr genstruct <调色板>在你脚下用随机种子拼接并实际放置,回报放了几块 / 计划几块 / 用的种子。
/qr genstruct <调色板> dry只算计划不放置:列出每块瓦片的偏移坐标与尺寸(看布局是否合理,不动世界)。
/qr genstruct <调色板> <种子>指定种子拼接放置(同种子复现同布局,便于调试 / 复刻)。

mode 参数三选一dry = 干跑预览;纯数字 = 当作种子用于复现;其余 / 留空 = 用系统随机种子。dry 模式用临时种子只算计划、不写方块。

🖼️ [图片占位] /qr genstruct dungeon dry 输出的瓦片计划清单 + 实际拼出的地牢俯视图 · 建议 assets/genstruct-dry.png


三、变体 vs 调色板:怎么选

子结构变体(variants)调色板拼接(Tile Palette)
配在哪遗迹的 blueprint.ymlvariantspalettes/<id>.yml 独立文件
定位主结构 + 局部随机点缀从零按连接规则拼整体
随机性每个槽位加权抽 1 个候选 + 垂直抖动整体布局随机(房间数 / 连接路径)
触发主结构自然生成时自动贴上目前由 /qr genstruct 手动生成
适合同主体不同房 / 塔尖 / 角楼、地形自适应点缀地牢 / 矿洞 / 走廊网络等拼装式大遗迹
上限保护嵌套深度 3 / 总数 256max-rooms / 各瓦片 max-count

提示

两者可叠用:调色板拼出的瓦片模板自己也能带 blueprint.variants,于是「拼出的每个房间内部再随机点缀」。


四、改完之后

  • blueprint.ymlvariants / palettes/*.yml/qr reload
  • 变体不生效:确认候选 template 存在、optionsweight 之和 > 0、槽位坐标在主结构范围内。
  • 调色板拼不起来:先 /qr genstruct <id> dry 看计划——瓦片数为 0 多半是连接器对不上口或瓦片模板 .nbt 读不出尺寸;引用了不存在的模板会在 /qr reload 时告警并跳过该瓦片。

下一步