Vue3 播放 Spine 3.8 动画实战指南

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 的生命周期替换为对应框架的写法即可。


**参考资料**: