Demo : Opensource Particle FX Engine (Work In Progress)

I can’t afford the truly remarkable particle candy, so I decided to start hammering out something to do the job.
I’ve been using corona for about 15 hours total so far, so please excuse (and by all means correct) the sloppy code.

I repeat, this is a work in progress, I’ve spent the last two nights on it (mostly spent debugging stupid errors), and any suggestions, improvements, comments, or nasty criticism is appreciated :smiley:

http://ghost.net.nz/iParticleFX.zip

oh, if anyone wants to do a device build, I’d hate to spend a month making something polished and find it maxes out at 2fps :stuck_out_tongue:

Oh, and feel free to use it in part or entirety for a credit :stuck_out_tongue: [import]uid: 34945 topic_id: 9078 reply_id: 309078[/import]

Looks great. Thanks for sharing. [import]uid: 2334 topic_id: 9078 reply_id: 36700[/import]

I’ll try uploading the updated version tonight. Its a lot fancier :wink: [import]uid: 34945 topic_id: 9078 reply_id: 36710[/import]

Nice :slight_smile: [import]uid: 39088 topic_id: 9078 reply_id: 36717[/import]

http://ghost.net.nz/iParticleFXv0_1.zip

Probably full of comments and redundant code, but its probably a better demonstration :slight_smile: [import]uid: 34945 topic_id: 9078 reply_id: 36770[/import]

Let me start by saying: Really good. I didn’t want to spend money on a particle engine quite yet and this certainly does the job. As to your comment on testing on a device, what I’m working on is close to done, and I did not intend to include a particle effects in my release, so I can give you a good idea of how things hold up in a close-to-release game that wasn’t built with your particle engine i mind…

On the iPhone 4 and 3GS, it works smoothly if the settings are kept moderate. An emitter rate above 35ish, maxing out particles around 30-ish, and keeping the life span of a particle relatively low (around 1-2k) keeps slowdown to a minimum. But slowdown becomes overwhelming as you move beyond those.

Your project is a really good candidate for an open-source alternative to something like particle candy. [import]uid: 36054 topic_id: 9078 reply_id: 37736[/import]

:smiley:
Great to see you managed to find a use for it!
If you could share your settings as a guide to others, that would be great.
And 30ish particles sounds good, I was a bit scared it would need further code optimization before those levels were reached.
Thanks heaps for the report, I’d shelved the project assuming noone would be interested, but I’ll keep developing it.
[import]uid: 34945 topic_id: 9078 reply_id: 37744[/import]

starParticle = {
image = “Star.png”,
rate=75,
v=6,
lifeSpan = 1000,
fadeStartTime = 1000,
path=iPFXparticlePaths.defaultsPath,
}

starParticlePlus = {
image = “Star.png”,
rate=25,
v=7.5,
–onDeath=testCallBack,
lifeSpan = 1200,
fadeStartTime = 1000,
–alpha = math.random(100)/500,
path=iPFXparticlePaths.starburstPath,
–rotation=‘rand’,
}

An important note (I made this change on my copy of your engine), you should change display.newImage to display.newImageRect anywhere it appears in your code in order to support scaling. Otherwise your particles can get ever so slightly fuzzy on the iPhone 4.

testEmitter = {
y=-300,
x=-300,
p=“static”,
particle=iPFXparticleConf.starParticle,
rate=35,
maxParticles=25,
lifeSpan=9999999999999999
}
testEmitter2 = {
y=-300,
x=-300,
p=“static”,
particle=iPFXparticleConf.starParticlePlus,
rate=20,
lifeSpan=7000,
maxParticles=50
}

Note: starParticlePlus/emitter2 would cause slowdown if I were to use it during gameplay, I only use it for animation on the main menu, when nothing else is really going on.

And yes, out of sheer luck I had intended to look at your engine to add trails of stars… talk about lucky :slight_smile: though my “Star” images are gold rather than see through like your demo.

[import]uid: 36054 topic_id: 9078 reply_id: 37750[/import]

I have fixed this to my project and it was just what I needed. I fixed the starting point and the path of the emitter so that it fits my project. I have one problem though, I can’t set the particles toFront() or toBack().

Does someone know how to do that? [import]uid: 24111 topic_id: 9078 reply_id: 47056[/import]

try this
iPFXparticle.lua modification

 function particle:destroy()  
 if self.onDeath then   
 --print("Callback")  
 self.onDeath(self)   
 end  
 printDebug("Particle Destroyed",system.getTimer())  
 timer.cancel(self.pathTimer)  
 decParticleCount()  
 parent.particleCount = parent.particleCount - 1  
 parent:removeParticle(self)  
 self:removeSelf()  
 self = nil  
 end  

iPFXemitter.lua

 function emitter:emit()  
 local particleCount = self.getParticleCount()  
 self.i = self.i+1  
 if self.maxParticles \> self.particleCount then  
 self.particle.path = self.particle.path or defaultPath  
 local p = iPFXparticle:new(self,self.particle)  
 --incParticleCount()  
 table.insert(self.particles,p)  
 print(#self.particles)  
 self.incParticleCount()  
 end  
 end   
 function emitter:removeParticle(particle)  
 for k,v in ipairs(self.particles) do  
 if v == particle   
 then table.remove(self.particles,k)  
 return true  
 end  
 end  
 end  

Then you should be able to do this to bring all of an emitters particles to front

for i = 1,#emittah.particles do  
 emittah.particles[i].toFront()  
end  

where emittah is the name of your emitter. [import]uid: 34945 topic_id: 9078 reply_id: 47223[/import]

Thanks for helping but what exactly should this replace:

 function emitter:emit()  
 local particleCount = self.getParticleCount()  
 self.i = self.i+1  
 if self.maxParticles \> self.particleCount then  
 self.particle.path = self.particle.path or defaultPath  
 local p = iPFXparticle:new(self,self.particle)  
 --incParticleCount()  
 table.insert(self.particles,p)  
 print(#self.particles)  
 self.incParticleCount()  
 end  
 end   
 function emitter:removeParticle(particle)  
 for k,v in ipairs(self.particles) do  
 if v == particle   
 then table.remove(self.particles,k)  
 return true  
 end  
 end  
 end  
  

Can you send me the full code of the modified lua files?

When I try I get this error:

Runtime error  
 filepath/iPFXparticle.lua:54: attempt to call global 'decParticleCount' (a nil value)  
stack traceback:  
 [C]: in function 'decParticleCount'  
 filepath/iPFXparticle.lua:54: in function 'destroy'  
 filepath/iPFXparticle.lua:37: in function '\_listener'  
 ?: in function <?:441>  
 ?: in function <?:214>  

[import]uid: 24111 topic_id: 9078 reply_id: 47381[/import]

iPFXemitter.lua
[lua]–Emitter.Lua
–Particle System Emitter Class

module(…, package.seeall)
printDebug(“Loading Emitter.lua”)

require(“iPFXparticle”)
require(“iPFXpaths”)

function new(self,parent,params)
params = params or {}
printDebug(system.getTimer())
printDebug(table.show(params,“New Emitter Params”))
local emitterImg = ‘blank.png’

if params.debug then
emitterImg = ‘crosshair.png’
end
local emitter = display.newImage(emitterImg,50,50)
–print(“LLLLL”,self.x)
emitter.x = params.x or parent.x
emitter.y = params.y or parent.y
emitter.particle = params.particle or {}
–print(table.show(params))
emitter.particles = {}
emitter.particleCount = 0
emitter.maxParticles = params.maxParticles or 500
printDebug("MAXPARTICLES = ",emitter.maxParticles)
emitter.lifeSpan = params.lifeSpan or 3000
emitter.rate = params.rate or 20
emitter.bounds = params.bounds or {y=1,x=1}
emitter.path = iPFXemitterPaths[params.p] or nil

local function followPath()
local j = 0
local t = emitter.lifeSpan/(#emitter.path)
printDebug(t)
for i,v in pairs(emitter.path) do
printDebug(i,v.x,v.y)
transition.to(emitter,{x=emitter.x + v.x,y=emitter.y + v.y,time=t,delay=t*j})
j = j + 1

end

end
if emitter.path then followPath() end

function emitter:getX()
return emitter.x
end

emitter.incParticleCount = function ()
emitter.particleCount = emitter.particleCount + 1
return emitter.particleCount
end
emitter.getParticleCount = function ()
return emitter.particleCount
end

function emitter:emit()
local particleCount = self.getParticleCount()
self.i = self.i+1
if self.maxParticles > self.particleCount then
self.particle.path = self.particle.path or defaultPath
local p = iPFXparticle:new(self,self.particle)
–incParticleCount()
table.insert(self.particles,p)
print(#self.particles)
self.incParticleCount()
end
end
function emitter:removeParticle(particle)
for k,v in ipairs(self.particles) do
if v == particle
then table.remove(self.particles,k)
return true
end
end
end

emitter.lifeTimer = timer.performWithDelay(emitter.lifeSpan,function() emitter:destroy() end,1)
emitter.rateTimer = timer.performWithDelay(emitter.rate,function() emitter:emit() end,(emitter.lifeSpan/emitter.rate))
emitter.i = 0

function emitter:destroy()
timer.cancel(emitter.rateTimer)
printDebug(“Emitter Destroyed”,system.getTimer())
self = nil
end

printDebug(“Emitter Created”,system.getTimer())

emitter:emit()
return emitter
end

function staticEmitter(self,params)
staticEmitter = emitter:new(params)

return staticEmitter
end

[/lua]
iPFXparticle.lua
[lua]–Particle.Lua
–Particle System Particle Class

module(…, package.seeall)
printDebug(“Loading Particle.lua”)

function new(self,parent,params)

local image = params.image or “1.png”
local particle = display.newImage(image,30,30)
particle.parent = parent
particle.onDeath = params.onDeath
–print(table.show(params))
printDebug("Particle Created, Number ",iPFXparticleID, “Particle Count For This Emitter”,parent.getParticleCount())
iPFXparticleID = iPFXparticleID +1
randomize()
particle.alpha = params.alpha or math.random(100)/100
local scale = math.random(100)/100 +.3
particle:scale(scale,scale)
particle.i = 1
particle.x = parent.x
particle.y = parent.y
particle.v = params.v or 5
particle.rotation = params.rotation or math.random(360)

math.randomseed(system.getTimer())
transition.to(particle,{time=300,alpha=0.55})
particle.d = params.d or math.random(360)
particle.fadeStartTime = params.fadeStartTime or 500
–print(params.path)
particle.path = params.path or defaultPath

particle.lifeSpan = params.lifeSpan or 1000
particle.rate = params.rate or 100
transition.to(particle,{delay=particle.fadeStartTime,time=particle.lifeSpan-particle.fadeStartTime,alpha=0})
particle.lifeTimer = timer.performWithDelay(particle.lifeSpan,function() particle:destroy() end,1)
particle.pathTimer = timer.performWithDelay(particle.rate,function() particle:followPath() end,particle.lifeSpan/particle.rate)

function particle:followPath()
local params = self.path(self.x,self.y,self.i,self.d,self.v)
self.i = params.i
–print(‘followPath’,params.x,params.y)
transition.to(self,{time=100,x=params.x,rotation=params.rotation,y=params.y,onComplete=function() end})
–transition.to(self,{time=1000,x=500,y=500})
end

function particle:destroy()
if self.onDeath then
–print(“Callback”)
self.onDeath(self)
end
printDebug(“Particle Destroyed”,system.getTimer())
timer.cancel(self.pathTimer)
decParticleCount()
parent.particleCount = parent.particleCount - 1
parent:removeParticle(self)
self:removeSelf()
self = nil
end

printDebug(“Particle Created”,system.getTimer())

return particle
end

[/lua] [import]uid: 34945 topic_id: 9078 reply_id: 47389[/import]

You are using the updated files I assume ?

http://ghost.net.nz/iParticleFXv0_1.zip
[import]uid: 34945 topic_id: 9078 reply_id: 47390[/import]

Yes I’m using the updated files and I still get the same error :confused: [import]uid: 24111 topic_id: 9078 reply_id: 47426[/import]

Thanks for helping but I realise now that i kind of looks better if it stays like it is. [import]uid: 24111 topic_id: 9078 reply_id: 47429[/import]

its a function in main.lua
it really should be in iPFXparticle.lua

but as I said, it was a proof of concept, not even alpha stage :wink:

once I get to implementing it in my game, I’ll release a cohesive version which should work properly first time, for newbies :smiley: [import]uid: 34945 topic_id: 9078 reply_id: 47437[/import]