prsm/packages/ngn/src/demo.ts
2024-08-28 09:39:27 -04:00

147 lines
4.0 KiB
TypeScript

import { pulse } from "./misc";
import { createWorld, type WorldState } from "./ngn";
import { create2D } from "./packages/2d";
import { ColorEasing, createParticleSystem, Particle } from "./packages/emitter";
const { canvas, draw } = create2D({ canvas: { fullscreen: true } });
const particleSystem = createParticleSystem({
x: canvas.width / 2,
y: canvas.height / 2,
canvas,
});
const emitter = particleSystem.createEmitter({
x: canvas.width / 2,
y: canvas.height / 2,
maxParticles: 100,
rate: 0.1,
lifetime: 1000,
lifetimeVariation: 0.2,
size: 20,
sizeVariation: 10,
colorStart: ["#FF0000", "#ff5100"],
colorEnd: "#222222",
colorEasing: ColorEasing.EASE_IN,
fadeOutEasing: ColorEasing.EASE_OUT,
speed: 0.1,
speedVariation: 1,
angle: 0,
spread: 0.75,
gravity: { x: 0, y: 0 },
canvas,
burst: false,
onInit: (particle: Particle, state: WorldState) => {
particle.x += Math.random() < 0.5 ? -6 : 6;
particle.y += Math.random() < 0.5 ? -6 : 6;
if (!(Math.random() < 0.02)) {
return;
}
particle.size = 15;
particle.speedY = -0.3;
particle.lifetime = 1000;
particle.colorEnd = "#ff0000";
particle.onRemove = () => {
particleSystem.createEmitter({
x: particle.x,
y: particle.y,
maxParticles: 3,
lifetimeVariation: 0.2,
size: 3,
sizeVariation: 2,
colorStart: ["#FF0000", "#ff5100"],
colorEnd: "#222222",
colorEasing: ColorEasing.EASE_IN,
fadeOutEasing: ColorEasing.EASE_OUT,
speed: 0.02,
speedVariation: 1,
spread: 6,
angle: 180,
canvas,
burst: true,
});
};
particle.onUpdate = () => {
particle.size = Math.max(0, particle.size + 0.25);
};
},
onUpdate: (particle: Particle, state: WorldState) => {
particle.size = Math.max(0, particle.size - 0.35);
const v = pulse(state.time.elapsed, 0.25, -1, 1);
particle.x += v * 1;
},
onRemove: (particle: Particle, state: WorldState) => {},
});
const { addSystem, start, step, defineMain } = createWorld();
const clearCanvasSystem = () => {
const context = canvas.getContext("2d");
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "#111";
context.fillRect(0, 0, canvas.width, canvas.height);
};
const fpsDrawSystem = (state: WorldState) => {
draw.text({ x: 10, y: 20 }, `FPS: ${state.time.fps.toFixed(2)}`, "white");
};
const particleCountSystem = (state: WorldState) => {
draw.text({ x: 10, y: 40 }, `Particle count: ${particleSystem.numParticles}`, "white");
};
const particlePositionSystem = (state: WorldState) => {
const { time } = state;
const xPos = pulse(time.elapsed, 0.25, canvas.width / 2 - 100, canvas.width / 2 + 100);
// const yPos = pulse(time.elapsed, 0.25, canvas.height / 2 - 100, canvas.height / 2 + 100);
emitter.x = xPos;
// emitter.y = yPos;
};
addSystem(clearCanvasSystem, fpsDrawSystem, particleCountSystem, particlePositionSystem, particleSystem);
defineMain(() => {
step();
});
start();
const container = document.createElement("div");
container.style.position = "fixed";
container.style.bottom = "0";
container.style.left = "0";
container.style.padding = "10px";
container.style.display = "flex";
container.style.justifyContent = "space-between";
container.style.alignItems = "center";
document.body.appendChild(container);
const pauseButton = document.createElement("button");
pauseButton.innerText = "Pause";
pauseButton.onclick = () => {
particleSystem.pause();
};
container.appendChild(pauseButton);
const startButton = document.createElement("button");
startButton.innerText = "Start";
startButton.style.marginLeft = "5px";
startButton.onclick = () => {
particleSystem.start();
};
container.appendChild(startButton);
const destroyButton = document.createElement("button");
destroyButton.innerText = "Destroy";
destroyButton.style.marginLeft = "5px";
destroyButton.onclick = () => {
particleSystem.destroy();
};
container.appendChild(destroyButton);