Spaces:
Running
Running
Codex CLI
commited on
Commit
·
ed36a74
1
Parent(s):
09a3a37
feat(fx, pickups, projectiles): remove dynamic lights to optimize performance and shader stability
Browse files- src/fx.js +5 -24
- src/pickups.js +6 -14
- src/projectiles.js +3 -4
src/fx.js
CHANGED
|
@@ -42,11 +42,8 @@ export function spawnMuzzleFlash() {
|
|
| 42 |
quad.renderOrder = 11;
|
| 43 |
G.scene.add(quad);
|
| 44 |
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
G.scene.add(light);
|
| 48 |
-
|
| 49 |
-
G.fx.flashes.push({ mesh: quad, light, life: CFG.fx.muzzleLife });
|
| 50 |
}
|
| 51 |
|
| 52 |
export function spawnMuzzleFlashAt(worldPos, color = 0xffc060) {
|
|
@@ -59,11 +56,7 @@ export function spawnMuzzleFlashAt(worldPos, color = 0xffc060) {
|
|
| 59 |
quad.renderOrder = 11;
|
| 60 |
G.scene.add(quad);
|
| 61 |
|
| 62 |
-
|
| 63 |
-
light.position.copy(worldPos);
|
| 64 |
-
G.scene.add(light);
|
| 65 |
-
|
| 66 |
-
G.fx.flashes.push({ mesh: quad, light, life: CFG.fx.muzzleLife });
|
| 67 |
}
|
| 68 |
|
| 69 |
// Quick, subtle dust puff at a world position
|
|
@@ -97,12 +90,9 @@ export function spawnPortalAt(worldPos, color = 0xff5522, size = 1.1, life = 0.3
|
|
| 97 |
flare.position.copy(worldPos);
|
| 98 |
flare.lookAt(G.camera.position);
|
| 99 |
flare.renderOrder = 12;
|
| 100 |
-
const light = new THREE.PointLight(color, 5, size * 6, 2);
|
| 101 |
-
light.position.copy(worldPos);
|
| 102 |
G.scene.add(ring);
|
| 103 |
G.scene.add(flare);
|
| 104 |
-
G.
|
| 105 |
-
G.fx.portals.push({ ring, flare, light, life, maxLife: life, rot: Math.random() * Math.PI * 2 });
|
| 106 |
}
|
| 107 |
|
| 108 |
// Grenade explosion: additive glow sphere + shock ring + light
|
|
@@ -118,12 +108,9 @@ export function spawnExplosionAt(worldPos, radius = 6) {
|
|
| 118 |
);
|
| 119 |
ring.position.copy(worldPos);
|
| 120 |
ring.rotation.x = Math.PI / 2;
|
| 121 |
-
const light = new THREE.PointLight(0xffa050, 9, radius * 3.5, 2);
|
| 122 |
-
light.position.copy(worldPos);
|
| 123 |
G.scene.add(glow);
|
| 124 |
G.scene.add(ring);
|
| 125 |
-
G.
|
| 126 |
-
G.explosions.push({ glow, ring, light, life: 0.5, maxLife: 0.5 });
|
| 127 |
}
|
| 128 |
|
| 129 |
export function updateFX(delta) {
|
|
@@ -155,10 +142,8 @@ export function updateFX(delta) {
|
|
| 155 |
m.life -= delta;
|
| 156 |
m.mesh.material.opacity = Math.max(0, m.life / CFG.fx.muzzleLife);
|
| 157 |
m.mesh.scale.setScalar(1 + (1 - m.life / CFG.fx.muzzleLife) * 0.6);
|
| 158 |
-
if (m.light) m.light.intensity = 3 + 4 * (m.life / CFG.fx.muzzleLife);
|
| 159 |
if (m.life <= 0) {
|
| 160 |
G.scene.remove(m.mesh);
|
| 161 |
-
if (m.light) G.scene.remove(m.light);
|
| 162 |
m.mesh.geometry.dispose();
|
| 163 |
if (m.mesh.material && m.mesh.material.dispose) m.mesh.material.dispose();
|
| 164 |
G.fx.flashes.splice(i, 1);
|
|
@@ -191,12 +176,10 @@ export function updateFX(delta) {
|
|
| 191 |
p.ring.scale.setScalar(s);
|
| 192 |
p.flare.material.opacity = 0.15 + 0.45 * t;
|
| 193 |
p.flare.scale.setScalar(s * 1.2);
|
| 194 |
-
if (p.light) p.light.intensity = 2 + 8 * t;
|
| 195 |
p.flare.lookAt(G.camera.position);
|
| 196 |
if (p.life <= 0) {
|
| 197 |
G.scene.remove(p.ring);
|
| 198 |
G.scene.remove(p.flare);
|
| 199 |
-
if (p.light) G.scene.remove(p.light);
|
| 200 |
p.ring.geometry.dispose();
|
| 201 |
p.flare.geometry.dispose();
|
| 202 |
if (p.ring.material && p.ring.material.dispose) p.ring.material.dispose();
|
|
@@ -220,11 +203,9 @@ export function updateFX(delta) {
|
|
| 220 |
e.ring.scale.setScalar(0.9 + (1 - t) * 2.6);
|
| 221 |
e.ring.rotation.z += delta * 2.5;
|
| 222 |
}
|
| 223 |
-
if (e.light) e.light.intensity = 2 + 10 * t;
|
| 224 |
if (e.life <= 0) {
|
| 225 |
if (e.glow) { G.scene.remove(e.glow); e.glow.geometry.dispose(); if (e.glow.material?.dispose) e.glow.material.dispose(); }
|
| 226 |
if (e.ring) { G.scene.remove(e.ring); e.ring.geometry.dispose(); if (e.ring.material?.dispose) e.ring.material.dispose(); }
|
| 227 |
-
if (e.light) { G.scene.remove(e.light); }
|
| 228 |
G.explosions.splice(i, 1);
|
| 229 |
}
|
| 230 |
}
|
|
|
|
| 42 |
quad.renderOrder = 11;
|
| 43 |
G.scene.add(quad);
|
| 44 |
|
| 45 |
+
// Avoid dynamic lights to prevent shader recompiles
|
| 46 |
+
G.fx.flashes.push({ mesh: quad, light: null, life: CFG.fx.muzzleLife });
|
|
|
|
|
|
|
|
|
|
| 47 |
}
|
| 48 |
|
| 49 |
export function spawnMuzzleFlashAt(worldPos, color = 0xffc060) {
|
|
|
|
| 56 |
quad.renderOrder = 11;
|
| 57 |
G.scene.add(quad);
|
| 58 |
|
| 59 |
+
G.fx.flashes.push({ mesh: quad, light: null, life: CFG.fx.muzzleLife });
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
}
|
| 61 |
|
| 62 |
// Quick, subtle dust puff at a world position
|
|
|
|
| 90 |
flare.position.copy(worldPos);
|
| 91 |
flare.lookAt(G.camera.position);
|
| 92 |
flare.renderOrder = 12;
|
|
|
|
|
|
|
| 93 |
G.scene.add(ring);
|
| 94 |
G.scene.add(flare);
|
| 95 |
+
G.fx.portals.push({ ring, flare, light: null, life, maxLife: life, rot: Math.random() * Math.PI * 2 });
|
|
|
|
| 96 |
}
|
| 97 |
|
| 98 |
// Grenade explosion: additive glow sphere + shock ring + light
|
|
|
|
| 108 |
);
|
| 109 |
ring.position.copy(worldPos);
|
| 110 |
ring.rotation.x = Math.PI / 2;
|
|
|
|
|
|
|
| 111 |
G.scene.add(glow);
|
| 112 |
G.scene.add(ring);
|
| 113 |
+
G.explosions.push({ glow, ring, light: null, life: 0.5, maxLife: 0.5 });
|
|
|
|
| 114 |
}
|
| 115 |
|
| 116 |
export function updateFX(delta) {
|
|
|
|
| 142 |
m.life -= delta;
|
| 143 |
m.mesh.material.opacity = Math.max(0, m.life / CFG.fx.muzzleLife);
|
| 144 |
m.mesh.scale.setScalar(1 + (1 - m.life / CFG.fx.muzzleLife) * 0.6);
|
|
|
|
| 145 |
if (m.life <= 0) {
|
| 146 |
G.scene.remove(m.mesh);
|
|
|
|
| 147 |
m.mesh.geometry.dispose();
|
| 148 |
if (m.mesh.material && m.mesh.material.dispose) m.mesh.material.dispose();
|
| 149 |
G.fx.flashes.splice(i, 1);
|
|
|
|
| 176 |
p.ring.scale.setScalar(s);
|
| 177 |
p.flare.material.opacity = 0.15 + 0.45 * t;
|
| 178 |
p.flare.scale.setScalar(s * 1.2);
|
|
|
|
| 179 |
p.flare.lookAt(G.camera.position);
|
| 180 |
if (p.life <= 0) {
|
| 181 |
G.scene.remove(p.ring);
|
| 182 |
G.scene.remove(p.flare);
|
|
|
|
| 183 |
p.ring.geometry.dispose();
|
| 184 |
p.flare.geometry.dispose();
|
| 185 |
if (p.ring.material && p.ring.material.dispose) p.ring.material.dispose();
|
|
|
|
| 203 |
e.ring.scale.setScalar(0.9 + (1 - t) * 2.6);
|
| 204 |
e.ring.rotation.z += delta * 2.5;
|
| 205 |
}
|
|
|
|
| 206 |
if (e.life <= 0) {
|
| 207 |
if (e.glow) { G.scene.remove(e.glow); e.glow.geometry.dispose(); if (e.glow.material?.dispose) e.glow.material.dispose(); }
|
| 208 |
if (e.ring) { G.scene.remove(e.ring); e.ring.geometry.dispose(); if (e.ring.material?.dispose) e.ring.material.dispose(); }
|
|
|
|
| 209 |
G.explosions.splice(i, 1);
|
| 210 |
}
|
| 211 |
}
|
src/pickups.js
CHANGED
|
@@ -4,12 +4,9 @@ import { CFG } from './config.js';
|
|
| 4 |
import { getTerrainHeight } from './world.js';
|
| 5 |
|
| 6 |
// Share orb material/geometry to avoid per-orb allocations
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
emissiveIntensity: 1.1,
|
| 11 |
-
roughness: 0.3,
|
| 12 |
-
metalness: 0.0
|
| 13 |
});
|
| 14 |
const ORB_GEO = new THREE.SphereGeometry(0.12, 14, 12);
|
| 15 |
|
|
@@ -30,15 +27,10 @@ export function spawnHealthOrbs(center, count) {
|
|
| 30 |
);
|
| 31 |
|
| 32 |
const sphere = new THREE.Mesh(ORB_GEO, ORB_MAT);
|
| 33 |
-
sphere.castShadow =
|
| 34 |
sphere.receiveShadow = false;
|
| 35 |
group.add(sphere);
|
| 36 |
|
| 37 |
-
// Soft green point light for glow
|
| 38 |
-
const light = new THREE.PointLight(0x33ff66, 0.9, 6, 2);
|
| 39 |
-
light.position.set(0, 0.1, 0);
|
| 40 |
-
group.add(light);
|
| 41 |
-
|
| 42 |
G.scene.add(group);
|
| 43 |
|
| 44 |
// Initial outward + upward velocity (reduced to keep grouping tighter)
|
|
@@ -49,7 +41,7 @@ export function spawnHealthOrbs(center, count) {
|
|
| 49 |
|
| 50 |
const orb = {
|
| 51 |
mesh: group,
|
| 52 |
-
light,
|
| 53 |
pos: group.position,
|
| 54 |
radius: 0.7, // pickup radius
|
| 55 |
heal: 1,
|
|
@@ -112,7 +104,7 @@ export function updatePickups(delta) {
|
|
| 112 |
// Visuals: rotation and glow pulse
|
| 113 |
o.bobT += delta * 2.0;
|
| 114 |
o.mesh.rotation.y += delta * 1.5;
|
| 115 |
-
|
| 116 |
|
| 117 |
// If settled, apply gentle bob around baseY
|
| 118 |
if (o.state === 'settled') {
|
|
|
|
| 4 |
import { getTerrainHeight } from './world.js';
|
| 5 |
|
| 6 |
// Share orb material/geometry to avoid per-orb allocations
|
| 7 |
+
// Switch to unlit Basic material so orbs glow without extra scene lights
|
| 8 |
+
const ORB_MAT = new THREE.MeshBasicMaterial({
|
| 9 |
+
color: 0x5cff9a
|
|
|
|
|
|
|
|
|
|
| 10 |
});
|
| 11 |
const ORB_GEO = new THREE.SphereGeometry(0.12, 14, 12);
|
| 12 |
|
|
|
|
| 27 |
);
|
| 28 |
|
| 29 |
const sphere = new THREE.Mesh(ORB_GEO, ORB_MAT);
|
| 30 |
+
sphere.castShadow = false;
|
| 31 |
sphere.receiveShadow = false;
|
| 32 |
group.add(sphere);
|
| 33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
G.scene.add(group);
|
| 35 |
|
| 36 |
// Initial outward + upward velocity (reduced to keep grouping tighter)
|
|
|
|
| 41 |
|
| 42 |
const orb = {
|
| 43 |
mesh: group,
|
| 44 |
+
light: null,
|
| 45 |
pos: group.position,
|
| 46 |
radius: 0.7, // pickup radius
|
| 47 |
heal: 1,
|
|
|
|
| 104 |
// Visuals: rotation and glow pulse
|
| 105 |
o.bobT += delta * 2.0;
|
| 106 |
o.mesh.rotation.y += delta * 1.5;
|
| 107 |
+
// No dynamic light; keep unlit glow cheap
|
| 108 |
|
| 109 |
// If settled, apply gentle bob around baseY
|
| 110 |
if (o.state === 'settled') {
|
src/projectiles.js
CHANGED
|
@@ -80,8 +80,7 @@ export function spawnEnemyFireball(start, dirOrVel, asVelocity = false) {
|
|
| 80 |
group.add(core);
|
| 81 |
group.add(glow);
|
| 82 |
group.add(ring);
|
| 83 |
-
|
| 84 |
-
group.add(light);
|
| 85 |
|
| 86 |
group.position.copy(start);
|
| 87 |
G.scene.add(group);
|
|
@@ -106,7 +105,7 @@ export function spawnEnemyFireball(start, dirOrVel, asVelocity = false) {
|
|
| 106 |
core,
|
| 107 |
glow,
|
| 108 |
ring,
|
| 109 |
-
light,
|
| 110 |
osc: Math.random() * Math.PI * 2
|
| 111 |
};
|
| 112 |
|
|
@@ -135,7 +134,7 @@ export function updateEnemyProjectiles(delta, onPlayerDeath) {
|
|
| 135 |
p.mesh.scale.setScalar(pulse);
|
| 136 |
if (p.ring) p.ring.rotation.z += delta * 3;
|
| 137 |
if (p.glow) p.glow.material.opacity = 0.6 + Math.abs(Math.sin(p.osc * 1.3)) * 0.5;
|
| 138 |
-
|
| 139 |
}
|
| 140 |
p.life -= delta;
|
| 141 |
|
|
|
|
| 80 |
group.add(core);
|
| 81 |
group.add(glow);
|
| 82 |
group.add(ring);
|
| 83 |
+
// Avoid dynamic lights to keep shaders stable; rely on glow meshes
|
|
|
|
| 84 |
|
| 85 |
group.position.copy(start);
|
| 86 |
G.scene.add(group);
|
|
|
|
| 105 |
core,
|
| 106 |
glow,
|
| 107 |
ring,
|
| 108 |
+
light: null,
|
| 109 |
osc: Math.random() * Math.PI * 2
|
| 110 |
};
|
| 111 |
|
|
|
|
| 134 |
p.mesh.scale.setScalar(pulse);
|
| 135 |
if (p.ring) p.ring.rotation.z += delta * 3;
|
| 136 |
if (p.glow) p.glow.material.opacity = 0.6 + Math.abs(Math.sin(p.osc * 1.3)) * 0.5;
|
| 137 |
+
// no dynamic light
|
| 138 |
}
|
| 139 |
p.life -= delta;
|
| 140 |
|