143 lines
3.7 KiB
Plaintext
143 lines
3.7 KiB
Plaintext
// particles2d.cm - Particle system factory
|
|
//
|
|
// Creates particle data objects that register with film2d
|
|
// Also provides emitter update logic
|
|
|
|
var film2d = use('film2d')
|
|
var random = use('random').random
|
|
|
|
var particles2d_proto = {
|
|
type: 'particles',
|
|
|
|
destroy: function() {
|
|
film2d.unregister(this._id)
|
|
}
|
|
}
|
|
|
|
// Emitter management
|
|
var emitters = {
|
|
// Spawn a particle for an emitter
|
|
spawn: function(emitter) {
|
|
emitter.particles.push({
|
|
pos: {
|
|
x: emitter.pos.x + (random() - 0.5) * emitter.spawn_area.width,
|
|
y: emitter.pos.y + (random() - 0.5) * emitter.spawn_area.height
|
|
},
|
|
velocity: {
|
|
x: emitter.velocity.x + (random() - 0.5) * emitter.velocity_var.x,
|
|
y: emitter.velocity.y + (random() - 0.5) * emitter.velocity_var.y
|
|
},
|
|
life: emitter.life,
|
|
time: 0,
|
|
max_scale: emitter.scale + (random() - 0.5) * emitter.scale_var,
|
|
scale: 0,
|
|
color: {
|
|
r: emitter.color.r,
|
|
g: emitter.color.g + (random() - 0.5) * 0.3,
|
|
b: emitter.color.b,
|
|
a: 1
|
|
}
|
|
})
|
|
},
|
|
|
|
// Update an emitter and its particles
|
|
update: function(emitter, dt) {
|
|
// Spawn new particles
|
|
if (emitter.rate > 0) {
|
|
emitter.spawn_timer = (emitter.spawn_timer || 0) + dt
|
|
var pp = 1 / emitter.rate
|
|
while (emitter.spawn_timer > pp) {
|
|
emitter.spawn_timer -= pp
|
|
emitters.spawn(emitter)
|
|
}
|
|
}
|
|
|
|
// Update existing particles
|
|
for (var i = emitter.particles.length - 1; i >= 0; i--) {
|
|
var p = emitter.particles[i]
|
|
p.time += dt
|
|
p.pos.x += p.velocity.x * dt
|
|
p.pos.y += p.velocity.y * dt
|
|
|
|
// Scale animation
|
|
var grow_for = emitter.grow_for || 0.3
|
|
var shrink_for = emitter.shrink_for || 0.5
|
|
if (p.time < grow_for) {
|
|
p.scale = lerp(0, p.max_scale, p.time / grow_for)
|
|
} else if (p.time > p.life - shrink_for) {
|
|
p.scale = lerp(0, p.max_scale, (p.life - p.time) / shrink_for)
|
|
} else {
|
|
p.scale = p.max_scale
|
|
}
|
|
|
|
// Alpha fade
|
|
var alpha = 1
|
|
if (p.time > p.life * 0.7) {
|
|
alpha = 1 - (p.time - p.life * 0.7) / (p.life * 0.3)
|
|
}
|
|
p.color.a = alpha
|
|
|
|
// Remove dead particles
|
|
if (p.time >= p.life) {
|
|
emitter.particles.splice(i, 1)
|
|
}
|
|
}
|
|
|
|
// Sync to film2d if handle provided
|
|
if (emitter.handle) {
|
|
emitter.handle.particles = emitter.particles
|
|
}
|
|
},
|
|
|
|
// Create an emitter config
|
|
create: function(config) {
|
|
return {
|
|
pos: config.pos || {x: 0, y: 0},
|
|
spawn_area: config.spawn_area || {width: 10, height: 10},
|
|
velocity: config.velocity || {x: 0, y: 100},
|
|
velocity_var: config.velocity_var || {x: 20, y: 20},
|
|
life: config.life || 2,
|
|
rate: config.rate || 10,
|
|
scale: config.scale || 1,
|
|
scale_var: config.scale_var || 0.3,
|
|
grow_for: config.grow_for || 0.3,
|
|
shrink_for: config.shrink_for || 0.5,
|
|
color: config.color || {r: 1, g: 1, b: 1, a: 1},
|
|
spawn_timer: 0,
|
|
particles: [],
|
|
handle: config.handle || null
|
|
}
|
|
}
|
|
}
|
|
|
|
function lerp(a, b, t) { return a + (b - a) * t }
|
|
|
|
// Factory function - auto-registers with film2d
|
|
var factory = function(props) {
|
|
var defaults = {
|
|
type: 'particles',
|
|
pos: {x: 0, y: 0},
|
|
image: null,
|
|
width: 16,
|
|
height: 16,
|
|
plane: 'default',
|
|
layer: 0,
|
|
groups: [],
|
|
particles: [],
|
|
opacity: 1,
|
|
tint: {r: 1, g: 1, b: 1, a: 1}
|
|
}
|
|
|
|
var data = {}
|
|
for(var k in defaults) data[k] = defaults[k]
|
|
for(var k in props) data[k] = props[k]
|
|
|
|
var newparticles = meme(particles2d_proto, data)
|
|
film2d.register(newparticles)
|
|
return newparticles
|
|
}
|
|
|
|
// Attach emitter helpers to factory
|
|
factory.emitters = emitters
|
|
|
|
return factory |