子发射器
This commit is contained in:
1 parent
d4ebb18777
commit
d224939efd
33 files changed
+5690
-1355
No files matched your search
+42
-23
@@ -37,6 +37,7 @@ import { Application, Container, Graphics, type Text, type Texture } from 'pixi.
|
||||
import { ParticleEmitter, ensureEmitterConfig, type ParticleState } from '../core/particleEmitter'
|
||||
import { bakeLoopFrames } from '../core/loopFrameBaker'
|
||||
import { loopPlaybackRange } from '../core/loopSegmentTiming'
|
||||
import { collectSubEmitterIds } from '../core/subEmitterIds'
|
||||
import { useParticleStore, type CollisionBody, type ParticleSystem } from '../store/particleStore'
|
||||
import { SpineRuntimeLayer } from '../spine/SpineRuntimeLayer'
|
||||
import { installEditorHistory, undoLastOperation } from '../editor/editorHistory'
|
||||
@@ -220,12 +221,21 @@ function bakeSystemFrames(system: ParticleSystem, emitter: ParticleEmitter, fps:
|
||||
*/
|
||||
function ensureFramesBaked(fps: number) {
|
||||
const totalFrames = Math.max(2, Math.floor(store.timeline.totalFrames) || 2)
|
||||
const subEmitterIds = collectSubEmitterIds(store.systems)
|
||||
let complete = true
|
||||
for (const system of store.systems) {
|
||||
if ((system.frames?.length || 0) >= totalFrames) continue
|
||||
const emitter = emitterSync.emitters.get(system.id)
|
||||
if (!emitter) { complete = false; continue }
|
||||
bakeSystemFrames(system as unknown as ParticleSystem, emitter, fps, totalFrames)
|
||||
// 烘焙预热会大量出生/死亡粒子,期间暂停跨系统事件,避免注入预热粒子。
|
||||
emitterSync.suspendEvents(true)
|
||||
try {
|
||||
for (const system of store.systems) {
|
||||
// 子发射器由父粒子事件驱动,不能脱离父系统单独烘焙。
|
||||
if (subEmitterIds.has(system.id)) continue
|
||||
if ((system.frames?.length || 0) >= totalFrames) continue
|
||||
const emitter = emitterSync.emitters.get(system.id)
|
||||
if (!emitter) { complete = false; continue }
|
||||
bakeSystemFrames(system as unknown as ParticleSystem, emitter, fps, totalFrames)
|
||||
}
|
||||
} finally {
|
||||
emitterSync.suspendEvents(false)
|
||||
}
|
||||
if (complete) store.timeline.recorded = true
|
||||
}
|
||||
@@ -351,6 +361,7 @@ function loop() {
|
||||
appendStateBones(collected, state, emitter.position.x, emitter.position.y)
|
||||
}
|
||||
const tl = store.timeline
|
||||
const playbackRange = store.playbackRange
|
||||
const fps = tl.fps
|
||||
const frameDuration = 1 / Math.max(1, fps)
|
||||
let displayFrame = timelineDisplayFrame
|
||||
@@ -376,18 +387,26 @@ function loop() {
|
||||
total = 0
|
||||
if (!tl.recorded) {
|
||||
// 录制阶段:固定步长模拟并记录当前帧。
|
||||
for (const sys of store.systems) {
|
||||
const emitter = emitterSync.emitters.get(sys.id)
|
||||
if (!emitter) continue
|
||||
const isLoopStream = sys.config.mode === 'stream' && sys.config.streamBehavior === 'loop'
|
||||
// 循环持续是一次性烘焙:只要缓存还没有就补上。录制不一定从第 0 帧开始
|
||||
// (播放头可能被播放范围钳到 A 帧),只按 tl.frame === 0 判断会漏烘焙,
|
||||
// 导致整段回放都没有可用帧。
|
||||
if (isLoopStream) {
|
||||
if (!sys.frames?.length) bakeLoopFrames(sys as unknown as ParticleSystem, emitter, fps, tl.totalFrames)
|
||||
} else if (tl.frame === 0) {
|
||||
emitter.reset()
|
||||
emitterSync.suspendEvents(true)
|
||||
try {
|
||||
const subEmitterIds = collectSubEmitterIds(store.systems)
|
||||
for (const sys of store.systems) {
|
||||
const emitter = emitterSync.emitters.get(sys.id)
|
||||
if (!emitter) continue
|
||||
const isLoopStream = !subEmitterIds.has(sys.id)
|
||||
&& sys.config.mode === 'stream'
|
||||
&& sys.config.streamBehavior === 'loop'
|
||||
// 循环持续是一次性烘焙:只要缓存还没有就补上。录制不一定从第 0 帧开始
|
||||
// (播放头可能被播放范围钳到 A 帧),只按 tl.frame === 0 判断会漏烘焙,
|
||||
// 导致整段回放都没有可用帧。
|
||||
if (isLoopStream) {
|
||||
if (!sys.frames?.length) bakeLoopFrames(sys as unknown as ParticleSystem, emitter, fps, tl.totalFrames)
|
||||
} else if (tl.frame === 0) {
|
||||
emitter.reset()
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
emitterSync.suspendEvents(false)
|
||||
}
|
||||
for (const [systemId, em] of emitterSync.emitters) {
|
||||
const system = store.systems.find((item) => item.id === systemId)
|
||||
@@ -412,7 +431,7 @@ function loop() {
|
||||
}
|
||||
tl.frame++
|
||||
if (tl.frame >= tl.totalFrames) {
|
||||
tl.frame = tl.playbackStart
|
||||
tl.frame = playbackRange.start
|
||||
tl.recorded = true
|
||||
}
|
||||
} else {
|
||||
@@ -435,13 +454,13 @@ function loop() {
|
||||
: null
|
||||
// 烘焙数据保留“末帧 = 首帧”以便导出闭环。预览精确循环段时,末帧只作为
|
||||
// 数学边界,不再与下一轮首帧连续显示两次,避免衔接处停顿一帧。
|
||||
const closesActiveLoop = tl.loop
|
||||
&& activeLoopRange?.start === tl.playbackStart
|
||||
&& activeLoopRange.end === tl.playbackEnd
|
||||
const playbackLastFrame = closesActiveLoop ? tl.playbackEnd - 1 : tl.playbackEnd
|
||||
const closesActiveLoop = tl.loop && tl.abLoop
|
||||
&& activeLoopRange?.start === playbackRange.start
|
||||
&& activeLoopRange.end === playbackRange.end
|
||||
const playbackLastFrame = closesActiveLoop ? playbackRange.end - 1 : playbackRange.end
|
||||
if (tl.frame > playbackLastFrame) {
|
||||
if (tl.loop) tl.frame = tl.playbackStart
|
||||
else { tl.frame = tl.playbackEnd; tl.playing = false; break }
|
||||
if (tl.loop) tl.frame = playbackRange.start
|
||||
else { tl.frame = playbackRange.end; tl.playing = false; break }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user