Unity砖块图概述

其它半成品
浏览数 - 127发布于 - 2026-03-25 - 12:13

重新编辑于 - 2026-03-25 - 12:14

这类图混淆的不是像素值本身,而是像素块的空间排列关系。01_Atlas0.png 只是“砖块仓库”,真正决定原图长什么样的是旁边的元信息和运行时代码,不是 PNG 本身。

它和常见“图片加密”的区别是:

  • 不是改颜色、改通道、改压缩算法
  • 不是简单行列置乱后就结束
  • 它把原图切成小块,加入边缘 padding,再打包进 atlas
  • 最终显示时,靠一张索引表把这些块重新拼成完整 CG

这套格式的代码定义在 DicingTextures.cs 和 DicingTextureData.cs。

组成部分
一套完整资源通常由 4 部分组成:

  • atlas PNG
    比如 01_Atlas0.png。它存放被切碎后的图块。
  • 元信息 .asset
    比如 01.asset。这里面有块大小、padding、输出尺寸、块索引表。
  • 运行时重组代码
    游戏运行时按 C# 逻辑把 atlas 里的块重新拼成图。
  • thumbnail
    比如缩略图往往是正常图,用来预览,也能拿来做验证,但不是恢复所必需的。

关键字段
在 01.asset 里,你能看到这几个核心字段:

  • cellSize
    atlas 里每个物理格子的边长。这里是 64。
  • padding
    每个物理格子四周保留的边距。这里是 3。
  • atlasTextures
    这个资源会用到哪些 atlas 图。
  • textureDataList
    真正的每张 CG/差分定义列表。
  • name
    某个差分名,比如 01x10、01x11。
  • atlasName
    当前差分使用哪个 atlas。
  • width / height
    最终输出图尺寸。
  • cellIndexList
    最重要的字段。记录“输出第 N 块应该去 atlas 的哪一个块取图”。
  • transparentIndex
    某些块可直接视为透明并跳过。当前 01x10 是 -1,表示没用透明块索引。

它到底做了什么
这套格式的核心动作是:

  • 把原图切成逻辑块
  • 每个逻辑块不是直接裸存,而是放进 atlas 的物理格子里
  • 物理格子比逻辑块大,因为四周带 padding
  • 最终靠 cellIndexList 指挥每个逻辑块去哪个物理格子取内容

以你这份资源为例:

  • cellSize = 64
  • padding = 3
  • 所以真正有效图像内容块大小是 64 - 3*2 = 58
  • 01x10 的目标尺寸是 1920 x 1080
  • 所以逻辑网格是:
    ceil(1920 / 58) = 34 列
    ceil(1080 / 58) = 19 行
  • 总逻辑块数是 34 * 19 = 646
  • 这正好和脚本列出来的 cells=646 一致

也就是说,一张 01x10 不是一整张图直接存下来,而是被拆成 646 个逻辑块,再按照索引表重组。

为什么要有 padding
padding 不是多余垃圾,而是图形工程里的常见做法。作用主要有:

  • 防止双线性采样时串色
  • 防止缩放或 mipmap 时读到隔壁块
  • 保证拼起来时没有边缘接缝

所以恢复时必须:

  • 不能整块复制 64x64
  • 必须跳过四周各 3 像素
  • 真正取的是中间的 58x58
  • 边缘块还要按实际剩余宽高裁剪

如果你把 padding 也一起拷进去,图通常会有细边、接缝、重复边缘像素。

差分是怎么回事
你说“多个差分”,这类资源里的差分不是传统意义上的“底图 + 补丁层”。

它更像这样:

  • 01.asset 里有很多条 textureDataList
  • 比如 01x10、01x11、01x12……
  • 每一条都有自己完整的 cellIndexList
  • 每一条都能独立恢复成一张完整 CG
  • 它们共享同一个 atlas,所以看起来像差分存储

好处是:

  • 相同区域可以复用同一组物理块
  • 只改动表情、眼睛、嘴、手等局部时,不必重复存整张图
  • 存储体积会更小

所以这更接近“块级别复用”,而不是“图层叠加”。

坐标系为什么容易错
这是这类恢复里最容易踩坑的点。

代码在 DicingTextureData.cs 里直接把 atlas 坐标转成 Unity UV,没有做 1 - y 翻转。这说明它按 Unity 纹理坐标系解释,也就是:

  • 原点在左下
  • x 向右增大
  • y 向上增大

所以逻辑块的遍历顺序是:

  • 每行从左到右
  • 行从下往上

但 PNG 文件本身是按左上角坐标读写的,所以恢复程序里要做一次坐标换算。这个如果做错,会出现:

  • 图块上下倒序

  • 人物位置怪异

  • 和缩略图明显对不上

恢复公式
核心恢复逻辑可以概括成这几步:

javascript
effective = cellSize - 2 * padding
cols = ceil(width / effective)
rows = ceil(height / effective)
atlas_cols = ceil(atlas_width / cellSize)

对每个逻辑块 k:
  col = k % cols
  row = k // cols
  atlas_index = cellIndexList[k]

  src_cell_x = (atlas_index % atlas_cols) * cellSize + padding
  src_cell_y = (atlas_index // atlas_cols) * cellSize + padding

  copy_w = min(effective, width - col * effective)
  copy_h = min(effective, height - row * effective)

  从 atlas 取出 (src_cell_x, src_cell_y) 起始的有效区域
  粘贴到输出图对应的逻辑块位置

本文版权遵循 CC BY-NC 协议 本站版权政策

1 条回复

aionfatedio
发布于 2026-03-25 - 23:13 (编辑于 2026-03-25 - 23:20)

和我前段时间研究的撞了,其实这个有两种方法处理,楼主提供的是方法二,手动导出 JSON然后从MonoBehaviour JSON 中读取 UTAGE 配置重建。还有一种方法是通过 UnityPy 直接读取 Sprite 的顶点/UV 数据重建。我个人建议是用方法一,因为比较通用。方法二的话可能还需要手动用 AssetStudio 导出 MonoBehaviour JSON,而且只支持 UTAGE 这一种格式。这里给出一个随便vibe coding的小脚本,拖入game_data目录即可解包到指定目录,支持正常图片和碎块化非正常图片:https://github.com/Aionfatedio/AokanaUnpacker/blob/main/dicing_restorer.py
目前效率比较慢,可能不如assetstudio一根

(。>︿<。) 已经一滴回复都不剩了哦~