spine资源引入
This commit is contained in:
1 parent
166b5015c2
commit
5f4667f736
25 files changed
+31151
-9082
No files matched your search
+35
-19
@@ -12,7 +12,7 @@
|
||||
<button class="scene-add" title="添加粒子系统" @click.stop="addSys">+粒子</button>
|
||||
<button class="scene-add" title="添加碰撞体" @click.stop="addCollider">+碰撞</button>
|
||||
<button class="scene-add" title="添加路径" @click.stop="addPath">+路径</button>
|
||||
<button class="scene-add placeholder-action" title="添加 Spine 骨架(待开发)" @click.stop="showScenePlaceholder('Spine 骨架')">+骨架</button>
|
||||
<button class="scene-add" title="添加 Spine 骨架" @click.stop="addSpine">+骨架</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group-body" v-show="!collapsed.scene">
|
||||
@@ -53,7 +53,19 @@
|
||||
<span class="scene-object-kind">路径</span>
|
||||
<button class="scene-object-delete" title="删除路径" @click.stop="store.removePath(path.id)">✕</button>
|
||||
</div>
|
||||
<div v-if="!store.systems.length && !store.colliders.length && !store.paths.length" class="empty">场景中暂无对象</div>
|
||||
<div
|
||||
v-for="spine in store.spines"
|
||||
:key="`spine-${spine.id}`"
|
||||
class="tree-item scene-object-item"
|
||||
:class="{ on: activeObjectType === 'spine' && spine.id === store.activeObjectId }"
|
||||
@click="store.selectSceneObject('spine', spine.id)"
|
||||
>
|
||||
<span class="tdot spine-dot">◆</span>
|
||||
<span class="tlabel">{{ spine.name }}</span>
|
||||
<span class="scene-object-kind">骨架</span>
|
||||
<button class="scene-object-delete" title="删除骨架" @click.stop="store.removeSpine(spine.id)">✕</button>
|
||||
</div>
|
||||
<div v-if="!store.systems.length && !store.colliders.length && !store.paths.length && !store.spines.length" class="empty">场景中暂无对象</div>
|
||||
</div>
|
||||
<template v-if="activeObjectType === 'particle' && sys">
|
||||
<label class="row"><span>名称</span><input v-model="sys.config.name" class="inp" /></label>
|
||||
@@ -92,6 +104,10 @@
|
||||
<label class="compact-field"><span>Y 位置</span><input v-model.number="activeCollider.y" type="number" class="inp" /></label>
|
||||
</div>
|
||||
<NumSlider label="旋转" :min="-360" :max="360" :step="1" v-model="activeCollider.rotation" />
|
||||
<div class="field-grid">
|
||||
<label class="compact-field"><span>缩放 X</span><input v-model.number="activeCollider.scaleX" type="number" min="0.05" step="0.05" class="inp" /></label>
|
||||
<label class="compact-field"><span>缩放 Y</span><input v-model.number="activeCollider.scaleY" type="number" min="0.05" step="0.05" class="inp" /></label>
|
||||
</div>
|
||||
<NumSlider v-if="activeCollider.shape === 'circle'" label="半径" :min="1" :max="1000" :step="1" v-model="activeCollider.radius" />
|
||||
<div v-else-if="activeCollider.shape === 'ellipse'" class="field-grid">
|
||||
<label class="compact-field"><span>X 半径</span><input v-model.number="activeCollider.radiusX" type="number" min="1" class="inp" /></label>
|
||||
@@ -114,6 +130,15 @@
|
||||
<template v-else-if="activePath">
|
||||
<div class="object-editor-title">路径属性</div>
|
||||
<label class="field-block"><span>轨道名称</span><input v-model="activePath.name" class="inp" /></label>
|
||||
<div class="field-grid">
|
||||
<label class="compact-field"><span>X 位置</span><input v-model.number="activePath.x" type="number" class="inp" /></label>
|
||||
<label class="compact-field"><span>Y 位置</span><input v-model.number="activePath.y" type="number" class="inp" /></label>
|
||||
</div>
|
||||
<NumSlider label="旋转" :min="-360" :max="360" :step="1" v-model="activePath.rotation" />
|
||||
<div class="field-grid">
|
||||
<label class="compact-field"><span>缩放 X</span><input v-model.number="activePath.scaleX" type="number" min="0.05" step="0.05" class="inp" /></label>
|
||||
<label class="compact-field"><span>缩放 Y</span><input v-model.number="activePath.scaleY" type="number" min="0.05" step="0.05" class="inp" /></label>
|
||||
</div>
|
||||
<label class="setting-line"><span>闭合轨道</span><input v-model="activePath.closed" type="checkbox" /></label>
|
||||
<label class="setting-line"><span>轨道颜色</span><input v-model="activePath.color" type="color" class="path-color" /></label>
|
||||
<div class="path-help"><span>操作说明:</span><ul>
|
||||
@@ -127,8 +152,8 @@
|
||||
<button class="path-action primary" @click="applyPathPreset">应用预设</button>
|
||||
<button class="path-action" @click="clearPathAnchors">清空锚点</button>
|
||||
</template>
|
||||
<SpineObjectPanel v-else-if="activeSpine" :spine-object="activeSpine" />
|
||||
<div v-else class="panel-hint">使用右上角按钮创建场景对象</div>
|
||||
<div v-if="sceneActionHint" class="scene-action-hint">{{ sceneActionHint }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -681,7 +706,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, markRaw, onUnmounted, reactive, ref, watchEffect } from 'vue'
|
||||
import { computed, markRaw, reactive, ref, watchEffect } from 'vue'
|
||||
import { Texture } from 'pixi.js'
|
||||
import { useParticleStore } from '../store/particleStore'
|
||||
import { defaultConfig, ensureEmitterConfig } from '../core/particleEmitter'
|
||||
@@ -690,6 +715,7 @@ import CurveEditor from './CurveEditor.vue'
|
||||
import ParticleAttributeControl from './ParticleAttributeControl.vue'
|
||||
import ColorGradientEditor from './ColorGradientEditor.vue'
|
||||
import ImageResourceCard from './ImageResourceCard.vue'
|
||||
import SpineObjectPanel from '../spine/SpineObjectPanel.vue'
|
||||
|
||||
const store = useParticleStore()
|
||||
// 默认场景始终有一个可编辑的粒子系统;热更新保留现有系统时不重复创建。
|
||||
@@ -707,6 +733,9 @@ const activeCollider = computed(() => store.activeObjectType === 'collision'
|
||||
const activePath = computed(() => store.activeObjectType === 'path'
|
||||
? store.paths.find((item) => item.id === store.activeObjectId) || null
|
||||
: null)
|
||||
const activeSpine = computed(() => store.activeObjectType === 'spine'
|
||||
? store.spines.find((item) => item.id === store.activeObjectId) || null
|
||||
: null)
|
||||
|
||||
// 开发热更新会保留旧的 Pinia 对象;渲染属性控件前先补齐新增字段。
|
||||
watchEffect(() => {
|
||||
@@ -800,6 +829,7 @@ function select(i: number) { store.selectSystem(store.systems[i]?.id ?? 0) }
|
||||
function addSys() { store.addSystem() }
|
||||
function addCollider() { store.addCollider() }
|
||||
function addPath() { store.addPath() }
|
||||
function addSpine() { store.addSpine() }
|
||||
function addColliderTag() {
|
||||
const collider = activeCollider.value
|
||||
if (!collider) return
|
||||
@@ -840,16 +870,6 @@ function applyPathPreset() {
|
||||
function clearPathAnchors() {
|
||||
if (activePath.value) activePath.value.points = []
|
||||
}
|
||||
const sceneActionHint = ref('')
|
||||
let sceneActionHintTimer: ReturnType<typeof setTimeout> | null = null
|
||||
function showScenePlaceholder(type: string) {
|
||||
sceneActionHint.value = `${type}对象将在后续阶段开发`
|
||||
if (sceneActionHintTimer) clearTimeout(sceneActionHintTimer)
|
||||
sceneActionHintTimer = setTimeout(() => { sceneActionHint.value = '' }, 2400)
|
||||
}
|
||||
onUnmounted(() => {
|
||||
if (sceneActionHintTimer) clearTimeout(sceneActionHintTimer)
|
||||
})
|
||||
function randomizeSeed() {
|
||||
if (sys.value) sys.value.config.seed = Math.floor(Math.random() * 999999) + 1
|
||||
}
|
||||
@@ -1283,11 +1303,6 @@ function toggleResourceLock(resourceId: number) {
|
||||
background: #2e3a55; color: #cdf; font-size: 10px; white-space: nowrap; cursor: pointer;
|
||||
}
|
||||
.scene-add:hover { border-color: #7774ff; background: #45426f; color: #fff; }
|
||||
.scene-add.placeholder-action { background: #232938; color: #9da7be; }
|
||||
.scene-action-hint {
|
||||
margin-top: 8px; padding: 6px 8px; border: 1px dashed #3a4963; border-radius: 5px;
|
||||
background: #181e2b; color: #8f9ab3; font-size: 11px;
|
||||
}
|
||||
.group-body { padding: 8px 10px 12px; }
|
||||
.caret { display: inline-block; width: 12px; color: #667; font-size: 11px; }
|
||||
.divider { height: 1px; background: #2a2a3c; margin: 8px 0; }
|
||||
@@ -1298,6 +1313,7 @@ function toggleResourceLock(resourceId: number) {
|
||||
.tree-item .tdot.dim { color: #556; }
|
||||
.tree-item .tdot.collision-dot { color: #ff7070; }
|
||||
.tree-item .tdot.path-dot { color: #ff933e; font-size: 14px; }
|
||||
.tree-item .tdot.spine-dot { color: #c08cff; font-size: 11px; }
|
||||
.tree-item .tlabel { flex: 1; }
|
||||
.scene-tree { padding-bottom: 8px; border-bottom: 1px solid #2a2a3c; }
|
||||
.scene-object-item { min-height: 28px; cursor: pointer; }
|
||||
|
||||
Reference in new issue
Block a user