系统优化

This commit is contained in:
tianmo committed 2026-08-29 01:59:58 +08:00
1 parent b98d67fdab
commit c37cf09273
5 files changed
+489 -32

No files matched your search

+77 -4
View File
@@ -138,13 +138,17 @@
<div class="group">
<div class="group-head" @click="toggle('emitMode')">
<span class="caret">{{ collapsed.emitMode ? '▸' : '▾' }}</span><span>发射模式</span>
<button class="group-reset" title="重置发射模式参数" @click.stop="resetModule('emitMode')">重置</button>
</div>
<div class="group-body" v-show="!collapsed.emitMode">
<div class="segs">
<button class="seg" :class="{ on: sys.config.mode === 'stream' }" @click="sys.config.mode = 'stream'">持续</button>
<button class="seg" :class="{ on: sys.config.mode === 'burst' }" @click="sys.config.mode = 'burst'">爆发</button>
<button class="seg" :class="{ on: sys.config.mode === 'stream' }" @click="setEmitMode('stream')">持续</button>
<button class="seg" :class="{ on: sys.config.mode === 'burst' }" @click="setEmitMode('burst')">爆发</button>
</div>
<NumSlider v-if="sys.config.mode === 'stream'" label="发射速率" :min="1" :max="300" :step="1" v-model="sys.config.rate" />
<template v-if="sys.config.mode === 'stream'">
<NumSlider label="发射速率" :min="1" :max="300" :step="1" v-model="sys.config.rate" />
<NumSlider label="发射时长" :min="1 / 30" :max="60" :step="1 / 30" v-model="sys.config.duration" />
</template>
<template v-else>
<NumSlider label="单次数量" :min="1" :max="1000" :step="1" v-model="sys.config.burstCount" />
</template>
@@ -156,6 +160,7 @@
<div class="group">
<div class="group-head" @click="toggle('shape')">
<span class="caret">{{ collapsed.shape ? '▸' : '▾' }}</span><span>发射器形状</span>
<button class="group-reset" title="重置发射器形状参数" @click.stop="resetModule('shape')">重置</button>
</div>
<div class="group-body" v-show="!collapsed.shape">
<label class="row"><span>形状</span>
@@ -187,6 +192,7 @@
<div class="group">
<div class="group-head" @click="toggle('attr')">
<span class="caret">{{ collapsed.attr ? '▸' : '▾' }}</span><span>粒子属性</span>
<button class="group-reset" title="重置粒子属性参数" @click.stop="resetModule('attr')">重置</button>
</div>
<div class="group-body attr-body" v-show="!collapsed.attr">
<div class="attr-section-title">粒子初始参数</div>
@@ -336,6 +342,7 @@
<div class="group">
<div class="group-head" @click="toggle('look')">
<span class="caret">{{ collapsed.look ? '▸' : '▾' }}</span><span>外观与资源</span>
<button class="group-reset" title="重置外观与资源参数" @click.stop="resetModule('look')">重置</button>
</div>
<div class="group-body" v-show="!collapsed.look">
<!-- 透明度 -->
@@ -427,6 +434,7 @@
<div class="group">
<div class="group-head" @click="toggle('mods')">
<span class="caret">{{ collapsed.mods ? '▸' : '▾' }}</span><span>修改器 (Modifiers)</span>
<button class="group-reset" title="重置修改器参数" @click.stop="resetModule('mods')">重置</button>
</div>
<div class="group-body" v-show="!collapsed.mods">
<div class="modifier-item">
@@ -662,6 +670,7 @@
<div class="group">
<div class="group-head" @click="toggle('export')">
<span class="caret">{{ collapsed.export ? '▸' : '▾' }}</span><span>导出设置</span><span class="tag">预留</span>
<button class="group-reset" title="重置导出设置" @click.stop="resetModule('export')">重置</button>
</div>
<div class="group-body" v-show="!collapsed.export">
<div class="placeholder">导出功能后续实现,已预留位置</div>
@@ -675,7 +684,7 @@
import { computed, markRaw, onUnmounted, reactive, ref, watchEffect } from 'vue'
import { Texture } from 'pixi.js'
import { useParticleStore } from '../store/particleStore'
import { ensureEmitterConfig } from '../core/particleEmitter'
import { defaultConfig, ensureEmitterConfig } from '../core/particleEmitter'
import NumSlider from './NumSlider.vue'
import CurveEditor from './CurveEditor.vue'
import ParticleAttributeControl from './ParticleAttributeControl.vue'
@@ -708,6 +717,7 @@ watchEffect(() => {
if (config.speedMode === 'curve') config.speedMode = 'random'
if (config.scaleMode === 'curve') config.scaleMode = 'random'
if (config.initialRotationMode === 'curve') config.initialRotationMode = 'random'
if (config.mode === 'stream' && (!(config.duration > 0))) config.duration = 1
})
const LINEAR_CURVE = [{ x: 0, y: 0 }, { x: 1, y: 1 }]
@@ -728,6 +738,64 @@ const collapsed = reactive<Record<string, boolean>>({
})
function toggle(key: string) { collapsed[key] = !collapsed[key] }
function setEmitMode(mode: 'stream' | 'burst') {
const config = sys.value?.config
if (!config) return
config.mode = mode
if (mode === 'stream' && (!(config.duration > 0))) config.duration = 1
}
type ResetModule = 'emitMode' | 'shape' | 'attr' | 'look' | 'mods' | 'export'
const MODULE_RESET_KEYS: Record<Exclude<ResetModule, 'export'>, string[]> = {
emitMode: ['mode', 'rate', 'burstCount', 'burstLoop', 'delay', 'duration'],
shape: ['shape', 'radius', 'rectW', 'rectH', 'direction', 'spread', 'coneAngle'],
attr: [
'lifeMin', 'lifeMax', 'lifeMode', 'lifeCurve',
'speedMin', 'speedMax', 'speedMode', 'speedCurve',
'scaleMin', 'scaleMax', 'scaleMode', 'scaleAxisMode', 'scaleYMin', 'scaleYMax',
'scaleCurve', 'scaleYCurve', 'scaleEndRatio',
'initialRotationMode', 'initialRotationMin', 'initialRotationMax', 'initialRotationCurve',
'rotationSpeedMin', 'rotationSpeedMax',
'speedOverLifeEnabled', 'speedOverLifeMode', 'speedOverLifeMin', 'speedOverLifeMax', 'speedOverLifeCurve',
'scaleOverLifeEnabled', 'scaleOverLifeMode', 'scaleOverLifeAxisMode',
'scaleOverLifeMin', 'scaleOverLifeMax', 'scaleYOverLifeMin', 'scaleYOverLifeMax',
'scaleOverLifeCurve', 'scaleYOverLifeCurve',
'rotationOverLifeEnabled', 'rotationOverLifeMode', 'rotationOverLifeCurve',
],
look: [
'texture', 'imageResources', 'imageWeightVersion',
'alpha', 'alphaEnd', 'colorStart', 'colorEnd', 'colorOverLife', 'blend',
'alphaMode', 'curveMode', 'alphaCurve', 'textureName', 'textureFolder',
'alphaScale', 'anchorX', 'anchorY', 'independentAlpha',
],
mods: [
'noise', 'noiseAmt', 'noiseRange', 'noiseSpeed',
'useGravity', 'gravity', 'gravityMode', 'gravityMin', 'gravityMax', 'gravityDirection', 'gravityCurve',
'wind', 'windX', 'windY', 'windMode', 'windMin', 'windMax', 'windDirection', 'windCurve',
'forceField', 'forceStrength', 'forceRadius', 'forceCenterX', 'forceCenterY', 'forceFalloff',
'attraction', 'attractionCenterX', 'attractionCenterY', 'attractionRadius',
'attractionDuration', 'attractionDelay', 'attractionDelayMode', 'attractionPath',
'trail', 'trailConfigVersion', 'trailResources', 'trailResourceVersion',
'trailTexture', 'trailTextureName', 'trailPreviewUrl', 'trailColorMode', 'trailColor',
'trailColorGradient', 'trailAlphaMode', 'trailAlpha', 'trailAlphaCurve',
'trailBoneCount', 'trailLength', 'trailWidth', 'trailWidthMode',
'trailLifeLengthEnabled', 'trailLifeLengthCurve', 'trailShapeEnabled', 'trailShapeCurve',
'trailGridRows', 'trailGridCols', 'collision', 'pathFollow',
'emitterFollow', 'emitterFollowMode', 'emitterFollowPathId', 'emitterAngleMode',
'emitterFollowCurve', 'emitterFollowDuration', 'emitterFollowSpace',
'emitterFollowDirection', 'emitterFollowOffset', 'damping',
],
}
function resetModule(module: ResetModule) {
const config = sys.value?.config as any
if (!config || module === 'export') return
const defaults = defaultConfig() as any
for (const key of MODULE_RESET_KEYS[module]) config[key] = defaults[key]
// 重置资源模块后,Stage 会在下一帧重新挂载默认 star/trail 纹理。
}
function select(i: number) { store.selectSystem(store.systems[i]?.id ?? 0) }
function addSys() { store.addSystem() }
function addCollider() { store.addCollider() }
@@ -1203,6 +1271,11 @@ function toggleResourceLock(resourceId: number) {
font-size: 12px; font-weight: 600; color: #ccd; background: #191924; user-select: none;
}
.group-head:hover { background: #20202e; }
.group-reset {
margin-left: auto; height: 22px; padding: 0 7px; border: 1px solid #3a465f; border-radius: 5px;
background: #252d3d; color: #9eabc3; font-size: 10px; line-height: 20px; cursor: pointer;
}
.group-reset:hover { border-color: #7774ff; background: #45426f; color: #fff; }
.scene-head { padding-right: 6px; }
.scene-actions { margin-left: auto; display: flex; align-items: center; gap: 4px; }
.scene-add {