阶段8落盘
This commit is contained in:
1 parent
b5ac9552a1
commit
a2de23a789
10 files changed
+419
-20
No files matched your search
@@ -1,7 +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'
|
||||
import { releaseSpineAsset, retainSpineAsset } from '../spine/spineAssetRegistry'
|
||||
|
||||
export interface ParticleSystem {
|
||||
id: number
|
||||
@@ -88,6 +88,29 @@ let colliderSeq = 1
|
||||
let pathSeq = 1
|
||||
let spineSeq = 1
|
||||
|
||||
/** 深拷贝编辑器配置,但保留 Texture 等运行时类实例的共享引用。 */
|
||||
function cloneEditorValue<T>(value: T): T {
|
||||
if (Array.isArray(value)) return value.map((item) => cloneEditorValue(item)) as T
|
||||
if (value && typeof value === 'object') {
|
||||
const prototype = Object.getPrototypeOf(value)
|
||||
if (prototype === Object.prototype || prototype === null) {
|
||||
const copy: Record<string, unknown> = {}
|
||||
for (const [key, item] of Object.entries(value)) copy[key] = cloneEditorValue(item)
|
||||
return copy as T
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function copyName(name: string, names: string[]) {
|
||||
const existing = new Set(names)
|
||||
const base = `${name} Copy`
|
||||
if (!existing.has(base)) return base
|
||||
let index = 2
|
||||
while (existing.has(`${base} ${index}`)) index++
|
||||
return `${base} ${index}`
|
||||
}
|
||||
|
||||
export const useParticleStore = defineStore('particle', {
|
||||
state: () => ({
|
||||
editor: { viewScale: 1.0, viewX: 0, viewY: 0 } as EditorState,
|
||||
@@ -273,6 +296,59 @@ export const useParticleStore = defineStore('particle', {
|
||||
this.activeObjectId = spine.id
|
||||
return spine
|
||||
},
|
||||
duplicateSelectedObject() {
|
||||
const id = this.activeObjectId
|
||||
if (!id) return null
|
||||
|
||||
if (this.activeObjectType === 'particle') {
|
||||
const source = this.systems.find((item) => item.id === id)
|
||||
if (!source) return null
|
||||
const copy = cloneEditorValue(source)
|
||||
copy.id = seq++
|
||||
copy.config.name = copyName(source.config.name, this.systems.map((item) => item.config.name))
|
||||
// 录制帧属于运行时缓存,副本由相同配置重新独立生成。
|
||||
copy.frames = undefined
|
||||
this.systems.push(copy)
|
||||
this.activeId = copy.id
|
||||
this.activeObjectId = copy.id
|
||||
this.timeline.animation = copy.animation
|
||||
return copy
|
||||
}
|
||||
|
||||
if (this.activeObjectType === 'collision') {
|
||||
const source = this.colliders.find((item) => item.id === id)
|
||||
if (!source) return null
|
||||
const copy = cloneEditorValue(source)
|
||||
copy.id = colliderSeq++
|
||||
copy.name = copyName(source.name, this.colliders.map((item) => item.name))
|
||||
this.colliders.push(copy)
|
||||
this.activeObjectId = copy.id
|
||||
return copy
|
||||
}
|
||||
|
||||
if (this.activeObjectType === 'path') {
|
||||
const source = this.paths.find((item) => item.id === id)
|
||||
if (!source) return null
|
||||
const copy = cloneEditorValue(source)
|
||||
copy.id = pathSeq++
|
||||
copy.name = copyName(source.name, this.paths.map((item) => item.name))
|
||||
this.paths.push(copy)
|
||||
this.activeObjectId = copy.id
|
||||
return copy
|
||||
}
|
||||
|
||||
const source = this.spines.find((item) => item.id === id)
|
||||
if (!source) return null
|
||||
const copy = cloneEditorValue(source)
|
||||
copy.id = spineSeq++
|
||||
copy.name = copyName(source.name, this.spines.map((item) => item.name))
|
||||
// 骨骼选择是层级树的临时操作状态,不在两个对象之间复用。
|
||||
copy.selectedBone = ''
|
||||
retainSpineAsset(source.id, copy.id)
|
||||
this.spines.push(copy)
|
||||
this.activeObjectId = copy.id
|
||||
return copy
|
||||
},
|
||||
selectSceneObject(type: SceneObjectType, id: number) {
|
||||
this.activeObjectType = type
|
||||
this.activeObjectId = id
|
||||
@@ -326,9 +402,16 @@ export const useParticleStore = defineStore('particle', {
|
||||
}
|
||||
this.recalcTotalFrames()
|
||||
},
|
||||
play() { this.timeline.playing = true },
|
||||
play() {
|
||||
// 非循环播放停在末帧后,再次播放应从头开始;中途暂停则继续当前位置。
|
||||
if (this.timeline.frame >= Math.max(0, this.timeline.totalFrames - 1)) this.timeline.frame = 0
|
||||
this.timeline.playing = true
|
||||
},
|
||||
pause() { this.timeline.playing = false },
|
||||
togglePlay() { this.timeline.playing = !this.timeline.playing },
|
||||
togglePlay() {
|
||||
if (this.timeline.playing) this.pause()
|
||||
else this.play()
|
||||
},
|
||||
toStart() { this.timeline.frame = 0 },
|
||||
previousFrame() {
|
||||
this.timeline.playing = false
|
||||
|
||||
Reference in new issue
Block a user