Experiencing 'coders block' - particle system.

Blitz3D Forums/Blitz3D Programming/Experiencing 'coders block' - particle system.

The forum is locked. These are the original blitzbasic.com forums, kept so they can still be read. No new topics, replies or edits can be made here. To join in, head over to the Blitz3D+ forums.

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...

; 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).