@fps-games/vfx 0.3.15 → 0.3.16

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
@@ -12,6 +12,14 @@
12
12
 
13
13
  <!-- release.mjs 在此行下方插入新版本区块 -->
14
14
 
15
+ ## v0.3.16 — 2026-07-08
16
+
17
+ ### 改动
18
+ - feat(pulse-laser-volley-sequence): add single-shot pulse laser variant
19
+
20
+ ### 特效
21
+ - 新增:pulse-laser-volley-sequence
22
+
15
23
  ## v0.3.11 — 2026-07-07
16
24
 
17
25
  ### 改动
@@ -0,0 +1,14 @@
1
+ import { VfxColorValue, VfxEffectPackage, VfxParamValues } from '../../types';
2
+ interface PulseLaserParams extends VfxParamValues {
3
+ lifetimeMode: string;
4
+ lifetime: number;
5
+ targetDistance: number;
6
+ boltSpeed: number;
7
+ boltLen: number;
8
+ boltColor: VfxColorValue;
9
+ boltCore: number;
10
+ boltCorePow: number;
11
+ boltBright: number;
12
+ }
13
+ export declare const pulseLaserVolleySequenceEffectPackage: VfxEffectPackage<PulseLaserParams>;
14
+ export {};
@@ -0,0 +1,142 @@
1
+ import { CreatePlane } from "@babylonjs/core/Meshes/Builders/planeBuilder";
2
+ import { ShaderMaterial } from "@babylonjs/core/Materials/shaderMaterial";
3
+ import { Effect } from "@babylonjs/core/Materials/effect";
4
+ import { Texture } from "@babylonjs/core/Materials/Textures/texture";
5
+ import { Color3 } from "@babylonjs/core/Maths/math.color";
6
+ import { Vector3 } from "@babylonjs/core/Maths/math.vector";
7
+ import { Space } from "@babylonjs/core/Maths/math.axis";
8
+ import { Constants } from "@babylonjs/core/Engines/constants";
9
+ import { L as LIFETIME_DEBUG_PARAMS, a as LIFETIME_DEFAULTS, s as scheduleLifetime } from "../../chunks/space-shared-xh5IOcLH.js";
10
+ const boltTextureUrl = "data:image/webp;base64,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";
11
+ const DEFAULT_PARAMS = {
12
+ ...LIFETIME_DEFAULTS,
13
+ targetDistance: 12,
14
+ boltSpeed: 26,
15
+ boltLen: 1,
16
+ boltColor: { r: 0.071, g: 0.408, b: 1 },
17
+ // #1268ff
18
+ boltCore: 0.3,
19
+ boltCorePow: 3,
20
+ boltBright: 1.3
21
+ };
22
+ const BOLT_LEN = 2, BOLT_W = 0.34;
23
+ const BOLT_VERTEX = `precision highp float;
24
+ attribute vec3 position; attribute vec2 uv;
25
+ uniform mat4 worldViewProjection;
26
+ varying vec2 vUV;
27
+ void main() { vUV = uv; gl_Position = worldViewProjection * vec4(position, 1.0); }`;
28
+ const BOLT_FRAGMENT = `precision highp float;
29
+ varying vec2 vUV;
30
+ uniform sampler2D tex;
31
+ uniform vec3 tint;
32
+ uniform float coreStr; uniform float corePow; uniform float bright;
33
+ void main() {
34
+ vec4 t = texture2D(tex, vUV);
35
+ float core = pow(t.a, corePow) * coreStr;
36
+ vec3 col = mix(tint, vec3(1.0), core) * bright;
37
+ gl_FragColor = vec4(col, t.a);
38
+ }`;
39
+ let shadersRegistered = false;
40
+ function registerShaders() {
41
+ if (shadersRegistered) return;
42
+ Effect.ShadersStore["vfxPulseBoltVertexShader"] = BOLT_VERTEX;
43
+ Effect.ShadersStore["vfxPulseBoltFragmentShader"] = BOLT_FRAGMENT;
44
+ shadersRegistered = true;
45
+ }
46
+ function colorVec(c) {
47
+ return new Color3(c.r, c.g, c.b);
48
+ }
49
+ function excludeFromGlow(scene, mesh) {
50
+ for (const layer of scene.effectLayers ?? []) {
51
+ const add = layer.addExcludedMesh;
52
+ if (typeof add === "function") add.call(layer, mesh);
53
+ }
54
+ }
55
+ const pulseLaserVolleySequenceEffectPackage = {
56
+ id: "pulse-laser-volley-sequence",
57
+ nameZh: "脉冲激光(单发)",
58
+ placement: "socket",
59
+ optionalInputs: ["aim"],
60
+ defaultParams: DEFAULT_PARAMS,
61
+ debugAnchor: "playerRight",
62
+ debugParams: [
63
+ ...LIFETIME_DEBUG_PARAMS,
64
+ { key: "targetDistance", label: "目标距离", kind: "number", min: 2, max: 40, step: 0.5 },
65
+ { key: "boltSpeed", label: "光弹速度", kind: "number", min: 5, max: 60, step: 1 },
66
+ { key: "boltLen", label: "光弹长度", kind: "number", min: 0.3, max: 4, step: 0.05 },
67
+ { key: "boltColor", label: "光弹颜色", kind: "color" },
68
+ { key: "boltCore", label: "白芯强度", kind: "number", min: 0, max: 1, step: 0.01 },
69
+ { key: "boltCorePow", label: "白芯收窄", kind: "number", min: 0.5, max: 8, step: 0.1 },
70
+ { key: "boltBright", label: "光弹亮度", kind: "number", min: 0.2, max: 3, step: 0.05 }
71
+ ],
72
+ create({ scene, root, position, rotationY, params, inputs }) {
73
+ root.position.copyFrom(position);
74
+ root.rotation.y = rotationY;
75
+ registerShaders();
76
+ const effectiveDist = () => {
77
+ if (!inputs?.aim) return params.targetDistance;
78
+ const tgt = inputs.aim();
79
+ root.lookAt(tgt, 0, 0, 0, Space.WORLD);
80
+ return Vector3.Distance(root.getAbsolutePosition(), tgt);
81
+ };
82
+ const boltTex = new Texture(boltTextureUrl, scene);
83
+ boltTex.hasAlpha = true;
84
+ const boltMat = new ShaderMaterial("vfx_pulse_boltMat", scene, { vertex: "vfxPulseBolt", fragment: "vfxPulseBolt" }, {
85
+ attributes: ["position", "uv"],
86
+ uniforms: ["worldViewProjection", "tint", "coreStr", "corePow", "bright"],
87
+ samplers: ["tex"],
88
+ needAlphaBlending: true
89
+ });
90
+ boltMat.setTexture("tex", boltTex);
91
+ boltMat.backFaceCulling = false;
92
+ boltMat.disableDepthWrite = true;
93
+ boltMat.alphaMode = Constants.ALPHA_ADD;
94
+ boltMat.setColor3("tint", colorVec(params.boltColor));
95
+ boltMat.setFloat("coreStr", params.boltCore);
96
+ boltMat.setFloat("corePow", params.boltCorePow);
97
+ boltMat.setFloat("bright", params.boltBright);
98
+ const mesh = CreatePlane("vfx_pulse_single_bolt", { width: BOLT_W, height: BOLT_LEN }, scene);
99
+ mesh.rotation.x = Math.PI / 2;
100
+ mesh.material = boltMat;
101
+ mesh.isPickable = false;
102
+ mesh.parent = root;
103
+ mesh.setEnabled(false);
104
+ excludeFromGlow(scene, mesh);
105
+ const bolt = { mesh, left: 0 };
106
+ let fired = false;
107
+ const observer = scene.onBeforeRenderObservable.add(() => {
108
+ const dt = Math.min(scene.getEngine().getDeltaTime() / 1e3, 0.05);
109
+ const dist = effectiveDist();
110
+ if (!fired) {
111
+ bolt.mesh.position.set(0, 0, 0);
112
+ bolt.mesh.scaling.z = params.boltLen / BOLT_LEN;
113
+ bolt.left = dist / params.boltSpeed;
114
+ bolt.mesh.setEnabled(true);
115
+ fired = true;
116
+ }
117
+ const step = params.boltSpeed * dt;
118
+ if (bolt.left > 0) {
119
+ bolt.left -= dt;
120
+ bolt.mesh.position.z += step;
121
+ if (bolt.left <= 0) bolt.mesh.setEnabled(false);
122
+ }
123
+ });
124
+ let disposed = false;
125
+ let ltTimer = 0;
126
+ const dispose = () => {
127
+ if (disposed) return;
128
+ disposed = true;
129
+ if (ltTimer) window.clearTimeout(ltTimer);
130
+ scene.onBeforeRenderObservable.remove(observer);
131
+ bolt.mesh.dispose();
132
+ boltMat.dispose();
133
+ boltTex.dispose();
134
+ root.dispose();
135
+ };
136
+ ltTimer = scheduleLifetime(params, dispose);
137
+ return { root, dispose };
138
+ }
139
+ };
140
+ export {
141
+ pulseLaserVolleySequenceEffectPackage
142
+ };
package/index.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": "vfx-index/1.0",
3
- "count": 40,
3
+ "count": 41,
4
4
  "effects": [
5
5
  {
6
6
  "id": "build.particle.lumber_debris_scatter",
@@ -277,6 +277,16 @@
277
277
  "media": {},
278
278
  "path": "packages/effects/pulse-laser-volley"
279
279
  },
280
+ {
281
+ "id": "pulse-laser-volley-sequence",
282
+ "dir": "pulse-laser-volley-sequence",
283
+ "nameZh": "pulse-laser-volley-sequence",
284
+ "kind": "code",
285
+ "category": null,
286
+ "tags": [],
287
+ "media": {},
288
+ "path": "packages/effects/pulse-laser-volley-sequence"
289
+ },
280
290
  {
281
291
  "id": "resource-collect-sparkles",
282
292
  "dir": "resource-collect-sparkles",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fps-games/vfx",
3
- "version": "0.3.15",
3
+ "version": "0.3.16",
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
6
  "publishConfig": {
@@ -0,0 +1,5 @@
1
+ {
2
+ "schemaVersion": "vfx-params/1.0",
3
+ "effectId": "pulse-laser-volley-sequence",
4
+ "params": {}
5
+ }