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

+84 -1
View File
@@ -1,5 +1,7 @@
import { defineStore } from 'pinia'
import { EmitterConfig, defaultConfig, ParticleState, type SceneCollider } from '../core/particleEmitter'
import type { SpineSceneObject } from '../spine/spineTypes'
import { releaseSpineAsset } from '../spine/spineAssetRegistry'
export interface ParticleSystem {
id: number
@@ -22,6 +24,8 @@ export interface ScenePath {
x: number
y: number
rotation: number
scaleX: number
scaleY: number
closed: boolean
color: string
preset: '' | 'circle' | 'square' | 'polygon' | 'star'
@@ -48,6 +52,8 @@ export interface SettingsState {
showAxisLabels: boolean
/** 坐标轴数值颜色(#rrggbb) */
axisColor: string
/** 是否在画布中显示已加载的 Spine 动画资源 */
showSpineResources: boolean
}
export interface TimelineState {
@@ -70,6 +76,7 @@ export interface TimelineState {
let seq = 1
let colliderSeq = 1
let pathSeq = 1
let spineSeq = 1
export const useParticleStore = defineStore('particle', {
state: () => ({
@@ -77,12 +84,13 @@ export const useParticleStore = defineStore('particle', {
systems: [] as ParticleSystem[],
colliders: [] as CollisionBody[],
paths: [] as ScenePath[],
spines: [] as SpineSceneObject[],
activeId: 0,
activeObjectType: 'particle' as SceneObjectType,
activeObjectId: 0,
// 与 Spine 编辑器默认时间基准对齐:1 秒 = 30 帧。
timeline: { playing: true, loop: true, frame: 0, totalFrames: 60, fps: 30, recorded: false, animation: 'animation' } as TimelineState,
settings: { tickEnabled: false, axisFontSize: 12, tickWidth: 1, showAxisLabels: true, axisColor: '#7a86ad' } as SettingsState,
settings: { tickEnabled: false, axisFontSize: 12, tickWidth: 1, showAxisLabels: true, axisColor: '#7a86ad', showSpineResources: true } as SettingsState,
}),
getters: {
activeSystemCount: (s) => s.systems.length,
@@ -136,6 +144,8 @@ export const useParticleStore = defineStore('particle', {
x: 0,
y: 0,
rotation: 0,
scaleX: 1,
scaleY: 1,
radius: 50,
radiusX: 70,
radiusY: 45,
@@ -161,6 +171,8 @@ export const useParticleStore = defineStore('particle', {
x: 0,
y: 0,
rotation: 0,
scaleX: 1,
scaleY: 1,
closed: false,
color: '#ff933e',
preset: '',
@@ -171,6 +183,33 @@ export const useParticleStore = defineStore('particle', {
this.activeObjectId = path.id
return path
},
addSpine() {
const id = spineSeq++
const spine: SpineSceneObject = {
id,
name: `SpineSkeleton${id}`,
enabled: true,
x: 0,
y: 0,
rotation: 0,
scaleX: 1,
scaleY: 1,
timeOffset: 0,
skeletonFileName: '',
atlasFileName: '',
textureFileNames: [],
selectedAnimation: '',
animations: [],
bones: [],
assetVersion: 0,
status: 'empty',
error: '',
}
this.spines.push(spine)
this.activeObjectType = 'spine'
this.activeObjectId = spine.id
return spine
},
selectSceneObject(type: SceneObjectType, id: number) {
this.activeObjectType = type
this.activeObjectId = id
@@ -194,10 +233,48 @@ export const useParticleStore = defineStore('particle', {
this.activeObjectId = fallback?.id ?? this.paths[0]?.id ?? 0
}
},
removeSpine(id: number) {
releaseSpineAsset(id)
const index = this.spines.findIndex((item) => item.id === id)
if (index >= 0) this.spines.splice(index, 1)
if (this.activeObjectType === 'spine' && this.activeObjectId === id) {
const particle = this.systems[0]
const collider = this.colliders[0]
const path = this.paths[0]
const spine = this.spines[0]
if (particle) {
this.activeObjectType = 'particle'
this.activeObjectId = particle.id
this.activeId = particle.id
} else if (collider) {
this.activeObjectType = 'collision'
this.activeObjectId = collider.id
} else if (path) {
this.activeObjectType = 'path'
this.activeObjectId = path.id
} else {
this.activeObjectType = 'spine'
this.activeObjectId = spine?.id ?? 0
}
}
this.recalcTotalFrames()
},
play() { this.timeline.playing = true },
pause() { this.timeline.playing = false },
togglePlay() { this.timeline.playing = !this.timeline.playing },
toStart() { this.timeline.frame = 0 },
previousFrame() {
this.timeline.playing = false
this.timeline.frame = Math.max(0, this.timeline.frame - 1)
},
nextFrame() {
this.timeline.playing = false
this.timeline.frame = Math.min(Math.max(0, this.timeline.totalFrames - 1), this.timeline.frame + 1)
},
toEnd() {
this.timeline.playing = false
this.timeline.frame = Math.max(0, this.timeline.totalFrames - 1)
},
toggleLoop() { this.timeline.loop = !this.timeline.loop },
setFrame(f: number) {
this.timeline.frame = Math.max(0, Math.min(f, this.timeline.totalFrames - 1))
@@ -220,6 +297,12 @@ export const useParticleStore = defineStore('particle', {
const end = Math.max(0, Number(c.delay) || 0) + emissionDuration + particleLife
if (end > maxSec) maxSec = end
}
for (const spine of this.spines) {
const animation = spine.animations.find((item) => item.name === spine.selectedAnimation)
if (!animation) continue
const end = Math.max(0, Number(spine.timeOffset) || 0) + Math.max(0, animation.duration || 0)
if (end > maxSec) maxSec = end
}
const frames = Math.max(1, Math.ceil(maxSec * fps))
this.timeline.totalFrames = frames
// clamp 当前帧