给你的博客部署一个Spine看板娘「可露希尔」

公開日: 2026-08-03 13:58 1990文字 10 min read

この投稿は「日本語」では表示できません。元の投稿を表示しています。
在Astro博客中通过Ark-Unpacker解包模型源文件,再通过Ark-waifu引擎在网页端加载Spine模型

前言

博客首页如果只有文章列表的话总显得有些冷清,所以我将奸商罗德岛超级总工程师——可露希尔请了过来()
本文记录从游戏客户端资源中提取她的 Spine 骨骼动画、最终以看板娘形式挂在博客首页的完整过程
环境:

  • Windows10 22H2
  • Node.js v22.23.1

概念准备

Spine

Spine 是 Esoteric Software 出品的2D骨骼动画系统:先用皮肤/骨骼/关键帧搭建动画,再导出为运行时文件。它产出的是一组配套文件,业内叫 Spine 三件套

  • .skel
    骨架数据:骨骼层级、插槽、关键帧动画(二进制或 JSON)
  • .png
    纹理图集:所有子图拼合后的实际像素
  • .atlas
    图集描述:子图在纹理中的布局与 UV 坐标

三者缺一不可,渲染时由 Spine 运行时解析.skel的动作、按.atlas的坐标从.png上裁出对应部位进行摆位

AssetBundle 与 LZHAM

AssetBundle(AB) 是Unity的资源打包格式,把纹理、网格动画序列化进单一文件,游戏运行时按需加载
明日方舟新版AB使用 LZHAM 无损压缩算法,导致AssetStudio 这类通用 Unity 解包工具无法解码——这是很多人第一步就卡住的原因。要解它必须用针对性的专用工具——Ark-Unpacker

通道分离

明日方舟的Spine纹理把RGB与Alpha拆成两张独立图片存储。直接拼接会得到一张发灰发黑的贴图,直接应用会导致模型出现类似花屏的效果。必须把Alpha通道图与RGB通道图合并还原为 RGBA 单图,Spine才能正确渲染

ark-waifu 与 pnpm patch

有了三件套,我们还需要一个“引擎”在网页加载模型,我们选择Arkwaifu
它是一个 npm 看板娘挂载库,基于 PixiJS + Spine Runtime,负责加载三件套、驱动待机/触控动画与气泡台词
pnpm patch 是 pnpm 的依赖补丁机制:它把某个依赖的源码复制出来让你直接编辑,再用 pnpm patch-commit 把改动固化成 patches/*.patch 文件,之后每次安装都会自动重放。除此之外还有使用CDN来加载组件的方式,但是受限于网络,我就直接使用patch来改动源码了

源文件提取

获取资源

直接从官网下载apk,7z解包,在如下路径找到.ab

arknights-hg-2761\assets\AB\Android\prefabs\shop\shopkeeper\live_007_closre.ab

解包

Ark-Unpacker下载最新release,是一个exe,把它跟.ab放到同一个目录下,双击使用

Unpacker界面
Unpacker界面

我们选择2,写入.ab路径,输出目录留空自动生成,类型全选itamj

准备开始
准备开始

完成后进入生成的目录,alpha通道的png即在根目录,json后续不用到,其余的文件在更内部的子目录,包括RGBonly的png和.skel以及.atlas

完成后目录结构
完成后目录结构

还没完,png需要通道合并,注意需要把所有文件放在同一目录下,Unpacker才能合并

所有文件
所有文件

然后在Unpacker中选择3,就能在工作目录根目录找到Combined_xxx文件夹,完整合并的png在内,最后把三件套拿出来,就算是完成啦

三件套
三件套

资源部署

将三件套与配置文件放入博客静态目录:

public/models/closure/
├── live_007_closre.skel
├── live_007_closre.atlas
├── live_007_closre.png
├── manifest.json
└── tips.json

其中manifest.jsontips.json这两个配置文件需要自己写入,arkwaifu的README中有示例,可以照着改

manifest.json

声明模型文件名、动画动作映射与渲染参数:

{
  "id": "closure-shop",
  "name": "可露希尔",
  "type": "ark-spine",
  "version": "spine-3.8",
  "files": {        
    "skel": "live_007_closre.skel",
    "atlas": "live_007_closre.atlas",
    "textures": ["live_007_closre.png"]
  },
  "actions": {   
    "idle": "Idle",
    "touch": "Introduce"
  },
  "scale": 1.2,   
  "position": { "x": 0.5, "y": 1.2 }
}
  • files指向三件套
  • actions把库的抽象动作映射到.skel中的真实动画名
  • scale负责调整角色在画布中的比例与站位

tips.json

状态台词,可以同时包含模型状态台词和网页DOM事件规则,不写入模型manifest,这里贴出示例,博士们自由发挥

{
  "version": 1,
  "lines": {
    "load": ["博士,我准备好了。"],
    "relax": ["先在这里休息一下。"],
    "sit": ["这个位置看起来不错。"],
    "stand": ["休息结束,继续工作吧。"],
    "click": ["嗯?博士有什么安排吗?"],
    "error": ["资源好像没有加载成功。"]
  },
  "rules": [
    {
      "event": "load",
      "text": ["欢迎来到这个博客页面。"]
    },
    {
      "selector": "a",
      "event": "mouseenter",
      "text": ["要打开这个链接吗?"],
      "action": "touch"
    },
    {
      "selector": ".ark-waifu-sit-target",
      "event": "settle",
      "text": ["这里可以坐一会儿。"],
      "action": "sit",
      "delayMs": 3000,
      "cooldownMs": 6000
    }
  ]
}

安装依赖与补丁

还没有将arkwaifu安装到博客中呢

pnpm add ark-waifu
pnpm patch ark-waifu

pnpm patch会返回一个编辑目录,比如node_modules/.pnpm_patches/ark-waifu@1.0.1,改完源码后执行

pnpm patch-commit node_modules/.pnpm_patches/ark-waifu@1.0.1

补丁一共有两处修改:

  1. 动画互斥,网页内能触发动作的地方太多了,导致鼠标一移动就开始触发各种事件,但是上一个动作还没做完下一个就来了,导致人物开始抽搐(),为此我引入状态守卫函数,同一时刻只允许一个临时动画,播放完才恢复待机:

    playTemporary(t) {
      if (this.currentTemporaryAction === t) return;   // 正在播同一个动作,直接丢弃
      this.context.hasAction(t) && (this.currentTemporaryAction = t,
        this.context.play(t, { loop: !1, onComplete: () => {
          this.currentTemporaryAction = null, this.sitting || this.playBaseAction();
        }}));
    }
  2. 关闭按钮,在根节点创建ark-waifu-close按钮,点击后销毁实例并派发ark-waifu:close自定义事件,交给上层组件处理:

    this.closeButton = document.createElement("div");
    this.closeButton.className = "ark-waifu-close";
    this.closeButton.textContent = "\u00d7";
    this.closeButton.addEventListener("click", () => {
      this.destroy();
      this.root.dispatchEvent(new CustomEvent("ark-waifu:close", { bubbles: true }));
    });

事件状态不存储,所以刷新又会回来的

组件挂载

src/components/ark-waifu/ArkWaifu.tsx

import { useEffect, useRef, useState } from 'react';
import { mountArkWaifu } from 'ark-waifu';
import type { MountedArkWaifu } from 'ark-waifu';

export function ArkWaifu() {
  const mountedRef = useRef<MountedArkWaifu | null>(null);
  const [visible, setVisible] = useState(true);

  useEffect(() => {
    if (!visible) return;
    let cancelled = false;

    const init = async () => {
      try {
        const mounted = mountArkWaifu({
          manifestUrl: '/models/closure/manifest.json',
          tipsUrl: '/models/closure/tips.json',
          draggable: false,          // 禁止拖拽
          clickAction: 'touch',      // 点击触发 Introduce 动画
          defaultAction: 'idle',
          width: 400,
          height: 480,
          zIndex: 9999,
          maxDpr: 1.5,               // 限制高 DPI 渲染开销
          fpsLimit: 30,              // 帧率上限,控 CPU
          pauseWhenHidden: true,     // 页面隐藏时暂停渲染
          pauseWhenOffscreen: true,  // 滚动出屏时暂停渲染
        });
        mountedRef.current = mounted;
        await mounted.ready;
        if (cancelled) mounted.widget.destroy();
      } catch (error) {
        console.error('Ark-Waifu mount failed', error);
      }
    };

    void init();

    const handleClose = () => setVisible(false);
    document.addEventListener('ark-waifu:close', handleClose);

    return () => {
      cancelled = true;
      document.removeEventListener('ark-waifu:close', handleClose);
      mountedRef.current?.widget.destroy();
      mountedRef.current = null;
    };
  }, [visible]);

  if (!visible) return null;
  return <div />;
}
  • mounted.ready等待资源加载完成,React卸载时destory()释放WebGL上下文
  • 监听补丁派发的ark-waifu:close事件,触发setVisible(false),不持久化

在首页挂载

src/pages/index.astro:

---
import { ArkWaifu } from '@/components/ark-waifu/ArkWaifu';
---
<Layout>
  ...
  <ArkWaifu client:only="react" />
</Layout>

client:only="react"声明该组件跳过服务端渲染,只在客户端由 React渲染,目的在于减小初始负载

样式定制

第一次想调气泡样式时我发现在tailwind.css里写了半天,页面上一点反应都没有,原来ark-waifu不是用css来控制样式的,而是用js的内联样式,优先级更高,那我只好!important了,在游戏内商店界面能看到的可露希尔的文字框,我尝试仿制了一下

.ark-waifu-theme .ark-waifu-bubble {
  right: 84px !important;
  bottom: 34% !important;
  width: 250px !important;
  height: 84px !important;
  border: 1px solid #f9fb67 !important;
  border-radius: 2px !important;
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  text-align: center !important;
  padding: 13px 14px !important;
  color: #f9fb67 !important;
  background: rgba(18, 20, 18, 0.78) !important;
  font-size: 13px !important;
  line-height: 1.5 !important;
}
.ark-waifu-theme .ark-waifu-bubble::before {
  content: "FROM";
  position: absolute;
  top: 0;
  left: 0;
  padding: 0 3px;
  color: #111 !important;
  background: #f9fb67 !important;
  font-family: Bahnschrift, "DIN Alternate", "DIN Condensed", "Archivo Narrow", "Oswald", "Helvetica Neue", Arial, sans-serif;
  font-size: 8px;
  font-weight: 700;
  line-height: 11px;
}

结语

至此,你的Spine看板娘就部署完成了~欢迎各位前来调戏()
游戏资源版权归鹰角网络所有,本文方法仅限个人学习与收藏,勿用于商用。

给你的博客部署一个Spine看板娘「可露希尔」
作者: Odonata
发布于: 2026年8月3日
许可协议: CC BY-NC-SA 4.0

気に入ったならばコメントを残してくださいね~

© 2026 Holo icon Odonata @Alights
Powered by theme astro-koharu · Inspired by Shoka