@fps-games/vfx 0.3.2 → 0.3.3

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.
Files changed (231) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/package.json +5 -4
  3. package/tooling/vfx-cli.mjs +8 -1
  4. package/docs/AUTHORING_EFFECTS.md +0 -297
  5. package/docs/INTEGRATION.md +0 -194
  6. package/docs/VFX_LIBRARY_ARCHITECTURE_chatgpt.md +0 -1580
  7. package/docs/VFX_LIBRARY_EXECUTION_PLAN.md +0 -229
  8. package/docs/VFX_LIBRARY_PLAN.md +0 -546
  9. package/docs/hit-flash-red-method.md +0 -155
  10. package/docs/treasure_attention_fx_babylonjs_guide.md +0 -901
  11. package/docs/vfx-ab/RESULTS.md +0 -35
  12. package/docs/vfx-ab/lumber-order/vehicle-exhaust-smoke/after.png +0 -0
  13. package/docs/vfx-ab/lumber-order/vehicle-exhaust-smoke/before.png +0 -0
  14. package/docs/vfx-ab/lumber-truck/vehicle-exhaust-smoke/after.png +0 -0
  15. package/docs/vfx-ab/lumber-truck/vehicle-exhaust-smoke/before.png +0 -0
  16. package/docs/vfx-integration_chatgpt.md +0 -462
  17. package/docs/vfx-placement-design.md +0 -164
  18. package/docs/weapon-trail-visualization.html +0 -241
  19. package/packages/EffectPackageService.ts +0 -346
  20. package/packages/SceneVfxService.ts +0 -96
  21. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/runtime/simpleMeshEffect.v1.js +0 -333
  22. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/shaders/mesh-effect-preview.frag.glsl +0 -25
  23. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/shaders/mesh-effect-preview.vert.glsl +0 -11
  24. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/textures/effect_jianbian.png +0 -0
  25. package/packages/build.mesh.yellow_build_upgrade_indicator/effect.json +0 -927
  26. package/packages/build.mesh.yellow_build_upgrade_indicator/media/contact-sheet.png +0 -0
  27. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.gif +0 -0
  28. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.mp4 +0 -0
  29. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.webm +0 -0
  30. package/packages/build.mesh.yellow_build_upgrade_indicator/media/thumbnail.png +0 -0
  31. package/packages/build.mesh.yellow_build_upgrade_indicator/metadata/description.json +0 -197
  32. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/03-effect.original.json +0 -895
  33. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/03-normalized-vfx-to-babylon-report.json +0 -241
  34. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/04-preview-overrides.json +0 -542
  35. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/04-preview-tuning-report.json +0 -69
  36. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/asset-map.json +0 -125
  37. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/lineage.json +0 -44
  38. package/packages/build.particle.golden_upgrade_starburst/assets/runtime/particleEmitter.v1.js +0 -210
  39. package/packages/build.particle.golden_upgrade_starburst/assets/textures/effect_guangdian.png +0 -0
  40. package/packages/build.particle.golden_upgrade_starburst/effect.json +0 -880
  41. package/packages/build.particle.golden_upgrade_starburst/media/contact-sheet.png +0 -0
  42. package/packages/build.particle.golden_upgrade_starburst/media/preview.gif +0 -0
  43. package/packages/build.particle.golden_upgrade_starburst/media/preview.mp4 +0 -0
  44. package/packages/build.particle.golden_upgrade_starburst/media/preview.webm +0 -0
  45. package/packages/build.particle.golden_upgrade_starburst/media/thumbnail.png +0 -0
  46. package/packages/build.particle.golden_upgrade_starburst/metadata/description.json +0 -161
  47. package/packages/build.particle.golden_upgrade_starburst/provenance/03-effect.original.json +0 -762
  48. package/packages/build.particle.golden_upgrade_starburst/provenance/03-normalized-vfx-to-babylon-report.json +0 -260
  49. package/packages/build.particle.golden_upgrade_starburst/provenance/04-preview-overrides.json +0 -436
  50. package/packages/build.particle.golden_upgrade_starburst/provenance/04-preview-tuning-report.json +0 -40
  51. package/packages/build.particle.golden_upgrade_starburst/provenance/asset-map.json +0 -115
  52. package/packages/build.particle.golden_upgrade_starburst/provenance/lineage.json +0 -40
  53. package/packages/build.particle.orange_upgrade_transform_sparks/assets/runtime/particleEmitter.v1.js +0 -210
  54. package/packages/build.particle.orange_upgrade_transform_sparks/assets/textures/b5b27ab1-e740-4398-b407-848fc2b2c897.png +0 -0
  55. package/packages/build.particle.orange_upgrade_transform_sparks/effect.json +0 -887
  56. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.gif +0 -0
  57. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.mp4 +0 -0
  58. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.webm +0 -0
  59. package/packages/build.particle.orange_upgrade_transform_sparks/media/thumbnail.png +0 -0
  60. package/packages/build.particle.orange_upgrade_transform_sparks/metadata/description.json +0 -179
  61. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/03-effect.original.json +0 -859
  62. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/03-normalized-vfx-to-babylon-report.json +0 -260
  63. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/04-preview-overrides.json +0 -478
  64. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/04-preview-tuning-report.json +0 -40
  65. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/asset-map.json +0 -114
  66. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/lineage.json +0 -41
  67. package/packages/build.particle.wood_chip_dust_burst/assets/runtime/particleEmitter.v1.js +0 -210
  68. package/packages/build.particle.wood_chip_dust_burst/assets/textures/effect_yanwu4.png +0 -0
  69. package/packages/build.particle.wood_chip_dust_burst/assets/textures/suipian3.png +0 -0
  70. package/packages/build.particle.wood_chip_dust_burst/effect.json +0 -1400
  71. package/packages/build.particle.wood_chip_dust_burst/media/contact-sheet.png +0 -0
  72. package/packages/build.particle.wood_chip_dust_burst/media/preview.gif +0 -0
  73. package/packages/build.particle.wood_chip_dust_burst/media/preview.mp4 +0 -0
  74. package/packages/build.particle.wood_chip_dust_burst/media/preview.webm +0 -0
  75. package/packages/build.particle.wood_chip_dust_burst/media/thumbnail.png +0 -0
  76. package/packages/build.particle.wood_chip_dust_burst/metadata/description.json +0 -173
  77. package/packages/build.particle.wood_chip_dust_burst/provenance/03-effect.original.json +0 -1277
  78. package/packages/build.particle.wood_chip_dust_burst/provenance/03-normalized-vfx-to-babylon-report.json +0 -329
  79. package/packages/build.particle.wood_chip_dust_burst/provenance/04-preview-overrides.json +0 -749
  80. package/packages/build.particle.wood_chip_dust_burst/provenance/04-preview-tuning-report.json +0 -60
  81. package/packages/build.particle.wood_chip_dust_burst/provenance/asset-map.json +0 -146
  82. package/packages/build.particle.wood_chip_dust_burst/provenance/lineage.json +0 -69
  83. package/packages/effects/building-upgrade/effect.json +0 -6
  84. package/packages/effects/building-upgrade/index.ts +0 -387
  85. package/packages/effects/character-entrance/effect.json +0 -6
  86. package/packages/effects/character-entrance/index.ts +0 -308
  87. package/packages/effects/coin-burst/index.ts +0 -48
  88. package/packages/effects/comet-trail-flame/assets/textures/trail_5x4.webp +0 -0
  89. package/packages/effects/comet-trail-flame/index.ts +0 -107
  90. package/packages/effects/conveyor-upgrade/effect.json +0 -6
  91. package/packages/effects/conveyor-upgrade/index.ts +0 -401
  92. package/packages/effects/debris-smoke/assets/models/rock_debris.glb +0 -0
  93. package/packages/effects/debris-smoke/assets/textures/smoke22.webp +0 -0
  94. package/packages/effects/debris-smoke/index.ts +0 -102
  95. package/packages/effects/door_open_gold_flakes/assets/runtime/goldFlakes.v1.js +0 -397
  96. package/packages/effects/door_open_gold_flakes/door_open_gold_flakes.json +0 -498
  97. package/packages/effects/door_open_gold_flakes/index.ts +0 -11
  98. package/packages/effects/electric-arc/assets/textures/spark_4x4.webp +0 -0
  99. package/packages/effects/electric-arc/index.ts +0 -63
  100. package/packages/effects/energy-shield/effect.json +0 -7
  101. package/packages/effects/energy-shield/index.ts +0 -432
  102. package/packages/effects/explosion/assets/textures/baozha29.webp +0 -0
  103. package/packages/effects/explosion/assets/textures/glow01.webp +0 -0
  104. package/packages/effects/explosion/assets/textures/lizi01.webp +0 -0
  105. package/packages/effects/explosion/assets/textures/xulie_lizibaozha02_5x5.webp +0 -0
  106. package/packages/effects/explosion/index.ts +0 -115
  107. package/packages/effects/ground-tile-upgrade/effect.json +0 -6
  108. package/packages/effects/ground-tile-upgrade/index.ts +0 -410
  109. package/packages/effects/hit-flash/index.ts +0 -77
  110. package/packages/effects/laser-beam/assets/textures/laser_glow.webp +0 -0
  111. package/packages/effects/laser-beam/assets/textures/laser_noise.webp +0 -0
  112. package/packages/effects/laser-beam/index.ts +0 -6
  113. package/packages/effects/laser-beam-factory.ts +0 -146
  114. package/packages/effects/lumber-cart-suction/effect.json +0 -6
  115. package/packages/effects/lumber-cart-suction/index.ts +0 -399
  116. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb +0 -0
  117. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb.versions/.active +0 -1
  118. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb.versions/1781589063890_upload.glb +0 -0
  119. package/packages/effects/lumber-debris-scatter/assets/runtime/solidParticleScatter.v1.js +0 -236
  120. package/packages/effects/lumber-debris-scatter/effect.json +0 -228
  121. package/packages/effects/lumber-debris-scatter/index.ts +0 -780
  122. package/packages/effects/lumber_vehicle_gear_spin/assets/runtime/gearSpin.v1.js +0 -202
  123. package/packages/effects/lumber_vehicle_gear_spin/index.ts +0 -11
  124. package/packages/effects/lumber_vehicle_gear_spin/lumber_vehicle_gear_spin.json +0 -338
  125. package/packages/effects/lumber_vehicle_upgrade_aura/assets/runtime/upgradeAura.v1.js +0 -353
  126. package/packages/effects/lumber_vehicle_upgrade_aura/index.ts +0 -13
  127. package/packages/effects/lumber_vehicle_upgrade_aura/lumber_vehicle_upgrade_aura.json +0 -365
  128. package/packages/effects/lumber_vehicle_wood_chips/assets/runtime/solidParticleScatter.v1.js +0 -310
  129. package/packages/effects/lumber_vehicle_wood_chips/index.ts +0 -11
  130. package/packages/effects/lumber_vehicle_wood_chips/lumber_vehicle_wood_chips.json +0 -335
  131. package/packages/effects/monster-hit/index.ts +0 -129
  132. package/packages/effects/player-upgrade/effect.json +0 -6
  133. package/packages/effects/player-upgrade/index.ts +0 -640
  134. package/packages/effects/pulse-laser-volley/assets/textures/bolt_guangshu.webp +0 -0
  135. package/packages/effects/pulse-laser-volley/index.ts +0 -215
  136. package/packages/effects/resource-collect-sparkles/effect.json +0 -6
  137. package/packages/effects/resource-collect-sparkles/index.ts +0 -254
  138. package/packages/effects/run-footstep-dust/assets/runtime/runFootstepDust.v1.js +0 -215
  139. package/packages/effects/run-footstep-dust/effect.json +0 -183
  140. package/packages/effects/run-footstep-dust/index.ts +0 -377
  141. package/packages/effects/space-shared.ts +0 -120
  142. package/packages/effects/thruster-flame/index.ts +0 -306
  143. package/packages/effects/treasure-attention/effect.json +0 -7
  144. package/packages/effects/treasure-attention/index.ts +0 -630
  145. package/packages/effects/tree-chop-dust/assets/textures/tree_chop_dust_atlas.png +0 -0
  146. package/packages/effects/tree-chop-dust/effect.json +0 -8
  147. package/packages/effects/tree-chop-dust/index.ts +0 -202
  148. package/packages/effects/tree-leaf-chop-burst/assets/runtime/particleEmitter.v1.js +0 -303
  149. package/packages/effects/tree-leaf-chop-burst/assets/textures/tree_leaf_chop_burst_leaf_atlas.png +0 -0
  150. package/packages/effects/tree-leaf-chop-burst/effect.json +0 -498
  151. package/packages/effects/tree-leaf-chop-burst/index.ts +0 -294
  152. package/packages/effects/tree_chop_dust_burst/assets/runtime/particleEmitter.v1.js +0 -318
  153. package/packages/effects/tree_chop_dust_burst/assets/textures/tree_chop_dust_burst_dust.png +0 -0
  154. package/packages/effects/tree_chop_dust_burst/index.ts +0 -13
  155. package/packages/effects/tree_chop_dust_burst/tree_chop_dust_burst.json +0 -412
  156. package/packages/effects/ufo-thread-flame/assets/textures/flame_flow.webp +0 -0
  157. package/packages/effects/ufo-thread-flame/assets/textures/flame_spike.webp +0 -0
  158. package/packages/effects/ufo-thread-flame/index.ts +0 -153
  159. package/packages/effects/vehicle-exhaust-smoke/assets/textures/vehicle_exhaust_smoke_atlas.png +0 -0
  160. package/packages/effects/vehicle-exhaust-smoke/effect.json +0 -8
  161. package/packages/effects/vehicle-exhaust-smoke/index.ts +0 -298
  162. package/packages/effects/vehicle-upgrade/effect.json +0 -6
  163. package/packages/effects/vehicle-upgrade/index.ts +0 -404
  164. package/packages/effects/weapon-trail/assets/textures/trail_wedge.png +0 -0
  165. package/packages/effects/weapon-trail/index.ts +0 -317
  166. package/packages/effects/wood-crusher-sawdust/assets/runtime/particleEmitter.v1.js +0 -312
  167. package/packages/effects/wood-crusher-sawdust/assets/textures/wood_crusher_sawdust_dust.png +0 -0
  168. package/packages/effects/wood-crusher-sawdust/assets/textures/wood_crusher_sawdust_fiber.png +0 -0
  169. package/packages/effects/wood-crusher-sawdust/index.ts +0 -43
  170. package/packages/effects/wood-crusher-sawdust/wood_crusher_sawdust.json +0 -592
  171. package/packages/effects/wood-cutter-sawdust/assets/runtime/particleEmitter.v1.js +0 -312
  172. package/packages/effects/wood-cutter-sawdust/assets/textures/wood_cutter_sawdust_chip.png +0 -0
  173. package/packages/effects/wood-cutter-sawdust/assets/textures/wood_cutter_sawdust_dust.png +0 -0
  174. package/packages/effects/wood-cutter-sawdust/index.ts +0 -43
  175. package/packages/effects/wood-cutter-sawdust/wood_cutter_sawdust.json +0 -592
  176. package/packages/effects/wood-sawdust-effect-factory.ts +0 -194
  177. package/packages/effects/wood_crusher_sawdust/assets/runtime/particleEmitter.v1.js +0 -318
  178. package/packages/effects/wood_crusher_sawdust/assets/textures/wood_crusher_sawdust_dust.png +0 -0
  179. package/packages/effects/wood_crusher_sawdust/assets/textures/wood_crusher_sawdust_fiber.png +0 -0
  180. package/packages/effects/wood_crusher_sawdust/index.ts +0 -29
  181. package/packages/effects/wood_crusher_sawdust/wood_crusher_sawdust.json +0 -592
  182. package/packages/effects/wood_cutter_sawdust/assets/runtime/particleEmitter.v1.js +0 -318
  183. package/packages/effects/wood_cutter_sawdust/assets/textures/wood_cutter_sawdust_chip.png +0 -0
  184. package/packages/effects/wood_cutter_sawdust/assets/textures/wood_cutter_sawdust_dust.png +0 -0
  185. package/packages/effects/wood_cutter_sawdust/index.ts +0 -29
  186. package/packages/effects/wood_cutter_sawdust/wood_cutter_sawdust.json +0 -592
  187. package/packages/effects/wood_peel_material_test/assets/runtime/woodPeelMaterialTest.v1.js +0 -196
  188. package/packages/effects/wood_peel_material_test/index.ts +0 -11
  189. package/packages/effects/wood_peel_material_test/wood_peel_material_test.json +0 -136
  190. package/packages/env.particle.forest_edge_fog/assets/runtime/particleEmitter.v1.js +0 -210
  191. package/packages/env.particle.forest_edge_fog/assets/textures/f12a23c4-b924-4322-a260-3d982428f1e8.png +0 -0
  192. package/packages/env.particle.forest_edge_fog/assets/textures/yuan.png +0 -0
  193. package/packages/env.particle.forest_edge_fog/effect.json +0 -854
  194. package/packages/env.particle.forest_edge_fog/media/preview.gif +0 -0
  195. package/packages/env.particle.forest_edge_fog/media/preview.mp4 +0 -0
  196. package/packages/env.particle.forest_edge_fog/media/preview.webm +0 -0
  197. package/packages/env.particle.forest_edge_fog/media/thumbnail.png +0 -0
  198. package/packages/env.particle.forest_edge_fog/metadata/description.json +0 -148
  199. package/packages/env.particle.forest_edge_fog/provenance/03-effect.original.json +0 -827
  200. package/packages/env.particle.forest_edge_fog/provenance/03-normalized-vfx-to-babylon-report.json +0 -196
  201. package/packages/env.particle.forest_edge_fog/provenance/04-preview-overrides.json +0 -432
  202. package/packages/env.particle.forest_edge_fog/provenance/04-preview-tuning-report.json +0 -28
  203. package/packages/env.particle.forest_edge_fog/provenance/asset-map.json +0 -124
  204. package/packages/env.particle.forest_edge_fog/provenance/lineage.json +0 -41
  205. package/packages/generation.ts +0 -14
  206. package/packages/index.ts +0 -14
  207. package/packages/legacy-particle-runtime.ts +0 -113
  208. package/packages/manifestRuntimePackage.ts +0 -519
  209. package/packages/registry.ts +0 -85
  210. package/packages/rendering.ts +0 -13
  211. package/packages/resource.particle.viscous_oil_suction/assets/runtime/top_disk_suction/particleEmitter.v1.js +0 -424
  212. package/packages/resource.particle.viscous_oil_suction/assets/textures/Glow ad.png +0 -0
  213. package/packages/resource.particle.viscous_oil_suction/effect.json +0 -1254
  214. package/packages/resource.particle.viscous_oil_suction/media/preview.gif +0 -0
  215. package/packages/resource.particle.viscous_oil_suction/media/preview.mp4 +0 -0
  216. package/packages/resource.particle.viscous_oil_suction/media/preview.webm +0 -0
  217. package/packages/resource.particle.viscous_oil_suction/media/thumbnail.png +0 -0
  218. package/packages/resource.particle.viscous_oil_suction/metadata/description.json +0 -122
  219. package/packages/resource.particle.viscous_oil_suction/provenance/03-effect.original.json +0 -1051
  220. package/packages/resource.particle.viscous_oil_suction/provenance/03-normalized-vfx-to-babylon-report.json +0 -200
  221. package/packages/resource.particle.viscous_oil_suction/provenance/04-preview-overrides.json +0 -581
  222. package/packages/resource.particle.viscous_oil_suction/provenance/04-preview-tuning-report.json +0 -28
  223. package/packages/resource.particle.viscous_oil_suction/provenance/asset-map.json +0 -111
  224. package/packages/resource.particle.viscous_oil_suction/provenance/lineage.json +0 -45
  225. package/packages/types.ts +0 -195
  226. package/tooling/check-dist-runtime.mjs +0 -166
  227. package/tooling/generate-index.mjs +0 -55
  228. package/tooling/test-guardrails.mjs +0 -157
  229. package/tooling/validate-boundary.mjs +0 -92
  230. package/tooling/validate-effects.mjs +0 -304
  231. package/tooling/validate.mjs +0 -53
@@ -1,346 +0,0 @@
1
- import { Vector3 } from '@babylonjs/core/Maths/math.vector';
2
- import type { Scene } from '@babylonjs/core/scene';
3
- import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
4
- import { VFX_DEFAULT_GENERATION_PARAMS, VFX_GENERATION_PARAM_KEYS } from './generation';
5
- import type { VfxRegistry } from './registry';
6
- import type {
7
- VfxColorValue,
8
- VfxDebugAnchor,
9
- VfxEffectHandle,
10
- VfxEffectInputs,
11
- VfxEffectPackage,
12
- VfxInputKey,
13
- VfxParamValue,
14
- VfxParamValues,
15
- VfxResolvedInputs,
16
- VfxSpawnParamKeys,
17
- VfxSpawnTransform,
18
- } from './types';
19
-
20
- export interface EffectPackageServiceOptions {
21
- /** 注入的注册表(由项目侧 glob + buildRegistry 构建);库不直接引用项目注册表 */
22
- registry: VfxRegistry;
23
- getDebugAnchorPosition?: (anchor: VfxDebugAnchor) => Vector3 | null;
24
- }
25
-
26
- const DEFAULT_SPAWN_PARAM_KEYS: Required<Pick<VfxSpawnParamKeys, 'offsetX' | 'offsetY' | 'offsetZ'>> = {
27
- offsetX: VFX_GENERATION_PARAM_KEYS.offsetX,
28
- offsetY: VFX_GENERATION_PARAM_KEYS.offsetY,
29
- offsetZ: VFX_GENERATION_PARAM_KEYS.offsetZ,
30
- };
31
-
32
- export class EffectPackageService {
33
- private readonly scene: Scene;
34
- private readonly registry: VfxRegistry;
35
- private readonly getDebugAnchorPosition?: (anchor: VfxDebugAnchor) => Vector3 | null;
36
- private readonly activeHandles = new Set<VfxEffectHandle>();
37
- private readonly savedParams: Record<string, Partial<VfxParamValues>>;
38
-
39
- constructor(scene: Scene, options: EffectPackageServiceOptions) {
40
- this.scene = scene;
41
- this.registry = options.registry;
42
- this.getDebugAnchorPosition = options.getDebugAnchorPosition;
43
- this.savedParams = cloneOverrideMap(options.registry.savedParams);
44
- }
45
-
46
- getEffectPackages(): VfxEffectPackage[] {
47
- return this.registry.getEffectPackages();
48
- }
49
-
50
- getEffectPackage(effectId: string): VfxEffectPackage | undefined {
51
- return this.registry.getEffectPackage(effectId);
52
- }
53
-
54
- getMergedParams(effectId: string, explicitParams?: Partial<VfxParamValues>): VfxParamValues | null {
55
- const effectPackage = this.getEffectPackage(effectId);
56
- if (!effectPackage) return null;
57
- return mergeVfxParams(
58
- VFX_DEFAULT_GENERATION_PARAMS,
59
- effectPackage.defaultParams,
60
- effectPackage.savedParams,
61
- this.savedParams[effectId],
62
- explicitParams,
63
- );
64
- }
65
-
66
- getDebugOverride(effectId: string): Partial<VfxParamValues> {
67
- return cloneParams(this.savedParams[effectId]);
68
- }
69
-
70
- setDebugOverride(effectId: string, params: Partial<VfxParamValues>): void {
71
- this.savedParams[effectId] = cloneParams(params);
72
- const effectPackage = this.getEffectPackage(effectId);
73
- if (effectPackage) effectPackage.savedParams = cloneParams(params);
74
- }
75
-
76
- playEffectPackage(
77
- effectId: string,
78
- explicitParams?: Partial<VfxParamValues>,
79
- spawnTransform?: VfxSpawnTransform | Vector3,
80
- ): VfxEffectHandle | null {
81
- const effectPackage = this.getEffectPackage(effectId);
82
- if (!effectPackage) {
83
- console.warn(`[EffectPackageService] Unknown VFX effect package: ${effectId}`);
84
- return null;
85
- }
86
-
87
- const params = this.getMergedParams(effectId, explicitParams);
88
- if (!params) return null;
89
-
90
- const spawn = this.resolveSpawnTransform(effectPackage, params, spawnTransform);
91
- warnMissingRequiredInputs(effectId, effectPackage.requiredInputs, spawn.inputs);
92
- const runtimeParams = stripSpawnParams(params, effectPackage.spawnParams);
93
- const root = new TransformNode(`vfx_package_${effectId}`, this.scene);
94
- applySpawnToRoot(root, spawn);
95
- // 世界位置(挂到 parent 后即为绝对世界坐标):保证世界空间特效(如拖尾)起点正确。
96
- const worldPosition = root.getAbsolutePosition().clone();
97
- let handle: VfxEffectHandle;
98
- try {
99
- handle = effectPackage.create({
100
- scene: this.scene,
101
- root,
102
- position: worldPosition,
103
- rotationY: spawn.rotationY,
104
- renderingGroupId: spawn.renderingGroupId,
105
- params: runtimeParams,
106
- scale: spawn.scale,
107
- inputs: spawn.inputs,
108
- });
109
- // root 的摆放归 service 独占。许多特效在 create() 里会写 `root.position.copyFrom(position)`
110
- // (用世界坐标),挂到 parent 时会与 local offset 冲突 → 双偏移。这里在 create() 之后
111
- // 重新断言一次 root transform,覆盖回 spawn 的正确值(无 parent 时为同值,无副作用)。
112
- applySpawnToRoot(root, spawn);
113
- } catch (error) {
114
- // 错误兜底:单个坏包不得崩掉整个场景(plan §7)
115
- console.warn(`[EffectPackageService] effect "${effectId}" create() failed; skipped.`, error);
116
- try { root.dispose(); } catch { /* ignore */ }
117
- return null;
118
- }
119
- this.activeHandles.add(handle);
120
- const originalDispose = handle.dispose.bind(handle);
121
- handle.dispose = () => {
122
- this.activeHandles.delete(handle);
123
- originalDispose();
124
- };
125
- // G4/M4:很多特效内部用 scheduleLifetime 自销,直接 dispose root(绕过上面 wrap 的 handle.dispose),
126
- // 会让 activeHandles 残留已死 handle。监听 root 销毁 → 任何路径销毁都清理(自销/手动皆覆盖)。
127
- handle.root.onDisposeObservable.addOnce(() => { this.activeHandles.delete(handle); });
128
- return handle;
129
- }
130
-
131
- dispose(): void {
132
- for (const handle of this.activeHandles) {
133
- handle.dispose();
134
- }
135
- this.activeHandles.clear();
136
- }
137
-
138
- private resolveDebugPosition(effectPackage: VfxEffectPackage): Vector3 {
139
- const anchor = effectPackage.debugAnchor ?? 'playerRight';
140
- const anchorPosition = this.getDebugAnchorPosition?.(anchor);
141
- if (anchorPosition) return anchorPosition.clone();
142
- const cameraTarget = (this.scene.activeCamera as any)?.target as Vector3 | undefined;
143
- if (cameraTarget?.clone) return cameraTarget.clone().addInPlace(new Vector3(1.2, 0, 0));
144
- return new Vector3(0, 0, 0);
145
- }
146
-
147
- private resolveSpawnTransform(
148
- effectPackage: VfxEffectPackage,
149
- params: VfxParamValues,
150
- spawnTransform?: VfxSpawnTransform | Vector3,
151
- ): ResolvedSpawnTransform {
152
- const normalized = normalizeSpawnTransform(spawnTransform);
153
- const parent = normalized.parent ?? null;
154
- // 有 parent(挂载)时默认落在挂点 (0,0,0) local;无 parent 时才回退到 debug 锚点(预览用)。
155
- const basePosition = normalized.position?.clone()
156
- ?? (parent != null ? new Vector3(0, 0, 0) : this.resolveDebugPosition(effectPackage));
157
- const rotation = resolveRotation(normalized.rotation, normalized.rotationY);
158
- const scale = resolveScale(normalized.scale);
159
- // 默认坐标系:给了 parent → local;没给 → 世界(保持现状)。
160
- const offsetIsLocal = normalized.offsetIsLocal ?? parent != null;
161
- const renderingGroupId = readOptionalFiniteNumber(normalized.renderingGroupId);
162
- const offset = readSpawnOffset(params, effectPackage.spawnParams);
163
- let position = basePosition;
164
- if (offset.lengthSquared() > 0.0000001) {
165
- const cos = Math.cos(rotation.y);
166
- const sin = Math.sin(rotation.y);
167
- position = basePosition.addInPlace(new Vector3(
168
- offset.x * cos - offset.z * sin,
169
- offset.y,
170
- offset.x * sin + offset.z * cos,
171
- ));
172
- }
173
- return { position, rotation, rotationY: rotation.y, scale, parent, offsetIsLocal, renderingGroupId, inputs: normalizeInputs(normalized.inputs) };
174
- }
175
- }
176
-
177
- // 把调用方的 inputs(值 or provider)归一化为访问器:create() 里 inputs.aim() 总可调用(动态目标每帧取)。
178
- function normalizeInputs(inputs?: VfxEffectInputs): VfxResolvedInputs | undefined {
179
- if (!inputs) return undefined;
180
- const asFn = <T>(v: T | (() => T) | undefined): (() => T) | undefined =>
181
- v == null ? undefined : (typeof v === 'function' ? v as () => T : () => v);
182
- const out: VfxResolvedInputs = {};
183
- if (inputs.aim != null) out.aim = asFn(inputs.aim);
184
- if (inputs.normal != null) out.normal = asFn(inputs.normal);
185
- if (inputs.endpoints) out.endpoints = inputs.endpoints.map((e) => asFn(e)!).filter(Boolean);
186
- if (inputs.target) out.target = inputs.target;
187
- if (inputs.scalars) {
188
- out.scalars = {};
189
- for (const [k, v] of Object.entries(inputs.scalars)) {
190
- const fn = asFn(v);
191
- if (fn) out.scalars[k] = fn;
192
- }
193
- }
194
- return out;
195
- }
196
-
197
- // G2:特效声明 requiredInputs 但调用方未提供对应 inputs → 警告(漏接静默降级很难查)。optionalInputs 不在此列。
198
- function warnMissingRequiredInputs(effectId: string, required: VfxInputKey[] | undefined, inputs: VfxResolvedInputs | undefined): void {
199
- if (!required || required.length === 0) return;
200
- const missing = required.filter((k) => {
201
- const v = inputs ? (inputs as Record<string, unknown>)[k] : undefined;
202
- return v == null || (k === 'endpoints' && Array.isArray(v) && v.length < 2);
203
- });
204
- if (missing.length > 0) {
205
- console.warn(`[EffectPackageService] effect "${effectId}" 声明 requiredInputs [${missing.join(', ')}] 但未提供 → 可能静默降级。`);
206
- }
207
- }
208
-
209
- interface ResolvedSpawnTransform {
210
- /** 按 offsetIsLocal 解释的位置:local(相对 parent)或世界坐标。 */
211
- position: Vector3;
212
- /** 完整欧拉角旋转(弧度)。 */
213
- rotation: Vector3;
214
- /** 有效 Y 轴旋转(弧度),透传给 create() 上下文(向后兼容)。 */
215
- rotationY: number;
216
- /** 三轴缩放。 */
217
- scale: Vector3;
218
- parent: TransformNode | null;
219
- offsetIsLocal: boolean;
220
- renderingGroupId?: number;
221
- inputs?: VfxResolvedInputs;
222
- }
223
-
224
- // 把解析后的 spawn transform 落到 root 节点上。
225
- // - 没给 parent:position/rotation/scale 即世界坐标(无 parent 时 local == world),与现状一致。
226
- // - 给了 parent + offsetIsLocal(默认):写到 root 的 local transform(随 parent 跟随)。
227
- // - 给了 parent + offsetIsLocal=false:position 按绝对世界坐标落位;rotation/scale 仍按 local 应用。
228
- function applySpawnToRoot(root: TransformNode, spawn: ResolvedSpawnTransform): void {
229
- if (spawn.parent) root.parent = spawn.parent;
230
- root.rotation.copyFrom(spawn.rotation);
231
- root.scaling.copyFrom(spawn.scale);
232
- if (spawn.parent && !spawn.offsetIsLocal) {
233
- // 需要先有 parent 的世界矩阵,才能把世界坐标反解成 local。
234
- root.computeWorldMatrix(true);
235
- root.setAbsolutePosition(spawn.position);
236
- } else {
237
- root.position.copyFrom(spawn.position);
238
- }
239
- root.computeWorldMatrix(true);
240
- }
241
-
242
- function resolveRotation(rotation: VfxSpawnTransform['rotation'], rotationY: unknown): Vector3 {
243
- if (rotation) {
244
- return new Vector3(
245
- readFiniteNumber(rotation.x, 0),
246
- readFiniteNumber(rotation.y, 0),
247
- readFiniteNumber(rotation.z, 0),
248
- );
249
- }
250
- return new Vector3(0, readFiniteNumber(rotationY, 0), 0);
251
- }
252
-
253
- function resolveScale(scale: VfxSpawnTransform['scale']): Vector3 {
254
- if (scale == null) return new Vector3(1, 1, 1);
255
- if (typeof scale === 'number') {
256
- const s = readFiniteNumber(scale, 1);
257
- return new Vector3(s, s, s);
258
- }
259
- return new Vector3(
260
- readFiniteNumber(scale.x, 1),
261
- readFiniteNumber(scale.y, 1),
262
- readFiniteNumber(scale.z, 1),
263
- );
264
- }
265
-
266
- function normalizeSpawnTransform(spawnTransform?: VfxSpawnTransform | Vector3): VfxSpawnTransform {
267
- if (!spawnTransform) return {};
268
- if (spawnTransform instanceof Vector3) return { position: spawnTransform };
269
- return spawnTransform;
270
- }
271
-
272
- function readSpawnOffset(params: VfxParamValues, spawnParams?: VfxSpawnParamKeys): Vector3 {
273
- const keys = resolveSpawnParamKeys(spawnParams);
274
- return new Vector3(
275
- readParamNumber(params[keys.offsetX ?? ''], 0),
276
- readParamNumber(params[keys.offsetY ?? ''], 0),
277
- readParamNumber(params[keys.offsetZ ?? ''], 0),
278
- );
279
- }
280
-
281
- function stripSpawnParams(params: VfxParamValues, spawnParams?: VfxSpawnParamKeys): VfxParamValues {
282
- const result = cloneParams(params) as VfxParamValues;
283
- const keys = resolveSpawnParamKeys(spawnParams);
284
- for (const key of [keys.offsetX, keys.offsetY, keys.offsetZ]) {
285
- if (key) delete result[key];
286
- }
287
- return result;
288
- }
289
-
290
- function resolveSpawnParamKeys(spawnParams?: VfxSpawnParamKeys): VfxSpawnParamKeys {
291
- return spawnParams ?? DEFAULT_SPAWN_PARAM_KEYS;
292
- }
293
-
294
- function readParamNumber(value: VfxParamValue | undefined, fallback: number): number {
295
- return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
296
- }
297
-
298
- function readFiniteNumber(value: unknown, fallback: number): number {
299
- return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
300
- }
301
-
302
- function readOptionalFiniteNumber(value: unknown): number | undefined {
303
- return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
304
- }
305
-
306
- function mergeVfxParams(
307
- ...sources: Array<Partial<VfxParamValues> | undefined>
308
- ): VfxParamValues {
309
- const result: Partial<VfxParamValues> = {};
310
- for (const source of sources) {
311
- Object.assign(result, cloneParams(source));
312
- }
313
- return result as VfxParamValues;
314
- }
315
-
316
- function cloneParams(value: Partial<VfxParamValues> | undefined): Partial<VfxParamValues> {
317
- if (!value) return {};
318
- const result: Partial<VfxParamValues> = {};
319
- for (const [key, item] of Object.entries(value)) {
320
- if (item === undefined) continue;
321
- result[key] = cloneParamValue(item);
322
- }
323
- return result;
324
- }
325
-
326
- function cloneOverrideMap(value: Record<string, Partial<VfxParamValues>>): Record<string, Partial<VfxParamValues>> {
327
- const result: Record<string, Partial<VfxParamValues>> = {};
328
- for (const [effectId, params] of Object.entries(value)) {
329
- result[effectId] = cloneParams(params);
330
- }
331
- return result;
332
- }
333
-
334
- function cloneParamValue(value: VfxParamValue): VfxParamValue {
335
- if (isColorValue(value)) return { ...value };
336
- return value;
337
- }
338
-
339
- function isColorValue(value: unknown): value is VfxColorValue {
340
- return !!value
341
- && typeof value === 'object'
342
- && !Array.isArray(value)
343
- && typeof (value as VfxColorValue).r === 'number'
344
- && typeof (value as VfxColorValue).g === 'number'
345
- && typeof (value as VfxColorValue).b === 'number';
346
- }
@@ -1,96 +0,0 @@
1
- /**
2
- * SceneVfxService —— 特效播放门面(库纯净版)
3
- *
4
- * 只委托 EffectPackageService:播放 / 预热 / 销毁。注册表经 options.registry 注入。
5
- * 注:旧版的 `initFromConfig`/`createParticleSystem`(读 scene.json + UIImages 的常驻场景粒子路径)
6
- * 属项目耦合,已从库剥离(契约 §17)。场景常驻粒子应建模为普通特效包,由项目在 init 时触发。
7
- */
8
- import { Scene } from '@babylonjs/core/scene';
9
- import { Vector3 } from '@babylonjs/core/Maths/math.vector';
10
-
11
- import type { VfxRegistry } from './registry';
12
- import type {
13
- VfxDebugAnchor,
14
- VfxEffectHandle,
15
- VfxEffectPackage,
16
- VfxParamValues,
17
- VfxSpawnTransform,
18
- } from './types';
19
- import { EffectPackageService } from './EffectPackageService';
20
-
21
- export interface SceneVfxWarmupEntry {
22
- effectId: string;
23
- params?: Partial<VfxParamValues>;
24
- spawnTransform?: VfxSpawnTransform | Vector3;
25
- }
26
-
27
- export interface SceneVfxServiceOptions {
28
- /** 注入的注册表(项目侧 glob + buildRegistry 构建) */
29
- registry: VfxRegistry;
30
- getDebugAnchorPosition?: (anchor: VfxDebugAnchor) => Vector3 | null;
31
- }
32
-
33
- export class SceneVfxService {
34
- private effectPackageService: EffectPackageService;
35
-
36
- constructor(scene: Scene, options: SceneVfxServiceOptions) {
37
- this.effectPackageService = new EffectPackageService(scene, {
38
- registry: options.registry,
39
- getDebugAnchorPosition: options.getDebugAnchorPosition,
40
- });
41
- }
42
-
43
- getEffectPackageService(): EffectPackageService {
44
- return this.effectPackageService;
45
- }
46
-
47
- getEffectPackages(): VfxEffectPackage[] {
48
- return this.effectPackageService.getEffectPackages();
49
- }
50
-
51
- getMergedParams(effectId: string, explicitParams?: Partial<VfxParamValues>): VfxParamValues | null {
52
- return this.effectPackageService.getMergedParams(effectId, explicitParams);
53
- }
54
-
55
- playEffectPackage(
56
- effectId: string,
57
- params: Partial<VfxParamValues> = {},
58
- spawnTransform?: VfxSpawnTransform | Vector3,
59
- ): VfxEffectHandle | null {
60
- return this.effectPackageService.playEffectPackage(effectId, params, spawnTransform);
61
- }
62
-
63
- async warmupEffectPackages(entries: SceneVfxWarmupEntry[]): Promise<void> {
64
- await Promise.all(entries.map((entry) => this.warmupEffectPackage(entry)));
65
- }
66
-
67
- private async warmupEffectPackage(entry: SceneVfxWarmupEntry): Promise<void> {
68
- let handle: VfxEffectHandle | null = null;
69
- try {
70
- handle = this.playEffectPackage(entry.effectId, entry.params ?? {}, entry.spawnTransform);
71
- if (!handle) return;
72
- await handle.ready;
73
- await this.waitForAnimationFrames(2);
74
- } catch (error) {
75
- console.warn(`[SceneVfxService] Failed to warm up effect package "${entry.effectId}".`, error);
76
- } finally {
77
- handle?.dispose();
78
- }
79
- }
80
-
81
- private async waitForAnimationFrames(frameCount: number): Promise<void> {
82
- for (let i = 0; i < frameCount; i += 1) {
83
- await new Promise<void>((resolve) => {
84
- if (typeof requestAnimationFrame === 'function') {
85
- requestAnimationFrame(() => resolve());
86
- return;
87
- }
88
- window.setTimeout(resolve, 0);
89
- });
90
- }
91
- }
92
-
93
- dispose(): void {
94
- this.effectPackageService.dispose();
95
- }
96
- }