@fps-games/vfx 0.3.7 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -38,6 +38,7 @@ const buildingUpgradeEffectPackage = {
|
|
|
38
38
|
manifest,
|
|
39
39
|
defaultParams: DEFAULT_PARAMS,
|
|
40
40
|
debugAnchor: "playerRight",
|
|
41
|
+
applyRootScale: false,
|
|
41
42
|
debugParams: [
|
|
42
43
|
{ key: "lifetime", label: "生命周期", kind: "lifetime", min: 0.2, max: 6, step: 0.01 },
|
|
43
44
|
{ key: "color", label: "金光颜色", kind: "color" },
|
|
@@ -56,13 +57,13 @@ const buildingUpgradeEffectPackage = {
|
|
|
56
57
|
],
|
|
57
58
|
create({ scene, root, position, params, scale }) {
|
|
58
59
|
root.position.copyFrom(position);
|
|
59
|
-
const
|
|
60
|
-
const centerStrands = createStrandSystem(scene, root, params,
|
|
61
|
-
const outlineStrands = createStrandSystem(scene, root, params,
|
|
62
|
-
const glow = createOutlineGlow(scene, root, params,
|
|
60
|
+
const footprintScale = normalizeBuildingUpgradeScale(scale);
|
|
61
|
+
const centerStrands = createStrandSystem(scene, root, params, footprintScale, "center");
|
|
62
|
+
const outlineStrands = createStrandSystem(scene, root, params, footprintScale, "outline");
|
|
63
|
+
const glow = createOutlineGlow(scene, root, params, footprintScale);
|
|
63
64
|
const disposeTimer = window.setTimeout(
|
|
64
65
|
() => disposeAll(),
|
|
65
|
-
Math.max(0.05, params.lifetime + getMaxStrandHeight(
|
|
66
|
+
Math.max(0.05, params.lifetime + getMaxStrandHeight() / Math.max(0.1, params.riseSpeed) * 0.18 + 0.45) * 1e3
|
|
66
67
|
);
|
|
67
68
|
let disposed = false;
|
|
68
69
|
centerStrands.start();
|
|
@@ -85,12 +86,12 @@ const buildingUpgradeEffectPackage = {
|
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
88
|
};
|
|
88
|
-
function createStrandSystem(scene, root, params,
|
|
89
|
+
function createStrandSystem(scene, root, params, footprintScale, mode) {
|
|
89
90
|
const count = Math.max(0, Math.round(mode === "center" ? params.centerCount : params.outlineCount));
|
|
90
91
|
const system = new ParticleSystem(`building-upgrade.${mode}-strands`, Math.max(1, count), scene);
|
|
91
92
|
const color = params.color;
|
|
92
|
-
const height = mode === "center" ? getCenterHeight(
|
|
93
|
-
const width = params.strandWidth *
|
|
93
|
+
const height = mode === "center" ? getCenterHeight() : getOutlineHeight();
|
|
94
|
+
const width = params.strandWidth * (mode === "center" ? 1.35 : 0.92);
|
|
94
95
|
configureVfxParticleSystem(system);
|
|
95
96
|
system.particleTexture = getGoldStrandTexture(scene);
|
|
96
97
|
system.emitter = root;
|
|
@@ -126,7 +127,7 @@ function createStrandSystem(scene, root, params, visualScale, mode) {
|
|
|
126
127
|
system.startPositionFunction = (worldMatrix, positionToUpdate, particle, isLocal) => {
|
|
127
128
|
const index = particle.id + positionIndex;
|
|
128
129
|
positionIndex += 1;
|
|
129
|
-
const local = mode === "center" ? sampleCenterPosition(index, params) : sampleOutlinePosition(index, params);
|
|
130
|
+
const local = mode === "center" ? sampleCenterPosition(index, params, footprintScale) : sampleOutlinePosition(index, params, footprintScale);
|
|
130
131
|
if (isLocal) positionToUpdate.copyFrom(local);
|
|
131
132
|
else Vector3.TransformCoordinatesToRef(local, worldMatrix, positionToUpdate);
|
|
132
133
|
};
|
|
@@ -144,11 +145,11 @@ function createStrandSystem(scene, root, params, visualScale, mode) {
|
|
|
144
145
|
};
|
|
145
146
|
return system;
|
|
146
147
|
}
|
|
147
|
-
function createOutlineGlow(scene, root, params,
|
|
148
|
+
function createOutlineGlow(scene, root, params, footprintScale) {
|
|
148
149
|
const count = Math.max(1, Math.round(params.outlineCount * 0.82));
|
|
149
150
|
const system = new ParticleSystem("building-upgrade.outline-glow-rays", count, scene);
|
|
150
|
-
const height = getOutlineHeight(
|
|
151
|
-
const glowWidth = Math.max(0.04, BASE_GLOW_THICKNESS
|
|
151
|
+
const height = getOutlineHeight() * 1.06;
|
|
152
|
+
const glowWidth = Math.max(0.04, BASE_GLOW_THICKNESS);
|
|
152
153
|
const alpha = params.glowOpacity * params.opacity;
|
|
153
154
|
configureVfxParticleSystem(system);
|
|
154
155
|
system.particleTexture = getOutlineGlowTexture(scene);
|
|
@@ -185,7 +186,7 @@ function createOutlineGlow(scene, root, params, visualScale) {
|
|
|
185
186
|
system.startPositionFunction = (worldMatrix, positionToUpdate, particle, isLocal) => {
|
|
186
187
|
const index = particle.id + positionIndex + 31;
|
|
187
188
|
positionIndex += 1;
|
|
188
|
-
const local = sampleOutlinePosition(index, params);
|
|
189
|
+
const local = sampleOutlinePosition(index, params, footprintScale);
|
|
189
190
|
if (isLocal) positionToUpdate.copyFrom(local);
|
|
190
191
|
else Vector3.TransformCoordinatesToRef(local, worldMatrix, positionToUpdate);
|
|
191
192
|
};
|
|
@@ -203,13 +204,13 @@ function createOutlineGlow(scene, root, params, visualScale) {
|
|
|
203
204
|
};
|
|
204
205
|
return system;
|
|
205
206
|
}
|
|
206
|
-
function sampleCenterPosition(index, params) {
|
|
207
|
+
function sampleCenterPosition(index, params, footprintScale) {
|
|
207
208
|
const shape = getFootprintShape(params);
|
|
208
|
-
if (shape === "circle") return sampleCircleCenterPosition(index, params);
|
|
209
|
+
if (shape === "circle") return sampleCircleCenterPosition(index, params, footprintScale);
|
|
209
210
|
const angle = sequence01(index, 0) * Math.PI * 2;
|
|
210
211
|
const radius = Math.sqrt(0.16 + sequence01(index, 1) * 0.84);
|
|
211
|
-
const xRadius = getFootprintX() * 0.5 * params.centerSpread;
|
|
212
|
-
const zRadius = getFootprintZ() * 0.5 * params.centerSpread;
|
|
212
|
+
const xRadius = getFootprintX(footprintScale) * 0.5 * params.centerSpread;
|
|
213
|
+
const zRadius = getFootprintZ(footprintScale) * 0.5 * params.centerSpread;
|
|
213
214
|
const jitterX = (sequence01(index, 7) - 0.5) * xRadius * 0.18;
|
|
214
215
|
const jitterZ = (sequence01(index, 8) - 0.5) * zRadius * 0.18;
|
|
215
216
|
const x = Math.cos(angle) * radius * xRadius + jitterX;
|
|
@@ -217,23 +218,24 @@ function sampleCenterPosition(index, params) {
|
|
|
217
218
|
const y = sequence01(index, 2) * 0.12;
|
|
218
219
|
return new Vector3(x, y, z);
|
|
219
220
|
}
|
|
220
|
-
function sampleOutlinePosition(index, params) {
|
|
221
|
+
function sampleOutlinePosition(index, params, footprintScale) {
|
|
221
222
|
const shape = getFootprintShape(params);
|
|
222
|
-
if (shape === "circle") return sampleCircleOutlinePosition(index, params);
|
|
223
|
-
return sampleRectOutlinePosition(index, getFootprintX(), getFootprintZ(), params.outlineThickness);
|
|
223
|
+
if (shape === "circle") return sampleCircleOutlinePosition(index, params, footprintScale);
|
|
224
|
+
return sampleRectOutlinePosition(index, getFootprintX(footprintScale), getFootprintZ(footprintScale), params.outlineThickness);
|
|
224
225
|
}
|
|
225
|
-
function sampleCircleCenterPosition(index, params) {
|
|
226
|
+
function sampleCircleCenterPosition(index, params, footprintScale) {
|
|
226
227
|
const angle = sequence01(index, 0) * Math.PI * 2;
|
|
227
|
-
const
|
|
228
|
-
const
|
|
228
|
+
const baseRadius = BASE_CIRCLE_RADIUS * footprintScale.horizontal;
|
|
229
|
+
const radius = Math.sqrt(sequence01(index, 1)) * baseRadius * params.centerSpread;
|
|
230
|
+
const jitterRadius = (sequence01(index, 7) - 0.5) * baseRadius * 0.08;
|
|
229
231
|
const r = Math.max(0, radius + jitterRadius);
|
|
230
232
|
const y = sequence01(index, 2) * 0.12;
|
|
231
233
|
return new Vector3(Math.cos(angle) * r, y, Math.sin(angle) * r);
|
|
232
234
|
}
|
|
233
|
-
function sampleCircleOutlinePosition(index, params) {
|
|
235
|
+
function sampleCircleOutlinePosition(index, params, footprintScale) {
|
|
234
236
|
const angle = sequence01(index, 0) * Math.PI * 2;
|
|
235
237
|
const jitter = (sequence01(index, 1) - 0.5) * params.outlineThickness;
|
|
236
|
-
const radius = Math.max(1e-3, BASE_CIRCLE_RADIUS + jitter);
|
|
238
|
+
const radius = Math.max(1e-3, BASE_CIRCLE_RADIUS * footprintScale.horizontal + jitter);
|
|
237
239
|
const y = sequence01(index, 2) * 0.16;
|
|
238
240
|
return new Vector3(Math.cos(angle) * radius, y, Math.sin(angle) * radius);
|
|
239
241
|
}
|
|
@@ -251,28 +253,26 @@ function getFootprintShape(params) {
|
|
|
251
253
|
if (params.footprintShape === "circle") return params.footprintShape;
|
|
252
254
|
return "rectangle";
|
|
253
255
|
}
|
|
254
|
-
function getFootprintX() {
|
|
255
|
-
return BASE_FOOTPRINT_X;
|
|
256
|
+
function getFootprintX(footprintScale) {
|
|
257
|
+
return BASE_FOOTPRINT_X * footprintScale.x;
|
|
256
258
|
}
|
|
257
|
-
function getFootprintZ() {
|
|
258
|
-
return BASE_FOOTPRINT_Z;
|
|
259
|
+
function getFootprintZ(footprintScale) {
|
|
260
|
+
return BASE_FOOTPRINT_Z * footprintScale.z;
|
|
259
261
|
}
|
|
260
|
-
function getCenterHeight(
|
|
261
|
-
return BASE_CENTER_HEIGHT
|
|
262
|
+
function getCenterHeight() {
|
|
263
|
+
return BASE_CENTER_HEIGHT;
|
|
262
264
|
}
|
|
263
|
-
function getOutlineHeight(
|
|
264
|
-
return BASE_OUTLINE_HEIGHT
|
|
265
|
+
function getOutlineHeight() {
|
|
266
|
+
return BASE_OUTLINE_HEIGHT;
|
|
265
267
|
}
|
|
266
|
-
function getMaxStrandHeight(
|
|
267
|
-
return Math.max(getCenterHeight(
|
|
268
|
+
function getMaxStrandHeight() {
|
|
269
|
+
return Math.max(getCenterHeight(), getOutlineHeight());
|
|
268
270
|
}
|
|
269
271
|
function normalizeBuildingUpgradeScale(scale) {
|
|
270
272
|
const x = Math.max(0.05, Math.abs(scale.x));
|
|
271
|
-
const y = Math.max(0.05, Math.abs(scale.y));
|
|
272
273
|
const z = Math.max(0.05, Math.abs(scale.z));
|
|
273
274
|
return {
|
|
274
275
|
x,
|
|
275
|
-
y,
|
|
276
276
|
z,
|
|
277
277
|
horizontal: Math.max(0.05, (x + z) * 0.5)
|
|
278
278
|
};
|
|
@@ -16,5 +16,6 @@ export declare const woodCrusherSawdustEffectPackage: {
|
|
|
16
16
|
requiredInputs?: import('../..').VfxInputKey[];
|
|
17
17
|
optionalInputs?: import('../..').VfxInputKey[];
|
|
18
18
|
geometrySpace?: "local" | "world";
|
|
19
|
+
applyRootScale?: boolean;
|
|
19
20
|
create(context: import('../..').VfxEffectPackageContext<import('../..').VfxParamValues>): import('../..').VfxEffectHandle;
|
|
20
21
|
};
|
|
@@ -16,5 +16,6 @@ export declare const woodCutterSawdustEffectPackage: {
|
|
|
16
16
|
requiredInputs?: import('../..').VfxInputKey[];
|
|
17
17
|
optionalInputs?: import('../..').VfxInputKey[];
|
|
18
18
|
geometrySpace?: "local" | "world";
|
|
19
|
+
applyRootScale?: boolean;
|
|
19
20
|
create(context: import('../..').VfxEffectPackageContext<import('../..').VfxParamValues>): import('../..').VfxEffectHandle;
|
|
20
21
|
};
|
package/dist/index.js
CHANGED
|
@@ -114,7 +114,8 @@ class EffectPackageService {
|
|
|
114
114
|
warnMissingRequiredInputs(effectId, effectPackage.requiredInputs, spawn.inputs);
|
|
115
115
|
const runtimeParams = stripSpawnParams(params, effectPackage.spawnParams);
|
|
116
116
|
const root = new TransformNode(`vfx_package_${effectId}`, this.scene);
|
|
117
|
-
|
|
117
|
+
const applyRootScale = effectPackage.applyRootScale !== false;
|
|
118
|
+
applySpawnToRoot(root, spawn, applyRootScale);
|
|
118
119
|
const worldPosition = root.getAbsolutePosition().clone();
|
|
119
120
|
let handle;
|
|
120
121
|
try {
|
|
@@ -128,7 +129,7 @@ class EffectPackageService {
|
|
|
128
129
|
scale: spawn.scale,
|
|
129
130
|
inputs: spawn.inputs
|
|
130
131
|
});
|
|
131
|
-
applySpawnToRoot(root, spawn);
|
|
132
|
+
applySpawnToRoot(root, spawn, applyRootScale);
|
|
132
133
|
} catch (error) {
|
|
133
134
|
console.warn(`[EffectPackageService] effect "${effectId}" create() failed; skipped.`, error);
|
|
134
135
|
try {
|
|
@@ -212,10 +213,11 @@ function warnMissingRequiredInputs(effectId, required, inputs) {
|
|
|
212
213
|
console.warn(`[EffectPackageService] effect "${effectId}" 声明 requiredInputs [${missing.join(", ")}] 但未提供 → 可能静默降级。`);
|
|
213
214
|
}
|
|
214
215
|
}
|
|
215
|
-
function applySpawnToRoot(root, spawn) {
|
|
216
|
+
function applySpawnToRoot(root, spawn, applyRootScale = true) {
|
|
216
217
|
if (spawn.parent) root.parent = spawn.parent;
|
|
217
218
|
root.rotation.copyFrom(spawn.rotation);
|
|
218
|
-
root.scaling.copyFrom(spawn.scale);
|
|
219
|
+
if (applyRootScale) root.scaling.copyFrom(spawn.scale);
|
|
220
|
+
else root.scaling.set(1, 1, 1);
|
|
219
221
|
if (spawn.parent && !spawn.offsetIsLocal) {
|
|
220
222
|
root.computeWorldMatrix(true);
|
|
221
223
|
root.setAbsolutePosition(spawn.position);
|
package/dist/types.d.ts
CHANGED
|
@@ -157,6 +157,12 @@ export interface VfxEffectPackage<PParams extends VfxParamValues = VfxParamValue
|
|
|
157
157
|
* 默认随 placement 推断('socket'→local,其余→world)。
|
|
158
158
|
*/
|
|
159
159
|
geometrySpace?: 'local' | 'world';
|
|
160
|
+
/**
|
|
161
|
+
* 是否把 spawnTransform.scale 应用到 root.scaling。默认 true。
|
|
162
|
+
* 对需要自行解释 scale 的特效(例如只把 scale 当作发射 footprint 尺寸)可设为 false,
|
|
163
|
+
* 此时 service 仍会把 scale 传入 create(context.scale),但 root 保持单位缩放。
|
|
164
|
+
*/
|
|
165
|
+
applyRootScale?: boolean;
|
|
160
166
|
/**
|
|
161
167
|
* 构建特效。**约定(G6):root 的 transform(position/rotation/scaling/parent)归 service 独占** ——
|
|
162
168
|
* service 在 create() 后会二次 `applySpawnToRoot` 覆盖,create 内写 `root.position/rotation/scaling` 会被静默清掉。
|
package/package.json
CHANGED