@fps-games/vfx 0.1.1 → 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 (250) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +41 -0
  3. package/dist/effects/coin-burst/index.js +6 -2
  4. package/dist/effects/comet-trail-flame/index.js +3 -1
  5. package/dist/effects/debris-smoke/index.js +1 -0
  6. package/dist/effects/electric-arc/index.js +1 -0
  7. package/dist/effects/energy-shield/index.js +1 -0
  8. package/dist/effects/explosion/index.js +3 -2
  9. package/dist/effects/hit-flash/index.js +12 -4
  10. package/dist/effects/laser-beam/index.js +14 -2
  11. package/dist/effects/lumber_vehicle_upgrade_aura/index.js +1 -0
  12. package/dist/effects/monster-hit/index.js +2 -1
  13. package/dist/effects/player-upgrade/index.js +5 -3
  14. package/dist/effects/pulse-laser-volley/index.js +14 -4
  15. package/dist/effects/thruster-flame/index.js +3 -1
  16. package/dist/effects/treasure-attention/index.d.ts +54 -0
  17. package/dist/effects/treasure-attention/index.js +535 -0
  18. package/dist/effects/ufo-thread-flame/index.js +1 -0
  19. package/dist/effects/vehicle-upgrade/index.js +4 -1
  20. package/dist/effects/weapon-trail/index.js +8 -3
  21. package/dist/effects/wood_crusher_sawdust/index.d.ts +4 -0
  22. package/dist/effects/wood_cutter_sawdust/index.d.ts +4 -0
  23. package/dist/index.js +85 -17
  24. package/dist/types.d.ts +80 -0
  25. package/index.json +19 -1
  26. package/package.json +9 -4
  27. package/packages/effects/treasure-attention/vfx-params.json +49 -0
  28. package/tooling/vfx-cli.mjs +8 -1
  29. package/docs/AUTHORING_EFFECTS.md +0 -297
  30. package/docs/VFX_LIBRARY_ARCHITECTURE_chatgpt.md +0 -1580
  31. package/docs/VFX_LIBRARY_EXECUTION_PLAN.md +0 -229
  32. package/docs/VFX_LIBRARY_PLAN.md +0 -546
  33. package/docs/vfx-ab/RESULTS.md +0 -35
  34. package/docs/vfx-ab/lumber-order/vehicle-exhaust-smoke/after.png +0 -0
  35. package/docs/vfx-ab/lumber-order/vehicle-exhaust-smoke/before.png +0 -0
  36. package/docs/vfx-ab/lumber-truck/vehicle-exhaust-smoke/after.png +0 -0
  37. package/docs/vfx-ab/lumber-truck/vehicle-exhaust-smoke/before.png +0 -0
  38. package/docs/vfx-integration_chatgpt.md +0 -462
  39. package/docs/weapon-trail-visualization.html +0 -241
  40. package/packages/EffectPackageService.ts +0 -240
  41. package/packages/SceneVfxService.ts +0 -96
  42. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/runtime/simpleMeshEffect.v1.js +0 -333
  43. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/shaders/mesh-effect-preview.frag.glsl +0 -25
  44. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/shaders/mesh-effect-preview.vert.glsl +0 -11
  45. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/textures/effect_jianbian.png +0 -0
  46. package/packages/build.mesh.yellow_build_upgrade_indicator/effect.json +0 -927
  47. package/packages/build.mesh.yellow_build_upgrade_indicator/media/contact-sheet.png +0 -0
  48. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.gif +0 -0
  49. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.mp4 +0 -0
  50. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.webm +0 -0
  51. package/packages/build.mesh.yellow_build_upgrade_indicator/media/thumbnail.png +0 -0
  52. package/packages/build.mesh.yellow_build_upgrade_indicator/metadata/description.json +0 -197
  53. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/03-effect.original.json +0 -895
  54. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/03-normalized-vfx-to-babylon-report.json +0 -241
  55. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/04-preview-overrides.json +0 -542
  56. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/04-preview-tuning-report.json +0 -69
  57. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/asset-map.json +0 -125
  58. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/lineage.json +0 -44
  59. package/packages/build.particle.golden_upgrade_starburst/assets/runtime/particleEmitter.v1.js +0 -210
  60. package/packages/build.particle.golden_upgrade_starburst/assets/textures/effect_guangdian.png +0 -0
  61. package/packages/build.particle.golden_upgrade_starburst/effect.json +0 -880
  62. package/packages/build.particle.golden_upgrade_starburst/media/contact-sheet.png +0 -0
  63. package/packages/build.particle.golden_upgrade_starburst/media/preview.gif +0 -0
  64. package/packages/build.particle.golden_upgrade_starburst/media/preview.mp4 +0 -0
  65. package/packages/build.particle.golden_upgrade_starburst/media/preview.webm +0 -0
  66. package/packages/build.particle.golden_upgrade_starburst/media/thumbnail.png +0 -0
  67. package/packages/build.particle.golden_upgrade_starburst/metadata/description.json +0 -161
  68. package/packages/build.particle.golden_upgrade_starburst/provenance/03-effect.original.json +0 -762
  69. package/packages/build.particle.golden_upgrade_starburst/provenance/03-normalized-vfx-to-babylon-report.json +0 -260
  70. package/packages/build.particle.golden_upgrade_starburst/provenance/04-preview-overrides.json +0 -436
  71. package/packages/build.particle.golden_upgrade_starburst/provenance/04-preview-tuning-report.json +0 -40
  72. package/packages/build.particle.golden_upgrade_starburst/provenance/asset-map.json +0 -115
  73. package/packages/build.particle.golden_upgrade_starburst/provenance/lineage.json +0 -40
  74. package/packages/build.particle.orange_upgrade_transform_sparks/assets/runtime/particleEmitter.v1.js +0 -210
  75. package/packages/build.particle.orange_upgrade_transform_sparks/assets/textures/b5b27ab1-e740-4398-b407-848fc2b2c897.png +0 -0
  76. package/packages/build.particle.orange_upgrade_transform_sparks/effect.json +0 -887
  77. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.gif +0 -0
  78. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.mp4 +0 -0
  79. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.webm +0 -0
  80. package/packages/build.particle.orange_upgrade_transform_sparks/media/thumbnail.png +0 -0
  81. package/packages/build.particle.orange_upgrade_transform_sparks/metadata/description.json +0 -179
  82. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/03-effect.original.json +0 -859
  83. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/03-normalized-vfx-to-babylon-report.json +0 -260
  84. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/04-preview-overrides.json +0 -478
  85. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/04-preview-tuning-report.json +0 -40
  86. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/asset-map.json +0 -114
  87. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/lineage.json +0 -41
  88. package/packages/build.particle.wood_chip_dust_burst/assets/runtime/particleEmitter.v1.js +0 -210
  89. package/packages/build.particle.wood_chip_dust_burst/assets/textures/effect_yanwu4.png +0 -0
  90. package/packages/build.particle.wood_chip_dust_burst/assets/textures/suipian3.png +0 -0
  91. package/packages/build.particle.wood_chip_dust_burst/effect.json +0 -1400
  92. package/packages/build.particle.wood_chip_dust_burst/media/contact-sheet.png +0 -0
  93. package/packages/build.particle.wood_chip_dust_burst/media/preview.gif +0 -0
  94. package/packages/build.particle.wood_chip_dust_burst/media/preview.mp4 +0 -0
  95. package/packages/build.particle.wood_chip_dust_burst/media/preview.webm +0 -0
  96. package/packages/build.particle.wood_chip_dust_burst/media/thumbnail.png +0 -0
  97. package/packages/build.particle.wood_chip_dust_burst/metadata/description.json +0 -173
  98. package/packages/build.particle.wood_chip_dust_burst/provenance/03-effect.original.json +0 -1277
  99. package/packages/build.particle.wood_chip_dust_burst/provenance/03-normalized-vfx-to-babylon-report.json +0 -329
  100. package/packages/build.particle.wood_chip_dust_burst/provenance/04-preview-overrides.json +0 -749
  101. package/packages/build.particle.wood_chip_dust_burst/provenance/04-preview-tuning-report.json +0 -60
  102. package/packages/build.particle.wood_chip_dust_burst/provenance/asset-map.json +0 -146
  103. package/packages/build.particle.wood_chip_dust_burst/provenance/lineage.json +0 -69
  104. package/packages/effects/building-upgrade/effect.json +0 -6
  105. package/packages/effects/building-upgrade/index.ts +0 -387
  106. package/packages/effects/character-entrance/effect.json +0 -6
  107. package/packages/effects/character-entrance/index.ts +0 -308
  108. package/packages/effects/coin-burst/index.ts +0 -43
  109. package/packages/effects/comet-trail-flame/assets/textures/trail_5x4.webp +0 -0
  110. package/packages/effects/comet-trail-flame/index.ts +0 -106
  111. package/packages/effects/conveyor-upgrade/effect.json +0 -6
  112. package/packages/effects/conveyor-upgrade/index.ts +0 -401
  113. package/packages/effects/debris-smoke/assets/models/rock_debris.glb +0 -0
  114. package/packages/effects/debris-smoke/assets/textures/smoke22.webp +0 -0
  115. package/packages/effects/debris-smoke/index.ts +0 -101
  116. package/packages/effects/door_open_gold_flakes/assets/runtime/goldFlakes.v1.js +0 -397
  117. package/packages/effects/door_open_gold_flakes/door_open_gold_flakes.json +0 -498
  118. package/packages/effects/door_open_gold_flakes/index.ts +0 -11
  119. package/packages/effects/electric-arc/assets/textures/spark_4x4.webp +0 -0
  120. package/packages/effects/electric-arc/index.ts +0 -62
  121. package/packages/effects/energy-shield/effect.json +0 -7
  122. package/packages/effects/energy-shield/index.ts +0 -431
  123. package/packages/effects/explosion/assets/textures/baozha29.webp +0 -0
  124. package/packages/effects/explosion/assets/textures/glow01.webp +0 -0
  125. package/packages/effects/explosion/assets/textures/lizi01.webp +0 -0
  126. package/packages/effects/explosion/assets/textures/xulie_lizibaozha02_5x5.webp +0 -0
  127. package/packages/effects/explosion/index.ts +0 -114
  128. package/packages/effects/ground-tile-upgrade/effect.json +0 -6
  129. package/packages/effects/ground-tile-upgrade/index.ts +0 -410
  130. package/packages/effects/hit-flash/index.ts +0 -69
  131. package/packages/effects/laser-beam/assets/textures/laser_glow.webp +0 -0
  132. package/packages/effects/laser-beam/assets/textures/laser_noise.webp +0 -0
  133. package/packages/effects/laser-beam/index.ts +0 -6
  134. package/packages/effects/laser-beam-factory.ts +0 -135
  135. package/packages/effects/lumber-cart-suction/effect.json +0 -6
  136. package/packages/effects/lumber-cart-suction/index.ts +0 -399
  137. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb +0 -0
  138. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb.versions/.active +0 -1
  139. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb.versions/1781589063890_upload.glb +0 -0
  140. package/packages/effects/lumber-debris-scatter/assets/runtime/solidParticleScatter.v1.js +0 -236
  141. package/packages/effects/lumber-debris-scatter/effect.json +0 -228
  142. package/packages/effects/lumber-debris-scatter/index.ts +0 -780
  143. package/packages/effects/lumber_vehicle_gear_spin/assets/runtime/gearSpin.v1.js +0 -202
  144. package/packages/effects/lumber_vehicle_gear_spin/index.ts +0 -11
  145. package/packages/effects/lumber_vehicle_gear_spin/lumber_vehicle_gear_spin.json +0 -338
  146. package/packages/effects/lumber_vehicle_upgrade_aura/assets/runtime/upgradeAura.v1.js +0 -353
  147. package/packages/effects/lumber_vehicle_upgrade_aura/index.ts +0 -11
  148. package/packages/effects/lumber_vehicle_upgrade_aura/lumber_vehicle_upgrade_aura.json +0 -365
  149. package/packages/effects/lumber_vehicle_wood_chips/assets/runtime/solidParticleScatter.v1.js +0 -310
  150. package/packages/effects/lumber_vehicle_wood_chips/index.ts +0 -11
  151. package/packages/effects/lumber_vehicle_wood_chips/lumber_vehicle_wood_chips.json +0 -335
  152. package/packages/effects/monster-hit/index.ts +0 -128
  153. package/packages/effects/player-upgrade/effect.json +0 -6
  154. package/packages/effects/player-upgrade/index.ts +0 -635
  155. package/packages/effects/pulse-laser-volley/assets/textures/bolt_guangshu.webp +0 -0
  156. package/packages/effects/pulse-laser-volley/index.ts +0 -204
  157. package/packages/effects/resource-collect-sparkles/effect.json +0 -6
  158. package/packages/effects/resource-collect-sparkles/index.ts +0 -254
  159. package/packages/effects/run-footstep-dust/assets/runtime/runFootstepDust.v1.js +0 -215
  160. package/packages/effects/run-footstep-dust/effect.json +0 -183
  161. package/packages/effects/run-footstep-dust/index.ts +0 -377
  162. package/packages/effects/space-shared.ts +0 -120
  163. package/packages/effects/thruster-flame/index.ts +0 -305
  164. package/packages/effects/tree-chop-dust/assets/textures/tree_chop_dust_atlas.png +0 -0
  165. package/packages/effects/tree-chop-dust/effect.json +0 -8
  166. package/packages/effects/tree-chop-dust/index.ts +0 -202
  167. package/packages/effects/tree-leaf-chop-burst/assets/runtime/particleEmitter.v1.js +0 -303
  168. package/packages/effects/tree-leaf-chop-burst/assets/textures/tree_leaf_chop_burst_leaf_atlas.png +0 -0
  169. package/packages/effects/tree-leaf-chop-burst/effect.json +0 -498
  170. package/packages/effects/tree-leaf-chop-burst/index.ts +0 -294
  171. package/packages/effects/tree_chop_dust_burst/assets/runtime/particleEmitter.v1.js +0 -318
  172. package/packages/effects/tree_chop_dust_burst/assets/textures/tree_chop_dust_burst_dust.png +0 -0
  173. package/packages/effects/tree_chop_dust_burst/index.ts +0 -13
  174. package/packages/effects/tree_chop_dust_burst/tree_chop_dust_burst.json +0 -412
  175. package/packages/effects/ufo-thread-flame/assets/textures/flame_flow.webp +0 -0
  176. package/packages/effects/ufo-thread-flame/assets/textures/flame_spike.webp +0 -0
  177. package/packages/effects/ufo-thread-flame/index.ts +0 -152
  178. package/packages/effects/vehicle-exhaust-smoke/assets/textures/vehicle_exhaust_smoke_atlas.png +0 -0
  179. package/packages/effects/vehicle-exhaust-smoke/effect.json +0 -8
  180. package/packages/effects/vehicle-exhaust-smoke/index.ts +0 -298
  181. package/packages/effects/vehicle-upgrade/effect.json +0 -6
  182. package/packages/effects/vehicle-upgrade/index.ts +0 -401
  183. package/packages/effects/weapon-trail/assets/textures/trail_wedge.png +0 -0
  184. package/packages/effects/weapon-trail/index.ts +0 -313
  185. package/packages/effects/wood-crusher-sawdust/assets/runtime/particleEmitter.v1.js +0 -312
  186. package/packages/effects/wood-crusher-sawdust/assets/textures/wood_crusher_sawdust_dust.png +0 -0
  187. package/packages/effects/wood-crusher-sawdust/assets/textures/wood_crusher_sawdust_fiber.png +0 -0
  188. package/packages/effects/wood-crusher-sawdust/index.ts +0 -43
  189. package/packages/effects/wood-crusher-sawdust/wood_crusher_sawdust.json +0 -592
  190. package/packages/effects/wood-cutter-sawdust/assets/runtime/particleEmitter.v1.js +0 -312
  191. package/packages/effects/wood-cutter-sawdust/assets/textures/wood_cutter_sawdust_chip.png +0 -0
  192. package/packages/effects/wood-cutter-sawdust/assets/textures/wood_cutter_sawdust_dust.png +0 -0
  193. package/packages/effects/wood-cutter-sawdust/index.ts +0 -43
  194. package/packages/effects/wood-cutter-sawdust/wood_cutter_sawdust.json +0 -592
  195. package/packages/effects/wood-sawdust-effect-factory.ts +0 -194
  196. package/packages/effects/wood_crusher_sawdust/assets/runtime/particleEmitter.v1.js +0 -318
  197. package/packages/effects/wood_crusher_sawdust/assets/textures/wood_crusher_sawdust_dust.png +0 -0
  198. package/packages/effects/wood_crusher_sawdust/assets/textures/wood_crusher_sawdust_fiber.png +0 -0
  199. package/packages/effects/wood_crusher_sawdust/index.ts +0 -29
  200. package/packages/effects/wood_crusher_sawdust/wood_crusher_sawdust.json +0 -592
  201. package/packages/effects/wood_cutter_sawdust/assets/runtime/particleEmitter.v1.js +0 -318
  202. package/packages/effects/wood_cutter_sawdust/assets/textures/wood_cutter_sawdust_chip.png +0 -0
  203. package/packages/effects/wood_cutter_sawdust/assets/textures/wood_cutter_sawdust_dust.png +0 -0
  204. package/packages/effects/wood_cutter_sawdust/index.ts +0 -29
  205. package/packages/effects/wood_cutter_sawdust/wood_cutter_sawdust.json +0 -592
  206. package/packages/effects/wood_peel_material_test/assets/runtime/woodPeelMaterialTest.v1.js +0 -196
  207. package/packages/effects/wood_peel_material_test/index.ts +0 -11
  208. package/packages/effects/wood_peel_material_test/wood_peel_material_test.json +0 -136
  209. package/packages/env.particle.forest_edge_fog/assets/runtime/particleEmitter.v1.js +0 -210
  210. package/packages/env.particle.forest_edge_fog/assets/textures/f12a23c4-b924-4322-a260-3d982428f1e8.png +0 -0
  211. package/packages/env.particle.forest_edge_fog/assets/textures/yuan.png +0 -0
  212. package/packages/env.particle.forest_edge_fog/effect.json +0 -854
  213. package/packages/env.particle.forest_edge_fog/media/preview.gif +0 -0
  214. package/packages/env.particle.forest_edge_fog/media/preview.mp4 +0 -0
  215. package/packages/env.particle.forest_edge_fog/media/preview.webm +0 -0
  216. package/packages/env.particle.forest_edge_fog/media/thumbnail.png +0 -0
  217. package/packages/env.particle.forest_edge_fog/metadata/description.json +0 -148
  218. package/packages/env.particle.forest_edge_fog/provenance/03-effect.original.json +0 -827
  219. package/packages/env.particle.forest_edge_fog/provenance/03-normalized-vfx-to-babylon-report.json +0 -196
  220. package/packages/env.particle.forest_edge_fog/provenance/04-preview-overrides.json +0 -432
  221. package/packages/env.particle.forest_edge_fog/provenance/04-preview-tuning-report.json +0 -28
  222. package/packages/env.particle.forest_edge_fog/provenance/asset-map.json +0 -124
  223. package/packages/env.particle.forest_edge_fog/provenance/lineage.json +0 -41
  224. package/packages/generation.ts +0 -14
  225. package/packages/index.ts +0 -14
  226. package/packages/legacy-particle-runtime.ts +0 -113
  227. package/packages/manifestRuntimePackage.ts +0 -519
  228. package/packages/registry.ts +0 -85
  229. package/packages/rendering.ts +0 -13
  230. package/packages/resource.particle.viscous_oil_suction/assets/runtime/top_disk_suction/particleEmitter.v1.js +0 -424
  231. package/packages/resource.particle.viscous_oil_suction/assets/textures/Glow ad.png +0 -0
  232. package/packages/resource.particle.viscous_oil_suction/effect.json +0 -1254
  233. package/packages/resource.particle.viscous_oil_suction/media/preview.gif +0 -0
  234. package/packages/resource.particle.viscous_oil_suction/media/preview.mp4 +0 -0
  235. package/packages/resource.particle.viscous_oil_suction/media/preview.webm +0 -0
  236. package/packages/resource.particle.viscous_oil_suction/media/thumbnail.png +0 -0
  237. package/packages/resource.particle.viscous_oil_suction/metadata/description.json +0 -122
  238. package/packages/resource.particle.viscous_oil_suction/provenance/03-effect.original.json +0 -1051
  239. package/packages/resource.particle.viscous_oil_suction/provenance/03-normalized-vfx-to-babylon-report.json +0 -200
  240. package/packages/resource.particle.viscous_oil_suction/provenance/04-preview-overrides.json +0 -581
  241. package/packages/resource.particle.viscous_oil_suction/provenance/04-preview-tuning-report.json +0 -28
  242. package/packages/resource.particle.viscous_oil_suction/provenance/asset-map.json +0 -111
  243. package/packages/resource.particle.viscous_oil_suction/provenance/lineage.json +0 -45
  244. package/packages/types.ts +0 -107
  245. package/tooling/check-dist-runtime.mjs +0 -166
  246. package/tooling/generate-index.mjs +0 -55
  247. package/tooling/test-guardrails.mjs +0 -157
  248. package/tooling/validate-boundary.mjs +0 -92
  249. package/tooling/validate-effects.mjs +0 -304
  250. package/tooling/validate.mjs +0 -53
package/dist/index.js CHANGED
@@ -111,20 +111,24 @@ class EffectPackageService {
111
111
  const params = this.getMergedParams(effectId, explicitParams);
112
112
  if (!params) return null;
113
113
  const spawn = this.resolveSpawnTransform(effectPackage, params, spawnTransform);
114
+ warnMissingRequiredInputs(effectId, effectPackage.requiredInputs, spawn.inputs);
114
115
  const runtimeParams = stripSpawnParams(params, effectPackage.spawnParams);
115
116
  const root = new TransformNode(`vfx_package_${effectId}`, this.scene);
116
- root.position.copyFrom(spawn.position);
117
- root.rotation.y = spawn.rotationY;
117
+ applySpawnToRoot(root, spawn);
118
+ const worldPosition = root.getAbsolutePosition().clone();
118
119
  let handle;
119
120
  try {
120
121
  handle = effectPackage.create({
121
122
  scene: this.scene,
122
123
  root,
123
- position: spawn.position.clone(),
124
+ position: worldPosition,
124
125
  rotationY: spawn.rotationY,
125
126
  renderingGroupId: spawn.renderingGroupId,
126
- params: runtimeParams
127
+ params: runtimeParams,
128
+ scale: spawn.scale,
129
+ inputs: spawn.inputs
127
130
  });
131
+ applySpawnToRoot(root, spawn);
128
132
  } catch (error) {
129
133
  console.warn(`[EffectPackageService] effect "${effectId}" create() failed; skipped.`, error);
130
134
  try {
@@ -139,6 +143,9 @@ class EffectPackageService {
139
143
  this.activeHandles.delete(handle);
140
144
  originalDispose();
141
145
  };
146
+ handle.root.onDisposeObservable.addOnce(() => {
147
+ this.activeHandles.delete(handle);
148
+ });
142
149
  return handle;
143
150
  }
144
151
  dispose() {
@@ -157,26 +164,87 @@ class EffectPackageService {
157
164
  }
158
165
  resolveSpawnTransform(effectPackage, params, spawnTransform) {
159
166
  const normalized = normalizeSpawnTransform(spawnTransform);
160
- const basePosition = normalized.position?.clone() ?? this.resolveDebugPosition(effectPackage);
161
- const rotationY = readFiniteNumber(normalized.rotationY, 0);
167
+ const parent = normalized.parent ?? null;
168
+ const basePosition = normalized.position?.clone() ?? (parent != null ? new Vector3(0, 0, 0) : this.resolveDebugPosition(effectPackage));
169
+ const rotation = resolveRotation(normalized.rotation, normalized.rotationY);
170
+ const scale = resolveScale(normalized.scale);
171
+ const offsetIsLocal = normalized.offsetIsLocal ?? parent != null;
162
172
  const renderingGroupId = readOptionalFiniteNumber(normalized.renderingGroupId);
163
173
  const offset = readSpawnOffset(params, effectPackage.spawnParams);
164
- if (offset.lengthSquared() <= 1e-7) {
165
- return { position: basePosition, rotationY, renderingGroupId };
166
- }
167
- const cos = Math.cos(rotationY);
168
- const sin = Math.sin(rotationY);
169
- return {
170
- position: basePosition.addInPlace(new Vector3(
174
+ let position = basePosition;
175
+ if (offset.lengthSquared() > 1e-7) {
176
+ const cos = Math.cos(rotation.y);
177
+ const sin = Math.sin(rotation.y);
178
+ position = basePosition.addInPlace(new Vector3(
171
179
  offset.x * cos - offset.z * sin,
172
180
  offset.y,
173
181
  offset.x * sin + offset.z * cos
174
- )),
175
- rotationY,
176
- renderingGroupId
177
- };
182
+ ));
183
+ }
184
+ return { position, rotation, rotationY: rotation.y, scale, parent, offsetIsLocal, renderingGroupId, inputs: normalizeInputs(normalized.inputs) };
185
+ }
186
+ }
187
+ function normalizeInputs(inputs) {
188
+ if (!inputs) return void 0;
189
+ const asFn = (v) => v == null ? void 0 : typeof v === "function" ? v : () => v;
190
+ const out = {};
191
+ if (inputs.aim != null) out.aim = asFn(inputs.aim);
192
+ if (inputs.normal != null) out.normal = asFn(inputs.normal);
193
+ if (inputs.endpoints) out.endpoints = inputs.endpoints.map((e) => asFn(e)).filter(Boolean);
194
+ if (inputs.target) out.target = inputs.target;
195
+ if (inputs.scalars) {
196
+ out.scalars = {};
197
+ for (const [k, v] of Object.entries(inputs.scalars)) {
198
+ const fn = asFn(v);
199
+ if (fn) out.scalars[k] = fn;
200
+ }
201
+ }
202
+ return out;
203
+ }
204
+ function warnMissingRequiredInputs(effectId, required, inputs) {
205
+ if (!required || required.length === 0) return;
206
+ const missing = required.filter((k) => {
207
+ const v = inputs ? inputs[k] : void 0;
208
+ return v == null || k === "endpoints" && Array.isArray(v) && v.length < 2;
209
+ });
210
+ if (missing.length > 0) {
211
+ console.warn(`[EffectPackageService] effect "${effectId}" 声明 requiredInputs [${missing.join(", ")}] 但未提供 → 可能静默降级。`);
178
212
  }
179
213
  }
214
+ function applySpawnToRoot(root, spawn) {
215
+ if (spawn.parent) root.parent = spawn.parent;
216
+ root.rotation.copyFrom(spawn.rotation);
217
+ root.scaling.copyFrom(spawn.scale);
218
+ if (spawn.parent && !spawn.offsetIsLocal) {
219
+ root.computeWorldMatrix(true);
220
+ root.setAbsolutePosition(spawn.position);
221
+ } else {
222
+ root.position.copyFrom(spawn.position);
223
+ }
224
+ root.computeWorldMatrix(true);
225
+ }
226
+ function resolveRotation(rotation, rotationY) {
227
+ if (rotation) {
228
+ return new Vector3(
229
+ readFiniteNumber(rotation.x, 0),
230
+ readFiniteNumber(rotation.y, 0),
231
+ readFiniteNumber(rotation.z, 0)
232
+ );
233
+ }
234
+ return new Vector3(0, readFiniteNumber(rotationY, 0), 0);
235
+ }
236
+ function resolveScale(scale) {
237
+ if (scale == null) return new Vector3(1, 1, 1);
238
+ if (typeof scale === "number") {
239
+ const s2 = readFiniteNumber(scale, 1);
240
+ return new Vector3(s2, s2, s2);
241
+ }
242
+ return new Vector3(
243
+ readFiniteNumber(scale.x, 1),
244
+ readFiniteNumber(scale.y, 1),
245
+ readFiniteNumber(scale.z, 1)
246
+ );
247
+ }
180
248
  function normalizeSpawnTransform(spawnTransform) {
181
249
  if (!spawnTransform) return {};
182
250
  if (spawnTransform instanceof Vector3) return { position: spawnTransform };
package/dist/types.d.ts CHANGED
@@ -1,7 +1,27 @@
1
1
  import { Scene } from '@babylonjs/core/scene';
2
2
  import { Vector3 } from '@babylonjs/core/Maths/math.vector';
3
3
  import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
4
+ import { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh';
4
5
  export type VfxDebugAnchor = 'playerRight' | 'player';
6
+ export type VfxInput<T> = T | (() => T);
7
+ export type VfxInputKey = 'aim' | 'normal' | 'endpoints' | 'target';
8
+ /** 调用方提供的 inputs(放在 VfxSpawnTransform.inputs)。 */
9
+ export interface VfxEffectInputs {
10
+ aim?: VfxInput<Vector3>;
11
+ normal?: VfxInput<Vector3>;
12
+ endpoints?: Array<VfxInput<Vector3>>;
13
+ target?: AbstractMesh;
14
+ /** 运行时标量(throttle/intensity 等),值可静态或 provider;特效每帧读取做活改(G5)。 */
15
+ scalars?: Record<string, VfxInput<number>>;
16
+ }
17
+ /** create() 收到的、已归一化为访问器的 inputs(provider 与静态值都变成可调用的函数)。 */
18
+ export interface VfxResolvedInputs {
19
+ aim?: () => Vector3;
20
+ normal?: () => Vector3;
21
+ endpoints?: Array<() => Vector3>;
22
+ target?: AbstractMesh;
23
+ scalars?: Record<string, () => number>;
24
+ }
5
25
  export type VfxParamValue = number | string | VfxColorValue;
6
26
  export type VfxParamValues = Record<string, VfxParamValue>;
7
27
  export interface VfxColorValue {
@@ -41,11 +61,21 @@ export interface VfxEffectPackageContext<PParams extends VfxParamValues = VfxPar
41
61
  rotationY: number;
42
62
  renderingGroupId?: number;
43
63
  params: PParams;
64
+ /**
65
+ * 解析后的三轴缩放(来自 spawnTransform.scale,默认 (1,1,1))。
66
+ * root 的缩放由 service 写好;但**几何不挂 root 的 world/detached 特效**(粒子/抛物池/世界坐标 emitter)
67
+ * root.scaling 作用不到,须自行把 ctx.scale 乘进几何尺寸/初速(G3)。
68
+ */
69
+ scale: Vector3;
70
+ /** 运行时外部信息(已归一化为访问器)。link/target/定向武器/活改标量等按 required/optionalInputs 读取。 */
71
+ inputs?: VfxResolvedInputs;
44
72
  }
45
73
  export interface VfxEffectHandle {
46
74
  root: TransformNode;
47
75
  ready?: Promise<void>;
48
76
  dispose(): void;
77
+ /** 可选:运行时活改 params(无须 dispose+replay,避免重置缓动/可见闪)。声明即纳入契约(G5)。 */
78
+ update?(params: Partial<VfxParamValues>): void;
49
79
  }
50
80
  export interface VfxEffectManifest {
51
81
  id: string;
@@ -58,10 +88,34 @@ export interface VfxSpawnParamKeys {
58
88
  offsetY?: string;
59
89
  offsetZ?: string;
60
90
  }
91
+ /** 欧拉角(弧度)/ 三轴缩放的纯数据三元组。 */
92
+ export interface VfxVector3Like {
93
+ x: number;
94
+ y: number;
95
+ z: number;
96
+ }
61
97
  export interface VfxSpawnTransform {
62
98
  position?: Vector3;
99
+ /**
100
+ * 兼容别名:绕 Y 轴旋转(弧度)。
101
+ * 若同时给了 `rotation`,以 `rotation` 为准;否则回退到 `rotationY`。
102
+ */
63
103
  rotationY?: number;
104
+ /** 完整欧拉角旋转(弧度);给了就覆盖 `rotationY`。 */
105
+ rotation?: VfxVector3Like;
106
+ /** 等比缩放(number)或三轴缩放;默认 1。 */
107
+ scale?: number | VfxVector3Like;
108
+ /** 挂载点节点;给了就把特效 root parent 到它(特效随之跟随)。 */
109
+ parent?: TransformNode;
110
+ /**
111
+ * position/rotation/scale 的坐标系解释:
112
+ * - 给了 `parent` 时默认 `true`:按相对 parent 的 **local** transform 解释;
113
+ * - 没给 `parent` 时默认 `false`:按 **世界坐标** 解释(保持现状行为)。
114
+ */
115
+ offsetIsLocal?: boolean;
64
116
  renderingGroupId?: number;
117
+ /** 运行时外部信息(瞄准点/法线/端点/目标 mesh);值可为静态或 provider(动态每帧取)。 */
118
+ inputs?: VfxEffectInputs;
65
119
  }
66
120
  export interface VfxParamsFile {
67
121
  schemaVersion: string;
@@ -69,6 +123,15 @@ export interface VfxParamsFile {
69
123
  updatedAt?: string | null;
70
124
  params?: Partial<VfxParamValues>;
71
125
  }
126
+ /**
127
+ * 特效的放置本质(内禀标记)。决定系统/binding/面板怎么放它,不靠猜。见 docs/vfx-placement-design.md。
128
+ * - 'socket' local:挂对象命名挂点、随它走(parent + follow)。
129
+ * - 'world' global:放世界某点、不跟随(fire-and-forget,可按法线定向)。
130
+ * - 'screen' global:屏幕/相机层(后处理/相机扰动/DOM 全屏),无 3D transform。
131
+ * - 'target' 改目标自身材质/渲染态,没有可 spawn 的 root。
132
+ * - 'link' 连 ≥2 个端点(每帧重建几何),单 parent 表达不了。
133
+ */
134
+ export type VfxPlacement = 'socket' | 'world' | 'screen' | 'target' | 'link';
72
135
  export interface VfxEffectPackage<PParams extends VfxParamValues = VfxParamValues> {
73
136
  id: string;
74
137
  nameZh: string;
@@ -78,6 +141,23 @@ export interface VfxEffectPackage<PParams extends VfxParamValues = VfxParamValue
78
141
  spawnParams?: VfxSpawnParamKeys;
79
142
  savedParams?: Partial<VfxParamValues>;
80
143
  debugParams: VfxParamDefinition[];
144
+ /** 放置本质标记(默认 'world')。 */
145
+ placement?: VfxPlacement;
146
+ /** 声明**必需**的运行时 inputs(link 必须给 endpoints/aim、target 类必须给 target mesh);service 缺失时 warn。 */
147
+ requiredInputs?: VfxInputKey[];
148
+ /** 声明**可选**的运行时 inputs(给了就用、不给走默认,如定向武器的 aim:给了朝目标、不给走挂点朝向);不触发缺失 warn(G2)。 */
149
+ optionalInputs?: VfxInputKey[];
150
+ /**
151
+ * 几何空间:'local'=几何挂 root 子节点随它刚性变换;'world'=几何用世界坐标、root 仅作每帧采样源(拖尾)。
152
+ * 默认随 placement 推断('socket'→local,其余→world)。
153
+ */
154
+ geometrySpace?: 'local' | 'world';
155
+ /**
156
+ * 构建特效。**约定(G6):root 的 transform(position/rotation/scaling/parent)归 service 独占** ——
157
+ * service 在 create() 后会二次 `applySpawnToRoot` 覆盖,create 内写 `root.position/rotation/scaling` 会被静默清掉。
158
+ * 需要内部局部偏移(如抬升)请用 root 下的**子 TransformNode** 承载,或声明 `spawnParams.offsetY` 走 service 存活通道。
159
+ * 几何挂 root 子树即随挂载跟随;world/拖尾类几何走世界坐标、读 ctx.scale 缩放。
160
+ */
81
161
  create(context: VfxEffectPackageContext<PParams>): VfxEffectHandle;
82
162
  }
83
163
  export type VfxDebugOverrides = Record<string, Partial<VfxParamValues>>;
package/index.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": "vfx-index/1.0",
3
- "count": 36,
3
+ "count": 37,
4
4
  "effects": [
5
5
  {
6
6
  "id": "build.particle.lumber_debris_scatter",
@@ -267,6 +267,24 @@
267
267
  "media": {},
268
268
  "path": "packages/effects/thruster-flame"
269
269
  },
270
+ {
271
+ "id": "treasure-attention",
272
+ "dir": "treasure-attention",
273
+ "nameZh": "宝藏注意力光效",
274
+ "kind": "code",
275
+ "category": "treasure-attention",
276
+ "tags": [
277
+ "treasure",
278
+ "attention",
279
+ "pickup",
280
+ "gold",
281
+ "sparkle",
282
+ "aura",
283
+ "ring"
284
+ ],
285
+ "media": {},
286
+ "path": "packages/effects/treasure-attention"
287
+ },
270
288
  {
271
289
  "id": "tree_chop_dust_burst",
272
290
  "dir": "tree_chop_dust_burst",
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@fps-games/vfx",
3
- "version": "0.1.1",
3
+ "version": "0.3.3",
4
4
  "type": "module",
5
5
  "description": "Shared VFX library for Playable projects: engine-injected effect packages + runtime. Project-pure (no imports of host project code).",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
6
9
  "types": "./dist/index.d.ts",
7
10
  "module": "./dist/index.js",
8
11
  "exports": {
@@ -18,9 +21,10 @@
18
21
  },
19
22
  "files": [
20
23
  "dist",
21
- "tooling",
22
- "packages",
23
- "docs",
24
+ "tooling/vfx-cli.mjs",
25
+ "tooling/vfx-package-resolution.mjs",
26
+ "packages/effects/*/vfx-params.json",
27
+ "README.md",
24
28
  "CHANGELOG.md",
25
29
  "index.json"
26
30
  ],
@@ -38,6 +42,7 @@
38
42
  "test:guardrails": "node tooling/test-guardrails.mjs",
39
43
  "generate:index": "node tooling/generate-index.mjs",
40
44
  "demo:energy-shield": "vite demo/energy-shield --host 127.0.0.1",
45
+ "demo:treasure-attention": "vite demo/treasure-attention --host 127.0.0.1",
41
46
  "release": "node scripts/release.mjs"
42
47
  },
43
48
  "//": "分发=git tag + 提交 dist(做法A):发版前 pnpm build → 提交 dist/ → git tag。消费端 import '@fps/vfx' 解析到 dist;本地开发可用 FPS_VFX_LIBRARY_REPO 切回源码 alias。",
@@ -0,0 +1,49 @@
1
+ {
2
+ "schemaVersion": "vfx-params/1.0",
3
+ "effectId": "treasure-attention",
4
+ "params": {
5
+ "radius": 1.15,
6
+ "yOffset": 0.05,
7
+ "height": 1.35,
8
+ "rays": 9,
9
+ "rayWidth": 0.34,
10
+ "spinSpeed": 0.85,
11
+ "color": {
12
+ "r": 1,
13
+ "g": 0.9,
14
+ "b": 0.22
15
+ },
16
+ "auraColor": {
17
+ "r": 1,
18
+ "g": 0.82,
19
+ "b": 0.18
20
+ },
21
+ "ringColor": {
22
+ "r": 1,
23
+ "g": 0.95,
24
+ "b": 0.38
25
+ },
26
+ "rayColor": {
27
+ "r": 1,
28
+ "g": 0.76,
29
+ "b": 0.12
30
+ },
31
+ "beamColor": {
32
+ "r": 1,
33
+ "g": 0.92,
34
+ "b": 0.32
35
+ },
36
+ "opacity": 1,
37
+ "auraOpacity": 0.82,
38
+ "ringOpacity": 0.95,
39
+ "beamOpacity": 0.68,
40
+ "particleRate": 42,
41
+ "particleLife": 0.82,
42
+ "sparkleMinSize": 0.045,
43
+ "sparkleMaxSize": 0.13,
44
+ "riseSpeed": 0.72,
45
+ "drift": 0.12,
46
+ "bloomIntensity": 0.55,
47
+ "bloomKernelSize": 32
48
+ }
49
+ }
@@ -17,6 +17,9 @@ import { fileURLToPath } from 'node:url';
17
17
  const LIB_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
18
18
  const LIB_EFFECTS = join(LIB_ROOT, 'packages', 'effects');
19
19
  const LIB_VERSION = (() => { try { return JSON.parse(readFileSync(join(LIB_ROOT, 'package.json'), 'utf8')).version; } catch { return '0.0.0'; } })();
20
+ const LIB_INDEX = (() => {
21
+ try { return JSON.parse(readFileSync(join(LIB_ROOT, 'index.json'), 'utf8')); } catch { return null; }
22
+ })();
20
23
 
21
24
  const [, , cmd, ...rest] = process.argv;
22
25
  const VALUED_FLAGS = new Set(['--project', '--to']); // 这些 flag 后跟一个值,值不算 positional
@@ -33,10 +36,14 @@ const force = flags.has('--force');
33
36
 
34
37
  // ── 通用 ──────────────────────────────────────────────────────────────────────
35
38
  function libEffectIds() {
39
+ if (Array.isArray(LIB_INDEX?.effects)) return LIB_INDEX.effects.map((e) => e.id).filter(Boolean);
36
40
  return readdirSync(LIB_EFFECTS).filter((e) => {
37
41
  try { return statSync(join(LIB_EFFECTS, e)).isDirectory() && existsSync(join(LIB_EFFECTS, e, 'index.ts')); } catch { return false; }
38
42
  });
39
43
  }
44
+ function hasLibEffect(id) {
45
+ return libEffectIds().includes(id) || existsSync(join(LIB_EFFECTS, id, 'vfx-params.json'));
46
+ }
40
47
  function stub(id) {
41
48
  return `// @fps/vfx re-export 桩(代码/资源在包内;本地仅 vfx-params.json + 此桩)。\nexport * from '@fps/vfx/effects/${id}';\n`;
42
49
  }
@@ -111,7 +118,7 @@ function changelogRange(fromVersion, toVersion) {
111
118
 
112
119
  // ── add ───────────────────────────────────────────────────────────────────────
113
120
  function add(id) {
114
- if (!existsSync(join(LIB_EFFECTS, id))) { console.error(`✗ 库中无特效 "${id}"。可用:run "list"。`); process.exit(1); }
121
+ if (!hasLibEffect(id)) { console.error(`✗ 库中无特效 "${id}"。可用:run "list"。`); process.exit(1); }
115
122
  const destDir = join(effectsDestDir(), id);
116
123
  const stubPath = join(destDir, 'index.ts');
117
124
  const paramsPath = join(destDir, 'vfx-params.json');