我在用AS解包オトギフロンティア的homestand文件后,解包出的表情差分是几十×几十分辨率的小图片,但我看别人的解包文件里的表情差分是和原立绘分辨率一致且位置对齐的图片,有没有大佬分析一下如何实现?附上对比图和资源文件(
22551.zip (616.5 KB)
我在用AS解包オトギフロンティア的homestand文件后,解包出的表情差分是几十×几十分辨率的小图片,但我看别人的解包文件里的表情差分是和原立绘分辨率一致且位置对齐的图片,有没有大佬分析一下如何实现?附上对比图和资源文件(
这边前几天也遇到了一样的问题,后来发现直接提取是没有关联坐标的,我这是用unitypy解出坐标然后贴到2000x2000画布解决的
import UnityPy
from PIL import Image
from pathlib import Path
def extract_sprites_with_canvas(file_path: str, output_dir: str):
env = UnityPy.load(file_path)
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
textures = {} # 存储所有 Texture2D
sprite_atlas = {} # 存储 SpriteAtlas
# **第一步:收集 Texture2D 和 SpriteAtlas**
for obj in env.objects:
if obj.byte_size < 600:
continue
if obj.type.name == "Texture2D":
texture = obj.read()
data_name = texture.m_Name
# 如果名称是纯数字,重命名为 "body"
if data_name.isdigit():
data_name = "body"
# 保存 Texture2D 图像
texture.image.save(output_dir / f"{data_name}.png")
textures[data_name] = texture.image
print(f"Collected Texture2D: {data_name}, size: {texture.image.size}")
elif obj.type.name == "SpriteAtlas":
atlas = obj.read()
sprite_atlas[atlas.m_Name] = atlas
print(f"Collected SpriteAtlas: {atlas.m_Name}")
# **第二步:处理所有 Sprite**
for obj in env.objects:
if obj.byte_size < 600:
continue
if obj.type.name == "Sprite":
sprite = obj.read()
sprite_name = getattr(sprite, "m_Name", "unknown_sprite")
if sprite_name == "_stand1":
continue
if not hasattr(sprite, "image") or sprite.image is None:
print(f"⚠️ {sprite_name} 没有 image 数据,跳过")
continue
# **获取 Sprite 相关信息**
rect = sprite.m_Rect
atlas_ptr = sprite.m_SpriteAtlas
print(f"Sprite: {sprite_name}, atlas_ptr: {atlas_ptr}")
print(f"Rect (unreliable): x={rect.x}, y={rect.y}, w={rect.width}, h={rect.height}")
if atlas_ptr is None or atlas_ptr.m_PathID == 0:
print(f"⚠️ {sprite_name} 没有 SpriteAtlas,可能是独立的")
continue
# **从 Sprite 的 m_RD 获取位置**
if hasattr(sprite, "m_RD"):
render_data = sprite.m_RD
if hasattr(render_data, "textureRect"):
texture_rect = render_data.textureRect
x, y, w, h = map(int, [texture_rect.x, texture_rect.y, texture_rect.width, texture_rect.height])
print(f"Found textureRect from m_RD for {sprite_name}: x={x}, y={y}, w={w}, h={h}")
else:
print(f"⚠️ {sprite_name} 的 m_RD 没有 textureRect,使用默认值")
x, y, w, h = 0, 0, int(rect.width), int(rect.height)
# **获取 textureRectOffset**
if hasattr(render_data, "textureRectOffset"):
offset = render_data.textureRectOffset
offset_x, offset_y = map(int, [offset.x, offset.y])
print(f"Found textureRectOffset for {sprite_name}: x={offset_x}, y={offset_y}")
else:
print(f"⚠️ {sprite_name} 的 m_RD 没有 textureRectOffset,使用 textureRect 作为默认")
offset_x, offset_y = x, y
else:
print(f"⚠️ {sprite_name} 没有 m_RD,使用默认值")
x, y, w, h = 0, 0, int(rect.width), int(rect.height)
offset_x, offset_y = 0, 0
# **提取 Sprite 的裁剪图像**
cropped_sprite = sprite.image
print(f"Cropped Sprite size: {cropped_sprite.size}")
# **创建 2000x2000 的透明画布**
target_size = (2000, 2000)
full_canvas = Image.new("RGBA", target_size, (0, 0, 0, 0))
# **使用 textureRectOffset 作为目标坐标**
new_x = offset_x # 例如 864
new_y = offset_y # 例如 1090
# **翻转 Y 轴:Unity (左下原点) -> PIL (左上原点)**
sprite_width, sprite_height = cropped_sprite.size
print(f"Before Y flip: new_y = {new_y}")
new_y = target_size[1] - new_y - sprite_height # 2000 - y - height
print(f"After Y flip: new_y = {new_y}")
# **确保位置在画布范围内**
new_x = max(0, min(new_x, target_size[0] - sprite_width))
new_y = max(0, min(new_y, target_size[1] - sprite_height))
print(f"After range adjustment: new_y = {new_y}")
print(f"Adjusted New position: ({new_x}, {new_y})")
# **粘贴到画布**
try:
full_canvas.paste(cropped_sprite, (new_x, new_y))
except Exception as e:
print(f"⚠️ 粘贴失败: {e}")
# **保存结果**
full_canvas.save(output_dir / f"{sprite_name}.png")
print(f"✅ 保存完整画布: {sprite_name}.png")
# 示例调用
extract_sprites_with_canvas("22611", "new")
ai写的,能跑就行,可以自己优化下(
就是解包出Texture2D和spriteatlas然后运行脚本就行了是吧
直接运行脚本,用脚本解包homestand包
好的,谢谢老哥