After about half an hour of fooling around with Processing.js/JavaScript, I got this fairly decent particle system set up:
// main particle constructor class
var Particle = function(position) {
this.velocity = new PVector(random(-0.1, 0.1), random(-0.1, 0.1));
this.acceleration = new PVector(random(-0.01, 0.01), random(-0.01, 0.01));
this.lifeTime = 300;
this.position = position.get();
};
// update the particle
Particle.prototype.update = function() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.lifeTime--;
};
// render the particle
Particle.prototype.render = function() {
strokeWeight(2);
stroke(161, 153, 153, this.lifeTime);
fill(140, 135, 140, this.lifeTime);
ellipse(this.position.x, this.position.y, 15, 15);
};
// check the bounds of a particle
Particle.prototype.checkBounds = function() {
if(this.position.x >= 385) {
this.acceleration = new PVector(random(-0.02, -0.01), random(-0.01, 0.01));
}
if(this.position.x <= 15) {
this.acceleration = new PVector(random(0.01, 0.02), random(-0.01, 0.01));
}
if(this.position.y >= 385) {
this.acceleration = new PVector(random(-0.01, 0.01), random(-0.02, -0.01));
}
if(this.position.y <= 15) {
this.acceleration = new PVector(random(-0.01, 0.01), random(0.01, 0.02));
}
};
// check if the particle is dead
Particle.prototype.particleDead = function() {
if(this.lifeTime > 0) {
return false;
} else {
return true;
}
};
// array containing particles
var particles = [];
// main draw loop
var draw = function() {
// draw the background and push a new particle
background(11, 100, 209);
particles.push(new Particle(new PVector(mouseX, mouseY)));
// iterate through particles and run their methods
for(var p = particles.length-1; p >= 0; p--) {
var particle = particles[p];
particle.checkBounds();
particle.update();
particle.render();
if(particle.particleDead()) {
particles.splice(p, 1);
}
}
};
Now, I think that this could definitely be improved, as the Particle.prototype.checkBounds
method seems to not work all the time. Anyways, what can I improve here?
1 Answer 1
Interesting question,
there are few things that could be improved:
Read up on the revealing module pattern, it would be excellent to
- Reduce your usage of
this
- Make your class look like 1 piece of code
Something like this:
// main particle constructor class function Particle(position) { var velocity = new PVector(random(-0.1, 0.1), random(-0.1, 0.1)), acceleration = new PVector(random(-0.01, 0.01), random(-0.01, 0.01)), lifeTime = 300, position = position.get(); function update() { velocity.add(acceleration); position.add(velocity); lifeTime--; } function render() { strokeWeight(2); stroke(161, 153, 153, lifeTime); fill(140, 135, 140, lifeTime); ellipse(position.x,position.y, 15, 15); }; return { update : update, render: render; } };
I obviously did not rewrite the whole thing, but you should catch my drift.
- Reduce your usage of
- You keep using
random(-0.1, 0.1)
, I would create a helper function for this fill(140, 135, 140, lifeTime);
<- I would put a comment as to what color this should beDo not use an if statement to determine true or false like you do :
// check if the particle is dead Particle.prototype.particleDead = function() { if(this.lifeTime > 0) { return false; } else { return true; } };
do this instead:
// check if the particle is dead Particle.prototype.particleDead = function() { return this.lifeTime > 0 };
If you have time, read up on falsy values, you could also simply do
return !this.lifetime
I am still playing with the code, I managed to fix the right hand side bounce with this:
if(this.position.x >= 385) { this.acceleration.x = random(-0.02, -0.01); this.velocity.x = random(-0.1, -0.01); }
In essence you have to solve both the acceleration and the velocity.
I have to wonder, how do you access random
? I can only access random
as a function under the instance of Processing.
It is too bad that the snippets will now allow for localStorage access, so I can only give you a link to jsbin with my refactored version and the source code here in case jsbin ever dies:
var canvas = document.getElementById('partikals'),
pjs = new Processing(canvas),
width = 400,
height = 400,
padding = 15;
function Particle2(position) {
var velocity = new PVector(pjs.random(-0.1, 0.1), pjs.random(-0.1, 0.1)),
acceleration = new PVector(pjs.random(-0.01, 0.01), pjs.random(-0.01, 0.01)),
lifeTime = 300;
position = position.get();
function update() {
velocity.add(acceleration);
position.add(velocity);
lifeTime--;
}
function render() {
pjs.strokeWeight(2);
//Each particle is grey
pjs.stroke(161, 153, 153, lifeTime);
pjs.fill(140, 135, 140, lifeTime);
pjs.ellipse(position.x, position.y, 15, 15);
}
function isDead() {
return !lifeTime;
}
//I wonder if just *= -1 would work
function bounce() {
if (position.x > width - padding) {
acceleration.x = -Math.abs(acceleration.x);
velocity.x = -Math.abs(velocity.x);
}
if (position.x < padding) {
acceleration.x = Math.abs(acceleration.x);
velocity.x = Math.abs(velocity.x);
}
if (position.y > height - padding) {
acceleration.y = -Math.abs(acceleration.y);
velocity.y = -Math.abs(velocity.y);
}
if (position.y < padding) {
acceleration.y = Math.abs(acceleration.y);
velocity.y = Math.abs(velocity.y);
}
}
return {
update: update,
render: render,
isDead: isDead,
bounce: bounce
};
}
// array containing particles
var particles = [];
pjs.setup = function() {
pjs.size(width, height);
pjs.frameRate(24);
};
pjs.draw = function() {
//Clear by drawing the background and push a new particle
pjs.background(11, 100, 209);
particles.push(new Particle2(new PVector(pjs.mouseX, pjs.mouseY)));
// iterate through particles and run their methods
for (var p = particles.length - 1; p >= 0; p--) {
var particle = particles[p];
particle.bounce();
particle.update();
particle.render();
if (particle.isDead()) {
particles.splice(p, 1);
}
}
};
pjs.setup();
-
\$\begingroup\$ I use Khanacademy to program, they must have some special features to make things easier. \$\endgroup\$Ethan Bierlein– Ethan Bierlein2014年10月24日 22:10:06 +00:00Commented Oct 24, 2014 at 22:10
-
1\$\begingroup\$ +1 for some good points, but I have a few concerns: A revealing JavaScript module is an IIFE - not a constructor function, just as described in your link. In my opinion, if OP wants to use the revealing module pattern, it should look something like this: jsfiddle.net/jw9cj6pj Your current
update
andrender
functions will be redefined for each newParticle
that is instantiated, which is bad from a performance point of view. \$\endgroup\$Johan– Johan2014年10月24日 22:59:47 +00:00Commented Oct 24, 2014 at 22:59 -
\$\begingroup\$ @Johan There is no performance problem, but there is a memory cost. Given that in this code, you can have at most 200 instances, I think this is still the right approach. \$\endgroup\$konijn– konijn2014年10月25日 00:32:25 +00:00Commented Oct 25, 2014 at 0:32
-
1\$\begingroup\$ There is a fine line between over-engineering and maintainability, but since using the
prototype
adds little to no overhead, I think despite the current(!) limit you should not waste the memory. Changing the design is much harder later on. \$\endgroup\$Ingo Bürk– Ingo Bürk2014年10月25日 11:37:52 +00:00Commented Oct 25, 2014 at 11:37
Explore related questions
See similar questions with these tags.
checkBounds
. \$\endgroup\$checkBounds
does not implement hard boundaries: it just applies a force to the right if the particle is close to the left edge. \$\endgroup\$