Code archives/Graphics/Explode!

This code has been declared by its author to be Public Domain code.

Download source code

Explode! by BlitzSupport2002
This little system will explode any image in realtime, applying gravity and some simple forces.

I recommend ignoring the include file posted below and just downloading this archive (18kB), complete with a couple of demos :)

UPDATED: Now with pre-rendering of explosions -- we're good for realtime now!
; THIS CODE SHOULD BE SAVED AS "explosions.bb" AND USED AS AN 'INCLUDE' (see URL above)...

; -----------------------------------------------------------------------------
; EXPLOSION System -- placed in the public domain by james @ hi-toro.com, 2002.
; -----------------------------------------------------------------------------

; Fully commented 2D image explosion system, suitable for realtime use if either:

; * RenderExplosion () is used, or;
; * StartRealtimeExplosion () is used on small images, few in number.

; NOTE FROM EXPERIENCE! If anything odd happens with explosions, make sure you're
; not making any variables global that are used inside these functions! Doh!

; -----------------------------------------------------------------------------
; **** IMPORTANT: doesn't work for MidHandle'd images! ****
; -----------------------------------------------------------------------------

; Only small images should use split sizes like 1 x 1 or 2 x 2! The larger the
; image, the larger the split size you should use (note that large images
; split into 1 x 1 or 2 x 2 can take AGES to pre-render!)...

; -----------------------------------------------------------------------------
; Usage:
; -----------------------------------------------------------------------------

; Startup: Set your display mode, then call InitExplosions ().

; Startup: Optionally pre-render explosions via RenderExplosion ().

; During game: Call StartRenderedExplosion () or StartRealtimeExplosion ()
; to explode an image.

; During game: Call UpdateExplosions somewhere before Flip ().

; -----------------------------------------------------------------------------
; Types used by explosion system...
; -----------------------------------------------------------------------------

; Particles actually drawn and updated on-screen...

Type ExplosionParticle
	Field prerendered
	Field sprite						; Particle image
	Field x#							; x-position on screen
	Field y#							; x-position on screen
	Field xs#							; x speed of particle
	Field ys#							; y speed of particle
End Type

; Pre-rendered explosion -- one created per image, via RenderExplosion ()...

Type RenderedExplosion
	Field rep.RenderedExplosionParticle
End Type

; Pre-rendered particles (not shown on-screen), created by RenderExplosion ()...

Type RenderedExplosionParticle
	Field rendered.RenderedExplosion	; Handle to rendered explosion
	Field sprite						; Particle image
	Field x#							; x-position on screen
	Field y#							; x-position on screen
	Field xs#							; x speed of particle
	Field ys#							; y speed of particle
End Type

; -----------------------------------------------------------------------------
; Globals used by explosion system...
; -----------------------------------------------------------------------------

Global EXPLODE_GW						; Display width
Global EXPLODE_GH						; Display height

Global EXPLODE_Num						; Number of particles currently on screen

Global EXPLODE_Gravity# = 0.05			; Gravity

Global EXPLODE_MaskR					; Images' mask red value
Global EXPLODE_MaskG					; Images' mask green value
Global EXPLODE_MaskB					; Images' mask blue value

Global EXPLODE_DefaultModifier# = 0.025	; Default modifier used when auto-calc'ing force in Start[*]Explosion functions
Global EXPLODE_DefaultMinRand# = 0.25	; Default minimum range of explosion randomiser
Global EXPLODE_DefaultMaxRand# = 1		; Default minimum range of explosion randomiser

Const EXPLODE_DefaultForce# = 0.05		; Default force used by Start[*]Explosion functions

; -----------------------------------------------------------------------------
; Explosion system functions...
; -----------------------------------------------------------------------------

; SETEXPLOSIONRGB ()						[normally just called internally]
; -----------------------------------------------------------------------------
; Alters the global image mask colours. Called by InitExplosions (), though you
; can call it between calls to RenderExplosion () or StartRealtimeExplosion ()
; if you INSIST on using different RGB masks for different images! :P

; PARAMETERS
; -----------------------------------------------------------------------------
; r, g, b -- transparency mask colours used by your image(s)...

Function SetExplosionRGB (r, g, b)
	EXPLODE_MaskR = r					; Images' mask red value
	EXPLODE_MaskG = g					; Images' mask green value
	EXPLODE_MaskB = b					; Images' mask blue value
End Function

; INITEXPLOSIONS ()
; -----------------------------------------------------------------------------
; Store width and height of display for quick access, and (IMPORTANT) sets the RGB
; transparency mask used by your images (NOTE: I've assumed you'll generally use the
; same mask value for all your images; if you do need to change this for different
; images between calls to RenderExplosion () or StartRealtimeExplosion (), use
; SetExplosionRGB ()). Simplest option is to make all your images use the same mask
; colour!

; PARAMETERS
; -----------------------------------------------------------------------------
; r, g, b -- transparency mask colours used by your image(s)...

Function InitExplosions (r = 0, g = 0, b = 0)
	EXPLODE_GW = GraphicsWidth ()		; Width of display	} For use by
	EXPLODE_GH = GraphicsHeight ()		; Height of display } UpdateExplosions
	SetExplosionRGB (r, g, b)
	SeedRnd MilliSecs ()
End Function

; RENDEREXPLOSION ()
; -----------------------------------------------------------------------------
; Takes an image and pre-renders the split images required, plus pre-calcs the appropriate
; angles for each image (force is added when StartRenderedExplosion () is called). Returns
; a handle to the pre-rendered explosion of type .RenderedExplosion, which you pass to the
; StartRenderedExplosion () function, like so:

; blah.RenderedExplosion = eRenderExplosion (... )
; 	...
; StartRenderedExplosion (blah, [...])

; THIS SHOULD BE CARRIED OUT BEFORE YOUR MAIN LOOP!

; PARAMETERS
; -----------------------------------------------------------------------------
; image				-- the image to be exploded...
; splitx			-- number of horizontal 'splits'...
; splity			-- number of vertical 'splits'...
; frame				-- image frame number (not really tested!)...

Function RenderExplosion.RenderedExplosion (image, splitx = 8, splity = 8, frame = 0)

	buffer = GraphicsBuffer ()							; Store current draw buffer (probably back)
	SetBuffer ImageBuffer (image)						; Use image's buffer
	iW = ImageWidth (image)								; Store width of image
	iH = ImageHeight (image)							; Store height of image
	cx# = iW / 2										; Pre-calc half of width
	cy# = iH / 2										; Pre-calc half of height

	re.RenderedExplosion = New RenderedExplosion		; Created a pre-rendered explosion

	; This section 'reads' the image in a grid split by splitx and splity,
	; from top down, like so...
	;
	; ||||| OOOOOOOOOO
	; ||||| OOOOOOOOOO
	; ||||| OOOOOOOOOO
	; ||||V OOOOOOOOOO
	; ||||  OOOOOOOOOO
	; |||| OOOOOOOOOOO
	; |||| OOOOOOOOOOO
	; |||| OOOOOOOOOOO

	While px < iW - 1
		While py < iH - 1
			splitxt = splitx							; Temporary splitx } Because...
			splityt = splity							; Temporary splity } ...
			If (px + splitx) > (iW - 1)
				splitxt = iW - px						; Extreme right pieces may be narrower
			EndIf
			If (py + splity) > (iH - 1)
				splityt = iH - py						; Extreme bottom pieces may be shorter
			EndIf
			
			; Check if this section of grid is empty (avoids creating TONS of blank particles)...

			If ImageRectCollide (image, 0, 0, frame, px, py, splitx, splity)
			
				re\rep.RenderedExplosionParticle = New RenderedExplosionParticle ; Created pre-rendered particle
				re\rep\rendered = re								; Handle to pre-rendered explosion instance (IDs this particle as belonging to this type of explosion)...
				re\rep\sprite = CreateImage (splitxt, splityt)		; Make an image of grid section size

				GrabImage re\rep\sprite, px, py						; Grab this part of grid into it
				MaskImage re\rep\sprite, EXPLODE_MaskR, EXPLODE_MaskG, EXPLODE_MaskB ; Mask it

				re\rep\x = px									; Particle's x/y position is image's x/y ->
				re\rep\y = py									; -> position plus current grid position

				; Um... trial and error to 'correct' (ie. randomise) some weirdness with the
				; middle row of particles (stayed in a straight line in some cases). Works :)
				
				xdiff# = cx - px
				ydiff# = cy - py
				ydiff2# = py - cy
				
				If xdiff = 0 Then xdiff = Rnd (8)
				If ydiff = 0 Then ydiff = Rnd (8)
				If ydiff2 = 0 Then ydiff2 = -ydiff
				
				ang# = ATan2 (ydiff, xdiff)						; Angle of grid section from centre
				sinang# = Sin (ang)								; Used twice, so we'll pre-calc it once :)
				vector# = (ydiff2) / sinang						; Speed vector (?) of particle (Pythagoras translation: hypotenuse = opposite / sine of angle)
				If vector = 0 Then vector = Rnd (8)				; 'Correction' of some more weirdness I don't understand...
				
				re\rep\xs = Cos (ang) * vector * Rnd (EXPLODE_DefaultMinRand, EXPLODE_DefaultMaxRand)	; Calculate x speed of particle from angle, vector and force (cosine of angle * vector gets x component; this is then multiplied by the force, and randomise)...
				re\rep\ys = sinang * vector * Rnd (EXPLODE_DefaultMinRand, EXPLODE_DefaultMaxRand)		; Calculate y speed of particle from angle, vector and force (csine of angle * vector gets y component; this is then multiplied by the force, and randomise)...

			EndIf
			py = py + splity							; Next line down...
		Wend											; Done this column, so...
		px = px + splitx								; ... next line across
		py = 0											; Go to top of grid
	Wend
	SetBuffer buffer									; Reset to buffer stored at start
	
	Return re											; Return handle to pre-rendered explosion
	
End Function

; STARTRENDEREDEXPLOSION ()
; -----------------------------------------------------------------------------
; Sets off an explosion using a handle returned by RenderExplosion (). Copies each pre-rendered
; particle into a 'real' particle for on-screen display/update by UpdateExplosions (). Also adds
; force to the pre-calculated angles, allowing for lots of variation from the same pre-calc'd
; explosion data...

; PARAMETERS
; -----------------------------------------------------------------------------
; rend				-- handle to a pre-rendered explosion (got from RenderExplosion ())...
; x					-- x position on screen for explosion to start...
; y					-- y position on screen for explosion to start...
; xs				-- x speed (optional) of a moving image...
; ys				-- y speed (optional) of a moving image...
; force				-- force of explosion... use 0 for auto-calculation from xs and ys values. Note that
;					   if these are both zero, the default force (EXPLODE_DefaultForce) will be used!

Function StartRenderedExplosion (rend.RenderedExplosion, x#, y#, xs# = 0, ys# = 0, force# = EXPLODE_DefaultForce)
	If force = 0
		force = Abs ((ys / Sin (ATan (ys / xs))) * EXPLODE_DefaultModifier) ; Auto-calc force from xs and ys...
		If force = 0 Then force = EXPLODE_DefaultForce
	EndIf
	For rep.RenderedExplosionParticle = Each RenderedExplosionParticle
		If rep\rendered = rend
			p.ExplosionParticle = New ExplosionParticle		; Create a new particle
			p\prerendered = True
			p\sprite = rep\sprite
			p\x = x + rep\x									; Particle's x/y position is image's x/y ->
			p\y = y + rep\y									; -> position plus current grid position
			p\xs = (rep\xs * force) + xs
			p\ys = (rep\ys * force) + ys
			EXPLODE_Num = EXPLODE_Num + 1
		EndIf
	Next
End Function

; STARTREALTIMEEXPLOSION ()
; -----------------------------------------------------------------------------
; Starts a realtime (ie. not pre-rendered) explosion. Good for small-ish images if you don't
; want a pre-rendered version taking up your graphics memory. Note that this effectively
; does the same as RenderExplosion (), but the differences are that it is slower for
; large/lots of images, but takes up less memory (since it doesn't have to keep a pre-rendered
; version in graphics memory)...

; PARAMETERS
; -----------------------------------------------------------------------------
; image				-- image to be exploded in realtime...
; x					-- x position on screen for explosion to start...
; y					-- y position on screen for explosion to start...
; xs				-- x speed (optional) of a moving image...
; ys				-- y speed (optional) of a moving image...
; force				-- force of explosion...
; splitx			-- number of horizontal 'splits'...
; splity			-- number of vertical 'splits'...
; frame				-- image frame number (not really tested!)...

Function StartRealtimeExplosion (image, x#, y#, xs# = 0, ys# = 0, force# = EXPLODE_DefaultForce, splitx = 8, splity = 8, frame = 0)
	If force = 0 Then force = Abs ((ys / Sin (ATan (ys / xs))) * EXPLODE_DefaultModifier): If force = 0 Then force = EXPLODE_DefaultForce
	buffer = GraphicsBuffer ()							; Store current draw buffer (probably back)
	SetBuffer ImageBuffer (image)						; Use image's buffer
	iW = ImageWidth (image)								; Store width of image
	iH = ImageHeight (image)							; Store height of image
	cx# = iW / 2										; Pre-calc half of width
	cy# = iH / 2										; Pre-calc half of height

	; This section 'reads' the image in a grid split by splitx and splity,
	; from top down, like so...
	;
	; ||||| OOOOOOOOOO
	; ||||| OOOOOOOOOO
	; ||||| OOOOOOOOOO
	; ||||V OOOOOOOOOO
	; ||||  OOOOOOOOOO
	; |||| OOOOOOOOOOO
	; |||| OOOOOOOOOOO
	; |||| OOOOOOOOOOO

	While px < iW - 1
		While py < iH - 1
			splitxt = splitx							; Temporary splitx } Because...
			splityt = splity							; Temporary splity } ...
			If (px + splitx) > (iW - 1)
				splitxt = iW - px						; Extreme right pieces may be narrower
			EndIf
			If (py + splity) > (iH - 1)
				splityt = iH - py						; Extreme bottom pieces may be shorter
			EndIf

			; Check if this section of grid is empty (avoids creating TONS of blank particles
			; at little cost time-wise in most cases)...

			If ImageRectCollide (image, 0, 0, frame, px, py, splitx, splity)
				EXPLODE_Num = EXPLODE_Num + 1					; Increase global particle counter
				p.ExplosionParticle = New ExplosionParticle		; Create a new particle
				p\sprite = CreateImage (splitxt, splityt)		; Make an image of grid section size
				GrabImage p\sprite, px, py						; Grab this part of grid into it
				MaskImage p\sprite, EXPLODE_MaskR, EXPLODE_MaskG, EXPLODE_MaskB ; Mask it
				p\x = x + px									; Particle's x/y position is image's x/y ->
				p\y = y + py									; -> position plus current grid position
				ang# = ATan2 (cy - py, cx - px)					; Angle of grid section from centre
				sinang# = Sin (ang)								; Used twice, so we'll pre-calc it once :)
				vector# = (py - cy) / sinang					; Speed vector of particle (Pythagoras translation: hypotenuse = opposite / sine of angle)
				p\xs = xs + (Cos (ang) * vector * force * Rnd (EXPLODE_DefaultMinRand, EXPLODE_DefaultMaxRand))	; Calculate x speed of particle from angle, vector and force (cosine of angle * vector gets x component; this is then multiplied by the force, added to the required x speed, and randomised)...
				p\ys = ys + (sinang * vector * force * Rnd (EXPLODE_DefaultMinRand, EXPLODE_DefaultMaxRand))	; Calculate y speed of particle from angle, vector and force (csine of angle * vector gets y component; this is then multiplied by the force, added to the required y speed, and randomised)...
			EndIf
			py = py + splity							; Next line down...
		Wend											; Done this column, so...
		px = px + splitx								; ... next line across
		py = 0											; Go to top of grid
	Wend
	SetBuffer buffer									; Reset to buffer stored at start
End Function

; UPDATEEXPLOSIONS ()
; -----------------------------------------------------------------------------
; Updates position of particles, draws them, and deletes them if off-screen. Called 'somewhere
; before Flip'...

; PARAMETERS
; -----------------------------------------------------------------------------
; NONE.

Function UpdateExplosions ()
	For p.ExplosionParticle = Each ExplosionParticle	; Process each particle
		p\x = p\x + p\xs								; Move particle by x speed
		p\ys = p\ys + EXPLODE_Gravity					; Add gravity to particle's y speed
		p\y = p\y + p\ys								; Move particle by y speed
		DrawImage p\sprite, p\x, p\y					; Draw particle
; -----------------------------------------------------------------------------
; You may wish to modify the condition check here for killing particles...
; -----------------------------------------------------------------------------
		If ((((p\x - 0) Xor (p\x - EXPLODE_GW)) And ((p\y - 0) Xor (p\y - EXPLODE_GH))) And $80000000) = 0 ; Fast 'in-box' code possibly by Shagwana or AntonyDB? :)
; -----------------------------------------------------------------------------
			If p\prerendered = 0 Then FreeImage p\sprite; Delete image only if NOT pre-rendered (pre-rendered particles use the image from the RenderedExplosionParticle store, which therefore needs to be kept)...
			Delete p									; Delete particle if off-screen
			EXPLODE_Num = EXPLODE_Num - 1
		EndIf
; -----------------------------------------------------------------------------
	Next
End Function

; COUNTEXPLOSIONPARTICLES ()
; -----------------------------------------------------------------------------
; Returns the number of particles in a pre-rendered explosion...

; PARAMETERS
; -----------------------------------------------------------------------------
; rend				-- handle of a pre-rendered explosion returned by RenderExplosion ()

Function CountRenderedExplosionParticles (rend.RenderedExplosion)
	For rep.RenderedExplosionParticle = Each RenderedExplosionParticle
		If rep\rendered = rend
			count = count + 1
		EndIf
	Next
	Return count
End Function

; FREERENDEREDEXPLOSION ()
; -----------------------------------------------------------------------------
; Frees the images and data associated with a pre-rendered explosion, meaning it
; can no longer be used (although your game wonn't crash if you still try to use
; the explosion -- in fact, this can be used while the explosion is taking place,
; although that's not recommended since it'll look a bit crap most of the time :)

; PARAMETERS
; -----------------------------------------------------------------------------
; rend				-- handle of a pre-rendered explosion returned by RenderExplosion ()
; copydrawing		-- normally particles being drawn when this function is called will just
;					   be deleted. If you set copydrawing to True, it will make a copy of any
;					   pre-rendered particle that's about to be deleted, but this can cause
;					   a 'jerk' if there are still lots on-screen. Try to only call this
;					   function WITHOUT copydrawing when there are no explosions happening!

Function FreeRenderedExplosion (rend.RenderedExplosion, copydrawing = False)
	For rep.RenderedExplosionParticle = Each RenderedExplosionParticle		; Loop through all pre-rendered particles
		If rep\rendered = rend												; One from this pre-rendered explosion?
			For p.ExplosionParticle = Each ExplosionParticle				; Check currently drawing particles...
				If p\sprite = rep\sprite									; Using the one we're about to delete?
					If copydrawing
						p\sprite = CopyImage (rep\sprite)					; Either make a copy of the image...
					Else
						Delete p											; Or just delete the particle (default)...
					EndIf
				EndIf
			Next
			FreeImage rep\sprite											; Free the pre-rendered particle image
			Delete rep														; Delete the particle
		EndIf
	Next
	rend.RenderedExplosion = Null											; Set the explosion handle to NULL so it can't be used...
End Function

Comments

Doggie2010
404 error. Yeah, I know, it was 7 years ago but looks like it's worth the download.


Jesse2010
it works, you just got to learn to read :).

try this with your dog image:
Graphics 800,600,32

InitExplosions ()
Local image = LoadImage("24n441v.jpg")
Local re.RenderedExplosion = RenderExplosion (image)

While Not KeyDown(1)
	Cls

	If KeyDown(57) Then
		StartRenderedExplosion (re,300,300)
	EndIf
	UpdateExplosions()
 	Flip()

Wend 



BlitzSupport2010
Re-uploaded -- try the link again!


Doggie2010
Thank you.


Code Archives Forum