オトギフロンティア立绘解包表情差分求助

我在用AS解包オトギフロンティア的homestand文件后,解包出的表情差分是几十×几十分辨率的小图片,但我看别人的解包文件里的表情差分是和原立绘分辨率一致且位置对齐的图片,有没有大佬分析一下如何实现?附上对比图和资源文件(



22551.zip (616.5 KB)

1 个赞

这边前几天也遇到了一样的问题,后来发现直接提取是没有关联坐标的,我这是用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包

好的,谢谢老哥 :smiley: