阶段七落盘文档

This commit is contained in:
tianmo committed 2026-08-29 03:25:57 +08:00
1 parent 5f4667f736
commit 031359586d
4 files changed
+452 -9

No files matched your search

+28 -3
View File
@@ -68,7 +68,7 @@
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import { Application, Container, Graphics, Text, Texture } from 'pixi.js'
import { ParticleEmitter, ensureEmitterConfig, type ParticleState } from '../core/particleEmitter'
import { useParticleStore, type ScenePath } from '../store/particleStore'
import { useParticleStore, type CollisionBody, type ScenePath } from '../store/particleStore'
import { SpineRuntimeLayer } from '../spine/SpineRuntimeLayer'
const store = useParticleStore()
@@ -108,6 +108,7 @@ let dotTex: Texture<any> | null = null
let trailTex: Texture<any> | null = null
const emitterMap = new Map<number, ParticleEmitter>()
let previewColliders: CollisionBody[] = []
const emitterPreviewTransform = new Map<number, { x: number; y: number; rotation: number }>()
const selectedPathNodes = new Set<number>()
let suppressNextPathDoubleClick = false
@@ -908,7 +909,7 @@ function drawCollisionBodies() {
if (!collisionGfx) return
const g = collisionGfx
g.clear()
for (const collider of store.colliders) {
for (const collider of previewColliders) {
const selected = store.activeObjectType === 'collision' && store.activeObjectId === collider.id
const color = collider.enabled ? (selected ? 0xff5e72 : 0xd4475b) : 0x586174
const angle = collider.rotation * Math.PI / 180
@@ -1050,6 +1051,29 @@ function sampleScenePath(path: ScenePath, progress: number) {
return { x, y, angle: Math.atan2(next.y - previous.y, next.x - previous.x) * 180 / Math.PI }
}
function buildPreviewColliders(timeSeconds: number) {
previewColliders = store.colliders.map((collider) => {
if (!collider.followPath || !collider.followPathId) return collider
const path = store.paths.find((item) => item.id === collider.followPathId && item.enabled)
if (!path?.points.length) return collider
const duration = Math.max(0.05, Number(collider.followDuration) || 2)
const normalized = Math.min(1, Math.max(0, timeSeconds / duration))
const curve = Array.isArray(collider.followCurve) ? collider.followCurve : [{ x: 0, y: 0 }, { x: 1, y: 1 }]
const progress = collider.followCurveMode === '线性'
? normalized
: Math.min(1, Math.max(0, followCurveValue(curve, normalized)))
const sample = sampleScenePath(path, progress)
return sample
? {
...collider,
x: sample.x,
y: sample.y,
rotation: collider.rotation + (collider.followPathRotation ? sample.angle : 0),
}
: collider
})
}
function worldPointToEmitterLocal(config: { centerX: number; centerY: number; rootRotation: number; rootScaleX: number; rootScaleY: number }, worldX: number, worldY: number) {
const scaleX = Math.max(0.0001, Math.abs(config.rootScaleX))
const scaleY = Math.max(0.0001, Math.abs(config.rootScaleY))
@@ -1075,12 +1099,13 @@ function loop() {
syncEmitters()
// 根节点(root)变换:位置(centerX/centerY)=位移,rotation/scale 绕 root 中心,与场景对象参数联动
const followTime = store.timeline.frame / Math.max(1, store.timeline.fps)
buildPreviewColliders(followTime)
for (const sys of store.systems) {
const em = emitterMap.get(sys.id)
if (!em) continue
const c = sys.config
em.scale.set(c.rootScaleX, c.rootScaleY)
em.setColliders(store.colliders)
em.setColliders(previewColliders)
em.setEmitterFollowSpawn(0, 0, 0)
let preview = { x: c.centerX, y: c.centerY, rotation: c.rootRotation }
const followPath = c.emitterFollow && c.emitterFollowMode === 'path'