spine资源引入

This commit is contained in:
tianmo committed 2026-08-29 03:10:16 +08:00
1 parent 166b5015c2
commit 5f4667f736
25 files changed
+31151 -9082

No files matched your search

+83
View File
@@ -0,0 +1,83 @@
import { Container } from 'pixi.js'
import { Spine } from '@esotericsoftware/spine-pixi-v7'
import { getSpineAsset } from './spineAssetRegistry'
import type { SpineSceneObject } from './spineTypes'
interface RuntimeEntry {
root: Container
spine: Spine
assetVersion: number
animation: string
}
export class SpineRuntimeLayer {
readonly container = new Container()
private entries = new Map<number, RuntimeEntry>()
sync(objects: SpineSceneObject[], frame: number, fps: number, showResources = true) {
for (const [id, entry] of this.entries) {
if (objects.some((object) => object.id === id)) continue
this.container.removeChild(entry.root)
entry.root.destroy({ children: true })
this.entries.delete(id)
}
for (const object of objects) {
const asset = getSpineAsset(object.id)
let entry = this.entries.get(object.id)
if (entry && entry.assetVersion !== object.assetVersion && !asset) {
this.container.removeChild(entry.root)
entry.root.destroy({ children: true })
this.entries.delete(object.id)
entry = undefined
}
if ((!entry || entry.assetVersion !== object.assetVersion) && asset) {
if (entry) {
this.container.removeChild(entry.root)
entry.root.destroy({ children: true })
}
const root = new Container()
const spine = new Spine({ skeletonData: asset.skeletonData, autoUpdate: false, darkTint: false })
root.addChild(spine)
this.container.addChild(root)
entry = { root, spine, assetVersion: object.assetVersion, animation: '' }
this.entries.set(object.id, entry)
}
if (!entry) continue
const root = entry.root
const spine = entry.spine
const visible = showResources && object.enabled
root.visible = visible
root.renderable = visible
root.position.set(object.x, -object.y)
root.rotation = -object.rotation * Math.PI / 180
root.scale.set(object.scaleX, object.scaleY)
if (entry.animation !== object.selectedAnimation) {
entry.animation = object.selectedAnimation
spine.skeleton.setToSetupPose()
spine.state.clearTracks()
// 编辑器时间轴负责整体循环;单个 Spine 动画自身只播放一次并停在末帧。
if (entry.animation) spine.state.setAnimation(0, entry.animation, false)
}
const track = spine.state.getCurrent(0)
if (track) {
const duration = object.animations.find((animation) => animation.name === object.selectedAnimation)?.duration || 0
const localTime = Math.max(0, frame / Math.max(1, fps) - object.timeOffset)
track.trackTime = duration > 0 ? Math.min(localTime, duration) : localTime
}
spine.update(0)
}
}
destroy() {
for (const entry of this.entries.values()) {
entry.root.removeFromParent()
entry.root.destroy({ children: true })
}
this.entries.clear()
this.container.removeFromParent()
this.container.destroy({ children: true })
}
}