Experiencing 'coders block' - particle system.

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

I've come up with this basic framework. Not tested so may need tweaking but shows basic idea. Don't bother running it because it doesn't do anything - I've kept the code bare bones so as not to be confusing. :)

; Particle type. Note that frame_life isn't actually needed - it's only included
; and decremented every update so that we can tell how long a particle has left to live.
; If you never need to find out how long a given particle has left to live, it can be removed.
Type particleT
	Field x%,y%										; Particle's current screen coords.
	; Other fields go here.
	Field frame_life%							; Number of frames particle has left to live.
	Field next_particle.particleT	; Next particle in linked list.
End Type

Const MAX_PARTICLES%			= 10000
Const MAX_PARTICLE_LIFE%	= 60*5	; FPS multiplied by seconds.

; Array of linked lists holding ALL particles.
; Note: plist(0) is list holding all available/free particles.
Dim plist.particleT(MAX_PARTICLE_LIFE)

; Index into plist that represents the current frame. Incremented every update.
Global now_index% = 1

; Create all particles and add them to the 'free' list (plist(0)).
For n = 1 To MAX_PARTICLES
	p.particleT = New particleT
	free_particle(p)
Next

; Main loop
While Not KeyHit(1)
	If KeyDown(57)
		; Hold SPACE to continuously add new particles.
		create_particle(Rand(800),Rand(600),Rand(MAX_PARTICLE_LIFE))
	EndIf
	
	update_particles()
	draw_particles()
	Flip
Wend

End
	
	
;
; Create a new particle. The oldest one will be used if no free ones are available.
;
Function create_particle.particleT(x,y,frame_life)

	; Get a particle to use.
	If plist(0) <> Null
		; Get particle from free list.
		p.particleT = plist(0)
		plist(0) = p\next_particle
		;p\next_particle = Null
	Else
		; No free particles available so get the oldest one.
		list_index = now_index
		
		; Look forwards from now_index until we find a list containing a particle.
		; This list will hold the oldest particle(s).
		Repeat
			list_index = list_index + 1
			If list_index > MAX_PARTICLE_LIFE Then list_index = 1
			
			If plist(list_index) <> Null
				; We've found the list containing the oldest particle(s) so
				; remove the first one in the list and use that.
				p.particleT = plist(list_index)
				plist(list_index) = p\next_particle
				;p\next_particle = Null
			EndIf
		Forever
	EndIf

	; Code to initialize new particle p goes here.
	p\x						= x
	p\y						= y
	p\frame_life	= frame_life

	; Add particle to list that is required number of frames ahead of now_index.
	ahead_index = (now_index + frame_life) Mod MAX_PARTICLE_LIFE
	p\next_particle = plist(ahead_index)
	plist(ahead_index) = p
	
	Return p
	
End Function


;
; Frees a particle by adding it to the linked list of free particles (plist(0)).
;
Function free_particle(p.particleT)

	p\next_particle = plist(0)
	plist(0) = p
	
End Function


;
; Frees all particles that expire on this frame then updates all remaining, living particles.
;
Function update_particles()

	If plist(now_index) <> Null
		; Particles in this list have expired so free them.
		p.particleT = plist(now_index)
		
		Repeat
			p_next.particleT = p\next_particle
			free_particle(p)
			p = p_next
		Until p = Null
		
		plist(now_index) = Null
	End If

	; Update all living particles.
	For n = 1 To MAX_PARTICLES
		p.particleT = plist(n)
		If p <> Null
			Repeat
				; Code to update particle p goes here.
				p\x						= p\x + Rand(10)
				p\y						= p\y + Rand(10)
				p\frame_life	= p\frame_life - 1
				
				p = p\next_particle
			Until p = Null
		End If
	Next

	; Increment list index of particle(s) to be killed next time around.
	now_index = now_index + 1
	If now_index > MAX_PARTICLE_LIFE Then now_index = 1

End Function


;
; Draw all living particles.
;
Function draw_particles()

	For n = 1 To MAX_PARTICLES
		p.particleT = plist(n)
		If p <> Null
			Repeat
				; Code to draw particle p goes here.
								
				p = p\next_particle
			Until p = Null
		End If
	Next

End Function


It basically works by creating an array of particle linked lists with a global index variable (now_index) that represents the current frame. This variable is incremented (and cycles back to 1) every update. All particles in the currently indexed list have expired and so get freed.

So, to create a particle with a 10 frame lifespan, we simply add it to the list 10 positions ahead of where now_index is referencing. Thus, it takes now_index 10 updates/increments before it reaches this list and frees all the particles contained within.

Now, when we run out of particles and want to use the oldest one, we can simply walk forwards from now_index until we hit a list containing a particle. Note that by "oldest one" I mean the particle with the shortest time left to live. This may not necessarily be the particle that has been in existence for the longest time.

This system could be converted to work with delta timing, I think, but may not be great in that situation since you'd have to have linked list for each millisec of MAX_PARTICLE_LIFE. Hmmm, i'd have to think about that, tbh.

May not be exactly what you're looking for, not sure. :)

[edit] slight code update.