File size: 1,867 Bytes
173e683 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | /**
* ParticleField — twinkling stars overhead, fade in as difficulty rises
*/
import {
AdditiveBlending, BufferAttribute, BufferGeometry, CanvasTexture,
Color, Points, PointsMaterial,
} from 'three';
function starTexture() {
const c = document.createElement('canvas');
c.width = c.height = 64;
const ctx = c.getContext('2d');
const g = ctx.createRadialGradient(32, 32, 0, 32, 32, 32);
g.addColorStop(0, 'rgba(255,255,255,1)');
g.addColorStop(0.35, 'rgba(255,255,255,0.85)');
g.addColorStop(0.7, 'rgba(255,255,255,0.15)');
g.addColorStop(1, 'rgba(255,255,255,0)');
ctx.fillStyle = g;
ctx.beginPath(); ctx.arc(32, 32, 32, 0, Math.PI * 2); ctx.fill();
const t = new CanvasTexture(c);
t.needsUpdate = true;
return t;
}
export class ParticleField {
constructor(scene) {
this.scene = scene;
const N = 700;
const geom = new BufferGeometry();
const pos = new Float32Array(N * 3);
for (let i = 0; i < N; i++) {
pos[i * 3] = (Math.random() * 2 - 1) * 90;
pos[i * 3 + 1] = 12 + Math.random() * 45;
pos[i * 3 + 2] = -20 - Math.random() * 55;
}
geom.setAttribute('position', new BufferAttribute(pos, 3));
this.material = new PointsMaterial({
size: 0.6,
color: 0xffffff,
map: starTexture(),
transparent: true,
opacity: 0,
blending: AdditiveBlending,
depthWrite: false,
});
this.points = new Points(geom, this.material);
this.scene.add(this.points);
this.t = 0;
}
applyPalette(p) {
this.targetOpacity = p.starOpacity;
}
update(dt) {
this.t += dt;
this.points.rotation.y += 0.005 * dt;
if (this.targetOpacity !== undefined) {
this.material.opacity += (this.targetOpacity - this.material.opacity) * 0.04;
}
// gentle twinkle
this.material.size = 0.55 + Math.sin(this.t * 2) * 0.07;
}
}
|