Vue3 播放 Spine 3.8 动画实战指南
简单说 :
-
pixi.js = 画布,负责"画出来"
-
@pixi-spine = 解析器,负责"理解动画数据并画对"
没有 pixi.js,动画无法渲染;没有 pixi-spine,无法播放 Spine 专属的骨骼动画格式。
前言
最近在开发一个游戏项目,需要在 Vue3 中播放 Spine 动画。在搜索了大量资料后,发现网上的教程要么版本不对,要么写法有问题。经过反复尝试,终于成功跑通了,特此记录。
核心技术栈版本(重要)
```
Vue 3.x
Spine 动画版本:3.8.75
pixi-spine:@pixi-spine/all-3.8@4.0.6
pixi.js:7.4.3
Vite:5.x
TypeScript:5.x
```
**版本匹配原则**:Spine 编辑器导出的版本必须与运行时版本匹配。如果使用 Spine 3.8.75 导出的动画,必须使用 3.8 版本的 pixi-spine 运行时。
项目初始化
```bash
npm create vite@latest spine-game -- --template vue-ts
cd spine-game
npm install
npm install pixi.js@7.4.3 @pixi-spine/all-3.8@4.0.6
```
核心代码实现
SpineWebPlayer.vue 组件
```vue
<template>
<div
class="spine-container"
:style="containerStyle"
ref="containerRef"
></div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch, computed } from 'vue'
import * as PIXI from 'pixi.js'
import { Spine } from '@pixi-spine/all-3.8'
// Props 定义
const props = defineProps<{
position: { x: number; y: number }
direction: string
isMoving: boolean
}>()
const containerRef = ref<HTMLDivElement | null>(null)
let app: PIXI.Application | null = null
let spineAnimation: Spine | null = null
let animations: string\[\] = \[\]
const containerStyle = computed(() => ({
position: 'fixed' as const,
left: `${props.position.x}px`,
top: `${props.position.y}px`,
pointerEvents: 'none' as const
}))
// 加载 Spine 动画
const loadSpine = async () => {
if (!containerRef.value) return
// 创建 PIXI 应用
app = new PIXI.Application({
width: 200,
height: 250,
backgroundAlpha: 0, // 透明背景
antialias: true
})
containerRef.value.appendChild(app.view as unknown as Node)
try {
// 关键:使用 PIXI.Assets.load 加载 JSON,pixi-spine 自动处理 atlas 和 png
const resources = await PIXI.Assets.load<any>('/spine/your-animation.json')
// 获取可用动画列表
if (resources.spineData?.animations) {
animations = resources.spineData.animations.map((a: any) => a.name)
console.log('Available animations:', animations)
}
// 创建 Spine 动画实例
spineAnimation = new Spine(resources.spineData)
// 设置位置和缩放
spineAnimation.x = 100
spineAnimation.y = 250
spineAnimation.scale.set(0.5)
// 添加到舞台
app.stage.addChild(spineAnimation)
// 播放待机动画
const idleAnim = animations.includes('idle') ? 'idle' : animations0
spineAnimation.state.setAnimation(0, idleAnim, true)
console.log('Spine loaded successfully!')
} catch (error) {
console.error('Failed to load Spine:', error)
}
}
// 监听移动状态,切换动画
watch(() => props.isMoving, (isMoving) => {
if (!spineAnimation) return
const animName = isMoving ? 'walk' : 'idle'
if (animations.includes(animName)) {
spineAnimation.state.setAnimation(0, animName, true)
}
})
// 监听方向,改变翻转
watch(() => props.direction, (direction) => {
if (!spineAnimation) return
spineAnimation.scale.x = direction.includes('left') ? -0.5 : 0.5
})
onMounted(() => {
loadSpine()
})
onUnmounted(() => {
spineAnimation?.destroy()
app?.destroy()
})
</script>
<style scoped>
.spine-container {
width: 200px;
height: 250px;
}
</style>
```
资源文件放置
将 Spine 导出的文件放到 `public` 目录下:
```
public/
└── 冰晶魔法少女/
├── m_searcy.json # 骨骼数据
├── m_searcy.atlas # 纹理图集配置
├── m_searcy.png # 纹理图集1
└── m_searcy2.png # 纹理图集2(如有)
```
关键要点总结
1. 版本匹配是核心
| Spine 编辑器版本 | pixi-spine 版本 | pixi.js 版本 |
|-----------------|-----------------|--------------|
| 3.8.x | @pixi-spine/all-3.8@4.0.6 | 7.x |
| 4.x | @pixi-spine/all-4.x | 7.x |
2. 正确加载方式
❌ **错误方式**:
```typescript
// 手动解析 atlas 和 json
const atlas = new TextureAtlas(atlasText, textureLoader)
const skeletonJson = new SkeletonJson(attachmentLoader)
```
✅ **正确方式**:
```typescript
// 使用 PIXI.Assets.load 一键加载
const resources = await PIXI.Assets.load<any>('/path/to/animation.json')
const spine = new Spine(resources.spineData)
```
3. 动画切换
```typescript
// 切换到行走动画(循环)
spine.state.setAnimation(0, 'walk', true)
// 添加动画到队列
spine.state.addAnimation(0, 'attack', false, 0)
// 清空所有动画
spine.state.setEmptyAnimation(0, 0)
```
4. 方向控制
```typescript
// 向左翻转
spine.scale.x = -Math.abs(spine.scale.x)
// 向右
spine.scale.x = Math.abs(spine.scale.x)
```
常见问题
Q1: 动画显示为空白或报错 "Unsupported skeleton data"
**原因**:Spine 版本与运行时版本不匹配
**解决**:确保使用 Spine 3.8.75 导出,或安装对应的 4.x 版本 pixi-spine
Q2: 贴图显示不全
**原因**:atlas 引用的 png 文件未完整加载
**解决**:确保所有 .png 文件都在同一目录,且文件名与 atlas 中一致
Q3: 动画切换时卡顿
**解决**:使用混合时间过渡
```typescript
spine.state.setAnimation(0, 'run', true)
spine.state.addAnimation(0, 'walk', true, 0.3) // 0.3秒过渡
```
完整项目结构
```
spine-game/
├── public/
│ └── 冰晶魔法少女/
│ ├── m_searcy.json
│ ├── m_searcy.atlas
│ └── *.png
├── src/
│ ├── components/
│ │ ├── SpineWebPlayer.vue
│ │ ├── GameScene.vue
│ │ └── VirtualJoystick.vue
│ ├── composables/
│ │ └── useGameControls.ts
│ ├── App.vue
│ └── main.ts
├── package.json
└── vite.config.ts
```
结语
Vue3 播放 Spine 动画的关键在于:**版本匹配 + 正确的加载方式**。建议使用官方推荐的 `PIXI.Assets.load()` 方法,它会自动处理 JSON、atlas 和 PNG 的关联,比手动解析更稳定可靠。
如果你的项目使用其他框架(如 React),核心逻辑是相同的,只需将 Vue 的生命周期替换为对应框架的写法即可。
**参考资料**:
在 Web 中使用 Spine 动画(https://juejin.cn/post/7402229249677508658)
pixi-spine GitHub(https://github.com/pixijs-userland/spine)
Spine Runtimes(https://zh.esotericsoftware.com/spine-runtimes)