UNPKG

raindrop-fx

Version:
125 lines (107 loc) 4.54 kB
import { div, mul, plus, vec2, Vector2 } from "@sardinefish/zogra-renderer"; import { random, randomRange } from "./random"; import { CollisionGrid, RaindropSimulator } from "./simulator"; import { lerp, Time } from "./utils"; export class RainDrop { pos: vec2; density: number = 1; velocity: vec2 = vec2.zero(); spread: vec2; destroied = false; parent?: RainDrop; grid?: CollisionGrid; gridIdx?: number; private _mass: number = 0; private _size: vec2 = vec2.zero(); private simulator: RaindropSimulator; private resistance = 0; private shifting = 0; private lastTrailPos: vec2; private nextTrailDistance: number; private nextRandomTime = 0; constructor(simulator: RaindropSimulator, pos: vec2, size: number, density = 1) { this.pos = pos; this.simulator = simulator; this.density = density; this.lastTrailPos = pos.clone(); this.nextTrailDistance = randomRange(...simulator.options.trailDistance); this.spread = vec2(simulator.options.initialSpread); this.mass = (size * density) ** 2; } get mass() { return this._mass; } set mass(m: number) { this._mass = m; const sqrtM = Math.sqrt(m) / this.density; this._size.x = (this.spread.x + 1) * sqrtM; this._size.y = (this.spread.y + 1) * sqrtM; // this._size = mul(plus(this.spread, vec2.one()), Math.sqrt(m)); } get size(): vec2 { return this._size; } get mergeDistance() { return this.size.x * (1 + this.spread.x) * 0.16 * this.simulator.options.colliderSize; } get options() { return this.simulator.options } updateRaindrop(time: Time) { if (this.nextRandomTime <= time.total) { this.nextRandomTime = time.total + randomRange(...this.simulator.options.motionInterval) this.randomMotion(); } this.mass -= this.simulator.options.evaporate * time.dt; const force = this.options.gravity * this.mass - this.resistance; const acceleration = force / this.mass; this.velocity.y -= acceleration * time.dt; if (this.velocity.y > 0) this.velocity.y = 0; this.velocity.x = Math.abs(this.velocity.y) * this.shifting; this.pos.x += this.velocity.x * time.dt; this.pos.y += this.velocity.y * time.dt; // this.pos.plus(mul(this.velocity, vec2(time.dt))); const spreadByVelocity = this.simulator.options.velocitySpread * 2 * Math.atan(Math.abs(this.velocity.y * 0.005)) / Math.PI; this.spread.y = Math.max(this.spread.y, spreadByVelocity); this.spread.x *= Math.pow(this.simulator.options.shrinkRate, time.dt); this.spread.y *= Math.pow(this.simulator.options.shrinkRate, time.dt); // this.spread.y += Math.abs(this.velocity.y) * 0.0001; if (Vector2.distanceSquared(this.lastTrailPos, this.pos) > this.nextTrailDistance * this.nextTrailDistance) { this.split(); } } split() { // return; if (this.mass < 1000) return; let size = this.size.x * randomRange(...this.simulator.options.trailDropSize); const pos = plus(vec2(randomRange(-5, 5), this.size.y / 4), this.pos); let trailDrop = this.simulator.spawner.spawn(pos.clone(), size, this.simulator.options.trailDropDensity); trailDrop.spread = vec2(0.1, Math.abs(this.velocity.y) * 0.01 * this.options.trailSpread); trailDrop.parent = this; this.mass -= trailDrop.mass; this.simulator.add(trailDrop); this.lastTrailPos = this.pos.clone(); this.nextTrailDistance = randomRange(...this.simulator.options.trailDistance); } randomMotion() { const maxResistance = lerp(...this.simulator.options.spawnSize, 1 - this.simulator.options.slipRate) ** 2 * 4; this.resistance = randomRange(0, 1) * this.options.gravity * maxResistance; this.shifting = random() * randomRange(...this.simulator.options.xShifting); } merge(target: RainDrop) { const selfMomentum = mul(this.velocity, this.mass); const targetMomentum = mul(target.velocity, target.mass); const momentum = plus(selfMomentum, targetMomentum); this.mass += target.mass; this.velocity = div(momentum, this.mass); } }