I'm having a serious brain-fart trying to write a particle system.
Goals are:
1) To be nice and modular and use an object-based programming approach.
2) Be adaptive to available resources.
I started off and got to grips with a particle system that worked very nicely, but had no upper bound on the number of particles in use at any one time, which is obviously not a good thing if the number of particles goes >200,000 because no PC is fast enough to update that many in a single frame (or even 2 or 3).
So, I tried to modify things a little and have a hard limit of 50K particles (just for testing purposes). That's where I hit my snag.
Particles are defined as a type, with positional, velocity, colour and time-to-live elements. When a particle is created, it is given a time-to-live number of milliseconds (actually, I'm just using game updates because I've not implemented delta time yet). When all particles are updated, the time-to-live is decreased, if it hits 0 the particle is not displayed.
Simple.
Simple until I want to create a new explosion effect and we already have 50K particles active. The elegant solution is to reassign the oldest (i.e. lowest time-to-live) particles to the new explosion. This would give the best visual experience using the limited number of particles available.
Doing this with custom types is proving difficult, though I'm sure it can be done very simply.
I'm sat here thinking that I should scrap the almost-elegant custom type approach and implement the whole thing using data stored in banks. I can't use arrays because the maximum number of particles in use needs to be dynamic.
Has anyone got a neat solution, or can at least bandy around some arguments for and against a particular method, or alternative way of thinking?
** EDIT: The prooblem with things at the moment is, each new effect reassigns particles, but always starts reassigning the first created particle. This is because when I for-each through the particles to update+display them, the type 'pointer' ends up back at the last particle.
What I could do with is a way to go to the nth particle. I'm guessing that the way things are stored internally is a linked-list, which precludes using anything other then a for-next to step through from the first particle to the desired one.
Code...
setting\max_paticles% is fairly self-explanatory. setting\ is a global custom type where I store all game settings (screen res, controls etc etc).
Goals are:
1) To be nice and modular and use an object-based programming approach.
2) Be adaptive to available resources.
I started off and got to grips with a particle system that worked very nicely, but had no upper bound on the number of particles in use at any one time, which is obviously not a good thing if the number of particles goes >200,000 because no PC is fast enough to update that many in a single frame (or even 2 or 3).
So, I tried to modify things a little and have a hard limit of 50K particles (just for testing purposes). That's where I hit my snag.
Particles are defined as a type, with positional, velocity, colour and time-to-live elements. When a particle is created, it is given a time-to-live number of milliseconds (actually, I'm just using game updates because I've not implemented delta time yet). When all particles are updated, the time-to-live is decreased, if it hits 0 the particle is not displayed.
Simple.
Simple until I want to create a new explosion effect and we already have 50K particles active. The elegant solution is to reassign the oldest (i.e. lowest time-to-live) particles to the new explosion. This would give the best visual experience using the limited number of particles available.
Doing this with custom types is proving difficult, though I'm sure it can be done very simply.
I'm sat here thinking that I should scrap the almost-elegant custom type approach and implement the whole thing using data stored in banks. I can't use arrays because the maximum number of particles in use needs to be dynamic.
Has anyone got a neat solution, or can at least bandy around some arguments for and against a particular method, or alternative way of thinking?
** EDIT: The prooblem with things at the moment is, each new effect reassigns particles, but always starts reassigning the first created particle. This is because when I for-each through the particles to update+display them, the type 'pointer' ends up back at the last particle.
What I could do with is a way to go to the nth particle. I'm guessing that the way things are stored internally is a linked-list, which precludes using anything other then a for-next to step through from the first particle to the desired one.
Code...
; particle.bb ; Particle handler for spheres.bb. ; Copyright 2005 Stefan Holmes. ; This file is part of Spheres. ; ; Spheres is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2 of the License, or ; (at your option) any later version. ; ; Spheres is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with Spheres; if not, write to the Free Software ; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ;----------------------------------------------------------------------------------------------------- ; Globals... Global game.particle ;----------------------------------------------------------------------------------------------------- ; Types... Type particle Field id% ; Positional elements. Field x# Field y# Field dx# Field dy# ; ## To-do: Convert to using HSL values for 'rainbow' effects. Field argb% ; Control elements. Field edge_action% ; 0 = die at edge of screen. ; 1 = wrap around screen. ; 2 = bounce off screen edge. ; ## Not actually implemented yet. Field ttl% ; Particle's time to live. End Type ;----------------------------------------------------------------------------------------------------- ; Functions... Function particleStart.particle() this.particle = New particle ; this\id% = Handle(this) this\x# = Rand(10,setting\screen_width%-10) this\y# = Rand(10,setting\screen_height%-10) this\dx# = Rnd(-0.5,0.5) this\dy# = Rnd(-0.5,0.5) this\argb% = $FFFFFF00 this\edge_action% = 0 this\ttl% = 200 Return this End Function Function particleUpdateRender(this.particle) LockBuffer For this.particle = Each particle If this\ttl% > 0 If this\x# < 10 Or this\x#>(setting\screen_width%-10) Then this\dx# = -this\dx# If this\y# < 10 Or this\y#>(setting\screen_height%-10) Then this\dy# = -this\dy# this\x# = this\x# + this\dx# : this\y# = this\y# + this\dy# WritePixelFast this\x#,this\y#,this\argb% this\ttl% = this\ttl% - 1 ; ## To-do: implement delta time. End If Next UnlockBuffer End Function Function explosionEffect(numparticles%,x#,y#,time%,edge%,initialradius%=1,lowestspeed#=1,highestspeed#=1.25,argb%=$00FFFF00) spread#=360.0/numparticles% offset#=Rnd(0,spread#) next_particle% = (next_particle% + num_particle%) Mod setting\max_particles% For i% = 1 To numparticles% game.particle = After game If game = Null Then game.particle = First particle j# = i% * spread# + offset# game\x# = x#+(Sin(j#)*initialradius%) ; ## To-do: Benchmark sin() against lookup-tables. game\y# = y#+(Cos(j#)*initialradius%) speed# = Rnd(lowestspeed#,highestspeed#) game\dx# = Sin(j#)*speed# game\dy# = Cos(j#)*speed# game\ttl% = 200 game\argb% = argb% Next End Function
setting\max_paticles% is fairly self-explanatory. setting\ is a global custom type where I store all game settings (screen res, controls etc etc).