[bb] Explode! by BlitzSupport [ 1+ years ago ]

Started by BlitzBot, June 29, 2017, 00:28:41

Previous topic - Next topic

BlitzBot

Title : Explode!
Author : BlitzSupport
Posted : 1+ years ago

Description : 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 <a href="http://www.hi-toro.com/blitz/explosions.zip" target="_blank">this archive</a> (18kB), complete with a couple of demos :)

UPDATED: Now with pre-rendering of explosions -- we're good for realtime now!


Code :
Code (blitzbasic) Select
; 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)

reep.RenderedExplosionParticle = New RenderedExplosionParticle ; Created pre-rendered particle
reependered = re ; Handle to pre-rendered explosion instance (IDs this particle as belonging to this type of explosion)...
reepsprite = CreateImage (splitxt, splityt) ; Make an image of grid section size

GrabImage reepsprite, px, py ; Grab this part of grid into it
MaskImage reepsprite, EXPLODE_MaskR, EXPLODE_MaskG, EXPLODE_MaskB ; Mask it

reepx = px ; Particle's x/y position is image's x/y ->
reepy = 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...

reepxs = 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)...
reepys = 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 rependered = rend
p.ExplosionParticle = New ExplosionParticle ; Create a new particle
pprerendered = True
psprite = repsprite
px = x + repx ; Particle's x/y position is image's x/y ->
py = y + repy ; -> position plus current grid position
pxs = (repxs * force) + xs
pys = (repys * 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
psprite = CreateImage (splitxt, splityt) ; Make an image of grid section size
GrabImage psprite, px, py ; Grab this part of grid into it
MaskImage psprite, EXPLODE_MaskR, EXPLODE_MaskG, EXPLODE_MaskB ; Mask it
px = x + px ; Particle's x/y position is image's x/y ->
py = 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)
pxs = 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)...
pys = 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
px = px + pxs ; Move particle by x speed
pys = pys + EXPLODE_Gravity ; Add gravity to particle's y speed
py = py + pys ; Move particle by y speed
DrawImage psprite, px, py ; Draw particle
; -----------------------------------------------------------------------------
; You may wish to modify the condition check here for killing particles...
; -----------------------------------------------------------------------------
If ((((px - 0) Xor (px - EXPLODE_GW)) And ((py - 0) Xor (py - EXPLODE_GH))) And $80000000) = 0 ; Fast 'in-box' code possibly by Shagwana or AntonyDB? :)
; -----------------------------------------------------------------------------
If pprerendered = 0 Then FreeImage psprite; 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 rependered = 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 rependered = rend ; One from this pre-rendered explosion?
For p.ExplosionParticle = Each ExplosionParticle ; Check currently drawing particles...
If psprite = repsprite ; Using the one we're about to delete?
If copydrawing
psprite = CopyImage (repsprite) ; Either make a copy of the image...
Else
Delete p ; Or just delete the particle (default)...
EndIf
EndIf
Next
FreeImage repsprite ; 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 :


Doggie(Posted 1+ years ago)

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


Jesse(Posted 1+ years ago)

 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



BlitzSupport(Posted 1+ years ago)

 Re-uploaded -- try the link again!


Doggie(Posted 1+ years ago)

 Thank you. [/i]