渲染顺序功能
This commit is contained in:
1 parent
aabf4ff693
commit
c041503004
22 files changed
+1904
-1291
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import { Container } from 'pixi.js'
|
||||
import { Container, Matrix } from 'pixi.js'
|
||||
import { getSpineAsset } from './spineAssetRegistry'
|
||||
import type { SpineSceneObject } from './spineTypes'
|
||||
|
||||
@@ -22,11 +22,79 @@ export class SpineRuntimeLayer {
|
||||
readonly container = new Container()
|
||||
private entries = new Map<number, RuntimeEntry>()
|
||||
|
||||
/** 供渲染层级使用:取某个场景 Spine 的运行时实例。 */
|
||||
getSpine(objectId: number) {
|
||||
return this.entries.get(objectId)?.spine || null
|
||||
}
|
||||
|
||||
/** 当前帧的绘制顺序(会跟随动画里的 drawOrder timeline)。 */
|
||||
getDrawOrder(objectId: number): string[] | null {
|
||||
const spine = this.entries.get(objectId)?.spine
|
||||
try {
|
||||
const drawOrder = spine?.skeleton?.drawOrder
|
||||
return Array.isArray(drawOrder) ? drawOrder.map((slot: any) => String(slot?.data?.name || '')) : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/** 插槽 wrapper 相对 Spine 的局部矩阵(与 SpinePixi updateSlotObject 保持一致)。 */
|
||||
getSlotLocalMatrix(objectId: number, slotName: string): Matrix | null {
|
||||
const spine = this.entries.get(objectId)?.spine
|
||||
const slot = spine?.skeleton?.findSlot?.(slotName)
|
||||
const bone = slot?.bone
|
||||
if (!bone?.active) return null
|
||||
return new Matrix(bone.a, bone.c, -bone.b, -bone.d, bone.worldX, bone.worldY)
|
||||
}
|
||||
|
||||
/**
|
||||
* 插槽 wrapper 在编辑器 world 空间中的父矩阵 = 场景对象 root 变换 × 插槽骨骼矩阵。
|
||||
* 粒子发射器挂进插槽后用它做逆变换补偿,保持原有的世界坐标表现。
|
||||
*/
|
||||
getSlotParentMatrix(
|
||||
objectId: number,
|
||||
slotName: string,
|
||||
root: { x: number; y: number; rotation: number; scaleX: number; scaleY: number },
|
||||
): Matrix | null {
|
||||
const local = this.getSlotLocalMatrix(objectId, slotName)
|
||||
if (!local) return null
|
||||
const radians = -root.rotation * Math.PI / 180
|
||||
const cos = Math.cos(radians), sin = Math.sin(radians)
|
||||
const rootMatrix = new Matrix(
|
||||
cos * root.scaleX, sin * root.scaleX,
|
||||
-sin * root.scaleY, cos * root.scaleY,
|
||||
root.x, -root.y,
|
||||
)
|
||||
return rootMatrix.append(local)
|
||||
}
|
||||
|
||||
attachSlotObject(objectId: number, slotName: string, container: Container, followAttachmentTimeline: boolean) {
|
||||
const spine = this.entries.get(objectId)?.spine
|
||||
if (!spine?.addSlotObject) return false
|
||||
try {
|
||||
spine.addSlotObject(slotName, container, { followAttachmentTimeline })
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
detachSlotObject(objectId: number, slotName: string, container: Container) {
|
||||
const spine = this.entries.get(objectId)?.spine
|
||||
try { spine?.removeSlotObject?.(slotName, container) } catch { /* 运行时已被释放时忽略 */ }
|
||||
}
|
||||
|
||||
private destroyEntry(entry: RuntimeEntry) {
|
||||
// 先摘掉渲染层级 wrapper,避免随 root 一起销毁编辑器里的粒子发射器。
|
||||
try { entry.spine?.removeSlotObjects?.() } catch { /* ignore */ }
|
||||
this.container.removeChild(entry.root)
|
||||
entry.root.destroy({ children: true })
|
||||
}
|
||||
|
||||
sync(objects: SpineSceneObject[], frame: number, fps: number) {
|
||||
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.destroyEntry(entry)
|
||||
this.entries.delete(id)
|
||||
}
|
||||
|
||||
@@ -34,16 +102,12 @@ export class SpineRuntimeLayer {
|
||||
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.destroyEntry(entry)
|
||||
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 })
|
||||
}
|
||||
if (entry) this.destroyEntry(entry)
|
||||
const root = new Container()
|
||||
const spine = new asset.SpineClass({ skeletonData: asset.skeletonData, autoUpdate: false, darkTint: false })
|
||||
if (asset.skeletonData.defaultSkin) spine.skeleton.setSkin(asset.skeletonData.defaultSkin)
|
||||
@@ -115,8 +179,7 @@ export class SpineRuntimeLayer {
|
||||
|
||||
destroy() {
|
||||
for (const entry of this.entries.values()) {
|
||||
entry.root.removeFromParent()
|
||||
entry.root.destroy({ children: true })
|
||||
this.destroyEntry(entry)
|
||||
}
|
||||
this.entries.clear()
|
||||
this.container.removeFromParent()
|
||||
|
||||
Reference in new issue
Block a user