This is the code that displays particles :
// --------------------------------- //
// Project: TestParticleEmitter
// Start: Saturday, May 19, 2012
// IDE Version: 11.001
TYPE tParticle
m_fAge
m_fScale
m_fRotation
m_fVelocityX
m_fVelocityY
m_fRed
m_fBlue
m_fGreen
m_fAlpha
m_fX
m_fY
ENDTYPE
TYPE TParticleEmitter
m_freeParticles[] AS tParticle
m_liveParticles[] AS tParticle
m_strName$
m_baseParticle AS tParticle
m_fPointWeight = 1.0
m_fScaleFactor = - 0.2
m_fScaleMin = 0.5
m_fScaleMax = 1.0
m_fRotationFactor = 720.0
m_fVelocityMin = 9.0
m_fVelocityMax = 100.0
m_fRedFactor = 0.0
m_fBlueFactor = 0.0
m_fGreenFactor = 0.0
m_fAlphaFactor = -1.0
m_fMinDistance
m_fMaxDistance
m_fConeAngle = 360
m_fDirection
m_particleFile$
m_iParticlesPerBurst% = 20
m_Image% = -1
m_iWidth%;m_iHeight%
m_iXHandle%; m_iYHandle%
m_iAlphaBlend% = 1
m_lastBurstTime%
FUNCTION Initialise%:fileName$,ignoreImage%
LOCAL handle%, strLine$,key$,value$,val
self.Destroy()
DIM self.m_freeParticles[self.ReturnMaxParticles()]
IF fileName$<>""
handle%=GENFILE()
IF handle%<0 THEN RETURN FALSE
IF OPENFILE(handle%,fileName$,1)
READLINE handle%,strLine$
WHILE NOT(ENDOFFILE(handle%))
IF LEN(strLine$) > 0 AND LEFT$( strLine$, 1 ) <> "'"
key$ = LEFT$(strLine$,INSTR(strLine$,"="))
value$=RIGHT$(strLine$, LEN( strLine$ ) - INSTR( strLine$, "=" )-1)
val=value$
SELECT key$
CASE "point_weight"
self.m_fPointWeight = val
CASE "point_scalefactor"
self.m_fScaleFactor = val
CASE "point_scale"
self.m_fScaleMin = val
self.m_fScaleMax = val
CASE "point_scale_min"
self.m_fScaleMin = val
CASE "point_scale_max"
self.m_fScaleMax = val
CASE "point_rotationfactor"
self.m_fRotationFactor = val
CASE "point_coneangle"
self.m_fConeAngle = val
CASE "point_mindistance"
self.m_fMinDistance = val
CASE "point_maxdistance"
self.m_fMaxDistance = val
CASE "point_velocity_min"
self.m_fVelocityMin = val
CASE "point_velocity_max"
self.m_fVelocityMax = val
CASE "point_redfactor"
self.m_fRedFactor = val
CASE "point_red"
self.m_baseParticle.m_fRed = INTEGER(val)
CASE "point_greenfactor"
self.m_fGreenFactor = val
CASE "point_green"
self.m_baseParticle.m_fGreen = INTEGER(val)
CASE "point_bluefactor"
self.m_fBlueFactor = val
CASE "point_blue"
self.m_baseParticle.m_fBlue = INTEGER(val)
CASE "point_alphafactor"
self.m_fAlphaFactor = val
CASE "point_alpha"
self.m_baseParticle.m_fAlpha = val
CASE "emitter_particles_per_burst"
self.m_iParticlesPerBurst% = INTEGER(val)
CASE "image"
IF ignoreImage%=FALSE THEN self.LoadSpr(value$)
CASE "xhandle"
self.m_iXHandle% = INTEGER(val)
CASE "yhandle"
self.m_iYHandle% = INTEGER(val)
CASE "blendmode"
self.m_iAlphaBlend% = INTEGER(val)
ENDSELECT
ENDIF
READLINE handle%,strLine$
WEND
CLOSEFILE handle%
DEBUG "Loaded\n"
RETURN TRUE
ELSE
RETURN FALSE
ENDIF
ENDIF
RETURN FALSE
ENDFUNCTION
FUNCTION Destroy%:
DIM self.m_liveParticles[0]
DIM self.m_freeParticles[0]
ENDFUNCTION
FUNCTION Finish%:
self.UnloadSprite()
self.Destroy()
ENDFUNCTION
FUNCTION UnloadSprite%:
IF self.m_Image%>=0 THEN LOADSPRITE "",self.m_Image%
self.m_Image%=-1
ENDFUNCTION
FUNCTION LoadSpr%:fileName$
self.m_Image%=GENSPRITE()
IF self.m_Image%>=0
self.m_particleFile$=fileName$
LOADSPRITE self.m_particleFile$,self.m_Image%
GETSPRITESIZE self.m_Image%,self.m_iWidth%,self.m_iHeight%
IF self.m_iWidth%=0 OR self.m_iHeight%=0 THEN self.m_Image%=-1
DEBUG "Sprite size : "+self.m_iWidth%+" "+self.m_iWidth%+"\n"
ENDIF
IF self.m_Image%>=0
RETURN TRUE
ELSE
RETURN FALSE
ENDIF
ENDFUNCTION
FUNCTION Draw%:
LOCAL p AS tParticle
IF self.m_Image%=-1 THEN RETURN FALSE
FOREACH p IN self.m_liveParticles[]
ALPHAMODE ABS(p.m_fAlpha)*SGN(self.m_iAlphaBlend%-1.0)
self.z_rotozoomanim(self.m_Image%,p.m_fX,p.m_fY,self.m_iWidth%,self.m_iHeight%,p.m_fRotation,p.m_fScale, _
RGB(p.m_fRed*255.0,p.m_fGreen*255.0,p.m_fBlue*255.0))
NEXT
RETURN TRUE
ENDFUNCTION
FUNCTION Update%:fTime
LOCAL p AS tParticle
FOREACH p IN self.m_liveParticles[]
INC p.m_fAlpha,self.m_fAlphaFactor * fTime
INC p.m_fScale,self.m_fScaleFactor * fTime
IF p.m_fAlpha <= 0.0 OR p.m_fScale <= 0.0
DIMPUSH self.m_freeParticles[],p
DELETE p
ELSE
INC p.m_fAge,fTime
INC p.m_fRotation,self.m_fRotationFactor * fTime
DEC p.m_fVelocityX,self.m_fPointWeight * fTime
DEC p.m_fVelocityY,self.m_fPointWeight * fTime
INC p.m_fX,p.m_fVelocityX * fTime
INC p.m_fY,p.m_fVelocityY * fTime
INC p.m_fY,self.m_fPointWeight * (p.m_fAge * p.m_fAge)
INC p.m_fRed,self.m_fRedFactor * fTime
INC p.m_fBlue,self.m_fBlueFactor * fTime
INC p.m_fGreen,self.m_fGreenFactor * fTime
ENDIF
NEXT
//ASSERT ( m_freeParticles.Count() + m_liveParticles.Count() = MAX_PARTICLES_PER_EMITTER ) 'ensure particles don't exist IN both lists!!
ENDFUNCTION
FUNCTION SetDirection%:a
self.m_fDirection = a
DEBUG "Direction : "+self.m_fDirection+"\n"
ENDFUNCTION
FUNCTION DoBurst%:iX%,iY%,fpsSync%=TRUE
LOCAL count%
LOCAL p AS tParticle
IF fpsSync%
LOCAL timeNow%
timeNow=INTEGER(GETTIMERALL())
IF timeNow - self.m_lastBurstTime >= 20
self.m_lastBurstTime = timeNow
ELSE
RETURN FALSE
ENDIF
ENDIF
count%=self.m_iParticlesPerBurst%
WHILE BOUNDS(self.m_freeParticles[],0)<count%
DIMPUSH self.m_freeParticles[],self.m_liveParticles[0]
DIMDEL self.m_liveParticles[],0
WEND
FOREACH p IN self.m_freeParticles[]
LOCAL angle,velocity,distance
DEC count%
IF count%<0
RETURN TRUE
ENDIF
p=self.m_baseParticle
angle= (self.RndFloat()*self.m_fConeAngle)+(self.m_fDirection - ( self.m_fConeAngle / 2.0 ))
p.m_fScale = self.m_fScaleMin + (self.RndFloat()*(self.m_fScaleMax - self.m_fScaleMin ) )
velocity = self.m_fVelocityMin + (self.RndFloat()*(self.m_fVelocityMax - self.m_fVelocityMin ))
p.m_fVelocityX = COS( angle ) * velocity
p.m_fVelocityY = SIN( angle ) * velocity
distance = self.m_fMinDistance + (RndFloat()*(self.m_fMaxDistance - self.m_fMinDistance ))
p.m_fX = iX + (COS( angle ) * distance )
p.m_fY = iY + (SIN( angle ) * distance )
DIMPUSH self.m_liveParticles[],p
DELETE p
NEXT
RETURN TRUE
ENDFUNCTION
FUNCTION LiveCount%:
RETURN BOUNDS(self.m_liveParticles[],0)
ENDFUNCTION
FUNCTION RndFloat:
RETURN RND(100)/100.0
ENDFUNCTION
FUNCTION ReturnMaxParticles%:
RETURN 600
ENDFUNCTION
FUNCTION z_rotozoomanim: num%,_x,_y,w%,h%,_angle,_scale,_col%=0xFFFFFF
LOCAL sinp, cosp, spw, sph, dx, dy
LOCAL hx, hy, dxp, dxm, dyp, dym
sinp = SIN(_angle)
cosp = COS(_angle)
spw = w%-0.5
sph = h%-0.5
dx=spw/2
dy=sph/2
hx = (dx - (w%-self.m_iXHandle%))
hy = (dy - (h%-self.m_iYHandle%))
dxp = (dx+hx) * _scale
dxm = (dx-hx) * _scale
dyp = (dy+hy) * _scale
dym = (dy-hy) * _scale
STARTPOLY num%
POLYVECTOR _x - cosp * dxm - sinp * dym, _y - cosp * dym + sinp * dxm, 0.5, 0.5, _col
POLYVECTOR _x - cosp * dxm + sinp * dyp, _y + cosp * dyp + sinp * dxm, 0.5, sph, _col
POLYVECTOR _x + cosp * dxp + sinp * dyp, _y + cosp * dyp - sinp * dxp, spw, sph, _col
POLYVECTOR _x + cosp * dxp - sinp * dym, _y - cosp * dym - sinp * dxp, spw, 0.5, _col
ENDPOLY
ENDFUNCTION
ENDTYPE
Thanks a lot for the code... I take a look, after :nw: :nw: :nw:
I second mentalthink's response.
A particle generator was on my to-do list for a while, but it seemed like too much to get into at any time.
I can't wait to plug this into my game and see how much more 'polish' it will add!
(ha, assuming I can create a nice looking particle configuration!)
Thanks again, great job! :good:
Glad you all like it!
pretty cool, pretty code too!
You guys have seen spritez?
i spent over a year working on that particle engine.
It has blackholes, fans, no fly zones, collision detection...and so much more.
It even can spawn particles based on an bmp image.
Unfortunetly i dimmed the memory blocks, instead of using types, but if you limit the particle count it will run fast.
edit:
ps i made my avatar using spritez, 1000 particles spawning on the bitmap spawnmap function(pheonix outline), background image(pheonix), quick lived particles(fireball), and a fan in the center to push them outwards.
pps. the sytem isnt perfect, it can be sped up quite a bit(Ocean once showed me a fast tweaked version of the engine, based on common C+ programming optimizations). I am not a professional programmer, optimizing really isnt my forte.
Yes, its very good!
Could do with being put in an extended type though :)
Quote from: Hemlos on 2012-Jun-11
You guys have seen spritez?
i spent over a year working on that particle engine.
It has blackholes, fans, no fly zones, collision detection...and so much more.
It even can spawn particles based on an bmp image.
Yeah, SpriteZ is a solid stuff, I've made an Android APK from that last year: https://dl.dropbox.com/u/292449/Android/apks/Particle.apk
MrTAToad: Ok so what type of file it could be read? I remember that you had a particle editor, but can't find that forum thread anymore. Please put a little bit more info and link into the first post. Thank you!
Will do soon!
Quote from: Albert on 2012-Jun-13
Yeah, SpriteZ is a solid stuff, I've made an Android APK from that last year: https://dl.dropbox.com/u/292449/Android/apks/Particle.apk
not sure what this does.
Im glad the engine wasnt a complete waste of time heh.
@toad: i agree, it would be easier to tweak with TYPE, to be honest with you, im not adept enough with types to be able to convert it properly(my brain hurts when i mess with them, because im so used to using multiple dimension memory.)
PS@all: im going to do my best to clean up the api, and im going to try and finish spritez-3d this year...ive been burned out from this monster...i really could use some help with converting the memory. Spritez3d will be used in a 3d flight sim which uses a quaternion camera..thats how im going to fine tune the thing. I know youve heard all this before...im only one man, trying to move a mountain.
@Hemlos, it wasn't a waste of time. I used it frequently in my stuff! ;)
Uh-ho: there are some bugs in the code:
CASE "point_red"
self.m_baseParticle.m_fRed = INTEGER(val)
CASE "point_greenfactor"
self.m_fGreenFactor = val
CASE "point_green"
self.m_baseParticle.m_fGreen = INTEGER(val)
CASE "point_bluefactor"
self.m_fBlueFactor = val
CASE "point_blue"
self.m_baseParticle.m_fBlue = INTEGER(val)
should be:
CASE "point_red"
self.m_baseParticle.m_fRed = val
CASE "point_greenfactor"
self.m_fGreenFactor = val
CASE "point_green"
self.m_baseParticle.m_fGreen = val
CASE "point_bluefactor"
self.m_fBlueFactor = val
CASE "point_blue"
self.m_baseParticle.m_fBlue = val
as you using this as:
self.z_rotozoomanim(self.m_Image%,p.m_fX,p.m_fY,self.m_iWidth%,self.m_iHeight%,p.m_fRotation,p.m_fScale, _
RGB(p.m_fRed*255.0,p.m_fGreen*255.0,p.m_fBlue*255.0))
Also I think this bug is present in the Particle Editor too.
I don't like when one of the color component overflows (254, 255, 0, 1, 2) and suddenly changes the color of the particle, so I'm using this in Update%(fTime):
p.m_fRed = MIN(1.0, MAX(0.0, p.m_fRed))
p.m_fBlue = MIN(1.0, MAX(0.0, p.m_fBlue))
p.m_fGreen = MIN(1.0, MAX(0.0, p.m_fGreen))
I write out the LiveParticles count on the screen, and I'm experienced, that particles live more longer than they disappeared (by reducing alpha) So i modified the expression in:
IF p.m_fAlpha <= 0.00 OR p.m_fScale <= 0.0
DIMPUSH self.m_freeParticles[],p
DELETE p
ELSE
to:
IF p.m_fAlpha <= 0.05 OR p.m_fScale <= 0.0
This produces a more satisfying particle lifecycle, and better framerate on Android.