From a7fd2205ef48ebe89824225db69a6cfa88403db1 Mon Sep 17 00:00:00 2001 From: Jesse <7832163+GammaGames@users.noreply.github.com> Date: Wed, 3 Jul 2024 07:01:38 +0000 Subject: [PATCH 1/9] WIP: add pooling --- pdParticles.lua | 337 ++++++++++++++++++++++-------------------------- 1 file changed, 157 insertions(+), 180 deletions(-) diff --git a/pdParticles.lua b/pdParticles.lua index 4f4f100..277151d 100644 --- a/pdParticles.lua +++ b/pdParticles.lua @@ -1,9 +1,11 @@ local particles = {} local precision = 1 +local particle_pool_size = 1 -- base particle class class("Particle").extends() -function Particle:init(x, y) +function Particle:init(x, y, pool_size) + pool_size = pool_size or particle_pool_size self.x = x or 0 self.y = y or 0 self.size = {1,1} @@ -13,7 +15,6 @@ function Particle:init(x, y) self.thickness = {0,0} self.lifespan = {1,1} self.decay = 1 - self.particles = {} self.colour = playdate.graphics.kColorBlack self.bounds = {0,0,0,0} self.mode = 0 @@ -28,16 +29,38 @@ function Particle:init(x, y) self.rotation = {0,359} end + self.partlist = table.create(pool_size, 0) + self.active = table.create(pool_size, 0) + self.available = table.create(pool_size, 0) + for index = 1, pool_size do + self.available[index] = index + end + self:create(pool_size) particles[#particles+1] = self +end +function Particle:resetPart(part) + part = part or {} + -- Stub function, should be overridden by child classes + return part or {} end -function Particle:remove() - for i = 1, #particles, 1 do - if particles[i] == self then - table.remove(particles, i) - break - end +function Particle:create(amount) + for index = 1, amount do + self.partlist[#self.partlist + 1] = self:resetPart() + self.available[index] = #self.partlist + end +end + +function Particle:add(amount) + if #self.available - amount < 0 then + self:create(amount - #self.available) + end + + for _ = 1, amount do + local index = table.remove(self.available) + table.insert(self.active, index) + self.partlist[index] = self:resetPart(self.partlist[index]) end end @@ -163,6 +186,8 @@ end function Particle:clearParticles() self.particles = {} + self.active = {} + self.available = {} end function Particle:update() @@ -170,103 +195,90 @@ end -- [[ PARTICLE MODES ]] -- -local function decay(partlist, decay) - for part = 1, #partlist, 1 do - local particle = partlist[part] - particle.size -= decay - - if particle.size <= 0 then - particle.size = 0 - end +local function remove(index, active, available) + table.insert(available, index) + table.remove(active, index) +end - partlist[part] = particle - end +local function decay(parts, active, available) + for i = #active, 1, -1 do + local index = active[i] + local part = parts[index] + part.size -= decay - for part = #partlist, 1, -1 do - local particle = partlist[part] - if particle.size <= 0 then - table.remove(partlist,part) + if part.size <= 0 then + part.size = 0 + remove(i, active, available) end end - - return partlist end -local function disappear(partlist) - for part = 1, #partlist, 1 do - local particle = partlist[part] +local function disappear(partlist, active, available) + for i = #active, 1, -1 do + local index = active[i] + local particle = partlist[index] particle.lifespan -= .1 - end - for part = #partlist, 1, -1 do - local particle = partlist[part] + if particle.lifespan <= 0 then - table.remove(partlist,part) + remove(i, active, available) end end - - return partlist end -local function loop(partlist, bounds) +local function loop(partlist, active, _, bounds) if bounds[3] > bounds[1] and bounds[4] > bounds[2] then local xDif , yDif = bounds[3] - bounds[1], bounds[4] - bounds[2] - for part = 1, #partlist, 1 do - local particle = partlist[part] + for index = 1, #active, 1 do + local particle = partlist[active[index]] if particle.x > bounds[3] then particle.x -= xDif elseif particle.x < bounds[1] then particle.x += xDif end if particle.y > bounds[4] then particle.y -= yDif elseif particle.y < bounds[2] then particle.y += yDif end end end - - return partlist end -local function stay(partlist, bounds) +local function stay(partlist, active, available, bounds) if bounds[3] > bounds[1] and bounds[4] > bounds[2] then - local xDif , yDif = bounds[3] - bounds[1], bounds[4] - bounds[2] - for part = #partlist, 1, -1 do - local particle = partlist[part] - if particle.x > bounds[3] then table.remove(partlist,part) - elseif particle.x < bounds[1] then table.remove(partlist,part) - elseif particle.y > bounds[4] then table.remove(partlist,part) - elseif particle.y < bounds[2] then table.remove(partlist,part) end + for i = #active, 1, -1 do + local index = active[i] + local particle = partlist[index] + if particle.x > bounds[3] then + remove(i, active, available) + elseif particle.x < bounds[1] then + remove(i, active, available) + elseif particle.y > bounds[4] then + remove(i, active, available) + elseif particle.y < bounds[2] then + remove(i, active, available) + end end end - - return partlist end class("ParticleCircle", {type = 1}).extends(Particle) -function ParticleCircle:create(amount) - for i = 1, amount, 1 do - local part = { - x = self.x, - y = self.y, - dir = math.random(self.spread[1],self.spread[2]), - size = math.random(self.size[1],self.size[2]) * precision, - speed = math.random(self.speed[1],self.speed[2]) * precision, - acceleration = math.random(self.acceleration[1],self.acceleration[2]) * precision, - lifespan = math.random(self.lifespan[1],self.lifespan[2]) * precision, - thickness = math.random(self.thickness[1],self.thickness[2]), - decay = self.decay - } - - self.particles[#self.particles+1] = part - end -end - -function ParticleCircle:add(amount) - self:create(amount) +function ParticleCircle:resetPart(part) + part = part or {} + part.x = self.x + part.y = self.y + part.dir = math.random(self.spread[1],self.spread[2]) + part.size = math.random(self.size[1],self.size[2]) * precision + part.speed = math.random(self.speed[1],self.speed[2]) * precision + part.acceleration = math.random(self.acceleration[1],self.acceleration[2]) * precision + part.lifespan = math.random(self.lifespan[1],self.lifespan[2]) * precision + part.thickness = math.random(self.thickness[1],self.thickness[2]) + part.decay = self.decay + return part end function ParticleCircle:update() local w = playdate.graphics.getLineWidth() local c = playdate.graphics.getColor() playdate.graphics.setColor(self.colour) - for part = 1, #self.particles, 1 do - local circ = self.particles[part] + for i = #self.active, 1, -1 do + local index = self.active[i] + local circ = self.partlist[index] if circ.thickness < 1 then playdate.graphics.fillCircleAtPoint(circ.x,circ.y,circ.size) else @@ -276,25 +288,25 @@ function ParticleCircle:update() circ.x += math.sin(math.rad(circ.dir)) * circ.speed circ.y -= math.cos(math.rad(circ.dir)) * circ.speed - + circ.speed += circ.acceleration / 100 - self.particles[part] = circ + self.partlist[index] = circ end playdate.graphics.setLineWidth(w) playdate.graphics.setColor(c) if self.mode == 1 then decay(self.particles, self.decay) - + elseif self.mode == 0 then - disappear(self.particles) - + disappear(self.partlist, self.active, self.available) + elseif self.mode == 2 then loop(self.particles, self.bounds) - + else stay(self.particles, self.bounds) - + end end @@ -325,29 +337,21 @@ function ParticlePoly:setRotation(min,max) self.rotation = {min, max or min} end -function ParticlePoly:create(amount) - for i = 1, amount, 1 do - local part = { - x = self.x, - y = self.y, - dir = math.random(self.spread[1],self.spread[2]), - size = math.random(self.size[1],self.size[2]) * precision, - speed = math.random(self.speed[1],self.speed[2]) * precision, - acceleration = math.random(self.acceleration[1],self.acceleration[2]) * precision, - lifespan = math.random(self.lifespan[1],self.lifespan[2]) * precision, - thickness = math.random(self.thickness[1],self.thickness[2]) * precision, - angular = math.random(self.angular[1],self.angular[2]) * precision, - points = math.random(self.points[1], self.points[2]), - decay = self.decay, - rotation = math.random(self.rotation[1],self.rotation[2]) - } - - self.particles[#self.particles+1] = part - end -end - -function ParticlePoly:add(amount) - self:create(amount) +function ParticlePoly:reset(part) + part = part or {} + part.x = self.x + part.y = self.y + part.dir = math.random(self.spread[1],self.spread[2]) + part.size = math.random(self.size[1],self.size[2]) * precision + part.speed = math.random(self.speed[1],self.speed[2]) * precision + part.acceleration = math.random(self.acceleration[1],self.acceleration[2]) * precision + part.lifespan = math.random(self.lifespan[1],self.lifespan[2]) * precision + part.thickness = math.random(self.thickness[1],self.thickness[2]) * precision + part.angular = math.random(self.angular[1],self.angular[2]) * precision + part.points = math.random(self.points[1], self.points[2]) + part.decay = self.decay + part.rotation = math.random(self.rotation[1],self.rotation[2]) + return part end function ParticlePoly:update() @@ -373,7 +377,7 @@ function ParticlePoly:update() poly.y = poly.y - math.cos(math.rad(poly.dir)) * poly.speed poly.rotation += poly.angular - + poly.speed += poly.acceleration / 100 self.particles[part] = poly @@ -383,16 +387,16 @@ function ParticlePoly:update() if self.mode == 1 then decay(self.particles, self.decay) - + elseif self.mode == 0 then disappear(self.particles) - + elseif self.mode == 2 then loop(self.particles, self.bounds) - + else stay(self.particles, self.bounds) - + end end @@ -432,50 +436,22 @@ function ParticleImage:getImageTable() return self.table end -function ParticleImage:create(amount) - if self.image ~= nil then - for i = 1, amount, 1 do - local part = { - x = self.x, - y = self.y, - dir = math.random(self.spread[1],self.spread[2]), - size = math.random(self.size[1],self.size[2]) * precision, - speed = math.random(self.speed[1],self.speed[2]) * precision, - acceleration = math.random(self.acceleration[1],self.acceleration[2]) * precision, - lifespan = math.random(self.lifespan[1],self.lifespan[2]) * precision, - thickness = math.random(self.thickness[1],self.thickness[2]), - angular = math.random(self.angular[1],self.angular[2]) * precision, - decay = self.decay, - image = self.image, - rotation = math.random(self.rotation[1],self.rotation[2]) - } - - self.particles[#self.particles+1] = part - end - else - for i = 1, amount, 1 do - local part = { - x = self.x, - y = self.y, - dir = math.random(self.spread[1],self.spread[2]), - size = math.random(self.size[1],self.size[2]) * precision, - speed = math.random(self.speed[1],self.speed[2]) * precision, - acceleration = math.random(self.acceleration[1],self.acceleration[2]) * precision, - lifespan = math.random(self.lifespan[1],self.lifespan[2]) * precision, - thickness = math.random(self.thickness[1],self.thickness[2]), - angular = math.random(self.angular[1],self.angular[2]) * precision, - decay = self.decay, - image = self.table[math.random(#self.table)], - rotation = math.random(self.rotation[1],self.rotation[2]) - } - - self.particles[#self.particles+1] = part - end - end -end - -function ParticleImage:add(amount) - self:create(amount) +function ParticleImage:resetPart(part) + part = part or {} + part.x = self.x + part.y = self.y + part.dir = math.random(self.spread[1],self.spread[2]) + part.size = math.random(self.size[1],self.size[2]) * precision + part.speed = math.random(self.speed[1],self.speed[2]) * precision + part.acceleration = math.random(self.acceleration[1],self.acceleration[2]) * precision + part.acceleration = math.random(self.acceleration[1],self.acceleration[2]) * precision + part.lifespan = math.random(self.lifespan[1],self.lifespan[2]) * precision + part.thickness = math.random(self.thickness[1],self.thickness[2]) + part.angular = math.random(self.angular[1],self.angular[2]) * precision + part.decay = self.decay + part.image = self.image + part.rotation = math.random(self.rotation[1],self.rotation[2]) + return part end function ParticleImage:update() @@ -488,7 +464,7 @@ function ParticleImage:update() img.x += math.sin(math.rad(img.dir)) * img.speed img.y = img.y - math.cos(math.rad(img.dir)) * img.speed - + img.speed += img.acceleration / 100 self.particles[part] = img @@ -496,16 +472,16 @@ function ParticleImage:update() if self.mode == 1 then decay(self.particles, self.decay) - + elseif self.mode == 0 then disappear(self.particles) - + elseif self.mode == 2 then loop(self.particles, self.bounds) - + else stay(self.particles, self.bounds) - + end end @@ -529,38 +505,31 @@ function ParticleImageBasic:update() if self.mode == 1 then decay(self.particles, self.decay) - + elseif self.mode == 0 then disappear(self.particles) - + elseif self.mode == 2 then loop(self.particles, self.bounds) - + else stay(self.particles, self.bounds) - + end end class("ParticlePixel").extends(Particle) -function ParticlePixel:create(amount) - for i = 1, amount, 1 do - local part = { - x = self.x, - y = self.y, - dir = math.random(self.spread[1],self.spread[2]), - speed = math.random(self.speed[1],self.speed[2]) * precision, - acceleration = math.random(self.acceleration[1],self.acceleration[2]) * precision, - lifespan = math.random(self.lifespan[1],self.lifespan[2]) * precision, - } - - self.particles[#self.particles+1] = part - end -end - -function ParticlePixel:add(amount) - self:create(amount) +function ParticlePixel:resetPart(part) + part = part or {} + part.x = self.x + part.y = self.y + part.dir = math.random(self.spread[1],self.spread[2]) + part.size = math.random(self.size[1],self.size[2]) * precision + part.speed = math.random(self.speed[1],self.speed[2]) * precision + part.acceleration = math.random(self.acceleration[1],self.acceleration[2]) * precision + part.lifespan = math.random(self.lifespan[1],self.lifespan[2]) * precision + return part end function ParticlePixel:update() @@ -568,12 +537,12 @@ function ParticlePixel:update() playdate.graphics.setColor(self.colour) for part = 1, #self.particles, 1 do local pix = self.particles[part] - + playdate.graphics.drawPixel(pix.x,pix.y,pix.size) pix.x += math.sin(math.rad(pix.dir)) * pix.speed pix.y -= math.cos(math.rad(pix.dir)) * pix.speed - + pix.speed += pix.acceleration / 100 self.particles[part] = pix @@ -582,13 +551,13 @@ function ParticlePixel:update() if self.mode == 0 then disappear(self.particles) - + elseif self.mode == 2 then loop(self.particles, self.bounds) - + else stay(self.particles, self.bounds) - + end end @@ -624,3 +593,11 @@ end function Particles:getPrecision() return precision end + +function Particles:setParticlePoolSize(size) + particle_pool_size = size +end + +function Particles:getParticlePoolSize() + return particle_pool_size +end From 981a982884d3c762a78efb20bbf880993e5be972 Mon Sep 17 00:00:00 2001 From: Jesse <7832163+GammaGames@users.noreply.github.com> Date: Wed, 3 Jul 2024 07:04:25 +0000 Subject: [PATCH 2/9] update default pool size --- pdParticles.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdParticles.lua b/pdParticles.lua index 277151d..1d2fbb3 100644 --- a/pdParticles.lua +++ b/pdParticles.lua @@ -1,6 +1,6 @@ local particles = {} local precision = 1 -local particle_pool_size = 1 +local particle_pool_size = 25 -- base particle class class("Particle").extends() From 482c2ce43e219bcd77aad6cb672f45ef5b6cdc7a Mon Sep 17 00:00:00 2001 From: GammaGames Date: Thu, 4 Jul 2024 00:57:56 -0600 Subject: [PATCH 3/9] use symlinks --- Examples/Circles/pdParticles.lua | 269 +--------- Examples/Particle Avoider/pdParticles.lua | 579 +--------------------- Examples/Particle Player/pdParticles.lua | 378 +------------- Examples/Shapes/pdParticles.lua | 378 +------------- 4 files changed, 4 insertions(+), 1600 deletions(-) mode change 100644 => 120000 Examples/Circles/pdParticles.lua mode change 100644 => 120000 Examples/Particle Avoider/pdParticles.lua mode change 100644 => 120000 Examples/Particle Player/pdParticles.lua mode change 100644 => 120000 Examples/Shapes/pdParticles.lua diff --git a/Examples/Circles/pdParticles.lua b/Examples/Circles/pdParticles.lua deleted file mode 100644 index 223565b..0000000 --- a/Examples/Circles/pdParticles.lua +++ /dev/null @@ -1,268 +0,0 @@ -local circles = {} - --- base particle class -class("Particle").extends() -function Particle:init(x, y) - self.x = x or 0 - self.y = y or 0 - self.size = {10,10} - self.spread = {0,359} - self.speed = {1,1} - self.thickness = {0,0} - self.lifespan = {1,1} - self.decay = 1 - self.particles = {} - self.colour = playdate.graphics.kColorBlack - self.bounds = {0,0,0,0} - self.mode = 0 - - if self.type == 1 then - circles[#circles+1] = self - end -end - --- [[ SETTERS AND GETTERS ]] -- - --- movement -function Particle:moveTo(x,y) - self.x = x - self.y = y -end - -function Particle:moveBy(x,y) - self.x += x - self.y += y -end - -function Particle:getPos() - return self.x, self.y -end - --- size -function Particle:setSize(min, max) - self.size = {min, max or min} -end - -function Particle:getSize() - return self.size[1], self.size[2] -end - --- mode -function Particle:setMode(mode) - self.mode = mode -end - -function Particle:getMode() - return self.mode -end - --- spread -function Particle:setSpread(min,max) - self.spread = {min, max or min} -end - -function Particle:getSpread() - return self.spread[1], self.spread[2] -end - --- speed -function Particle:setSpeed(min,max) - self.speed = {min, max or min} -end - -function Particle:getSpeed() - return self.speed[1], self.speed[2] -end - --- thickness -function Particle:setThickness(min, max) - self.thickness = {min, max or min} -end - -function Particle:getThickness() - return self.thickness[1], self.thickness[2] -end - --- lifespan -function Particle:setLifespan(min,max) - self.lifespan = {min, max or min} -end - -function Particle:getLifespan() - return self.lifespan[1], self.lifespan[2] -end - --- colour -function Particle:setColor(colour) - self.colour = colour -end - -function Particle:getColor() - return self.colour -end - -function Particle:setColour(colour) - self.colour = colour -end - -function Particle:getColour() - return self.colour -end - --- bounds -function Particle:setBounds(x1, y1, x2, y2) - self.bounds = {x1,y1,x2,y2} -end - -function Particle:getBounds() - return self.bounds[1],self.bounds[2],self.bounds[3],self.bounds[4] -end - --- decay -function Particle:setDecay(decay) - self.decay = decay -end - -function Particle:getDecay() - return self.decay -end - --- particles -function Particle:getParticles() - return self.particles -end - -function Particle:clearParticles() - self.particles = {} -end - -function Particle:update() -end - --- [[ PARTICLE MODES ]] -- - --- circles -local function decayCirc(circs, decay) - for part = 1, #circs, 1 do - local circ = circs[part] - circ.size -= decay - - if circ.size <= 0 then - circ.size = 0 - end - - circs[part] = circ - end - - for part = 1, #circs, 1 do - local circ = circs[part] - if circ.size <= 0 then - table.remove(circs,part) - break - end - end - - return circs -end - -local function disappearCirc(circs) - for part = 1, #circs, 1 do - local circ = circs[part] - circ.lifespan -= .1 - end - for part = 1, #circs, 1 do - local circ = circs[part] - if circ.lifespan <= 0 then - table.remove(circs,part) - break - end - end - - return circs -end - -local function loopCirc(circs, bounds) - if bounds[3] > bounds[1] and bounds[4] > bounds[2] then - local xDif , yDif = bounds[3] - bounds[1], bounds[4] - bounds[2] - for part = 1, #circs, 1 do - local circ = circs[part] - if circ.x > bounds[3] then circ.x -= xDif - elseif circ.x < bounds[1] then circ.x += xDif end - if circ.y > bounds[4] then circ.y -= yDif - elseif circ.y < bounds[2] then circ.y += yDif end - end - end - - return circs -end - -class("ParticleCircle", {type = 1}).extends(Particle) - -function ParticleCircle:create(amount) - for i = 1, amount, 1 do - local part = { - x = self.x, - y = self.y, - dir = math.random(self.spread[1],self.spread[2]), - size = math.random(self.size[1],self.size[2]), - speed = math.random(self.speed[1],self.speed[2]), - lifespan = math.random(self.lifespan[1],self.lifespan[2]), - thickness = math.random(self.thickness[1],self.thickness[2]), - decay = self.decay - } - - self.particles[#self.particles+1] = part - end -end - -function ParticleCircle:add(amount) - self:create(amount) -end - -function ParticleCircle:update() - local w = playdate.graphics.getLineWidth() - local c = playdate.graphics.getColor() - playdate.graphics.setColor(self.colour) - for part = 1, #self.particles, 1 do - local circ = self.particles[part] - if circ.thickness < 1 then - playdate.graphics.fillCircleAtPoint(circ.x,circ.y,circ.size) - else - playdate.graphics.setLineWidth(circ.thickness) - playdate.graphics.drawCircleAtPoint(circ.x, circ.y, circ.size) - end - - circ.x += math.sin(math.rad(circ.dir)) * circ.speed - circ.y -= math.cos(math.rad(circ.dir)) * circ.speed - - self.particles[part] = circ - end - playdate.graphics.setLineWidth(w) - playdate.graphics.setColor(c) - if self.mode == 1 then - local newCircs = decayCirc(self.particles, self.decay) - self.circs = newCircs - elseif self.mode == 0 then - local newCircs = disappearCirc(self.particles) - self.circs = newCircs - elseif self.mode == 2 then - local newCircs = loopCirc(self.particles, self.bounds) - self.circs = newCircs - end -end - -class("Particles").extends() - -Particles.modes = {DISAPPEAR = 0, DECAY = 1, LOOP = 2, STAY = 3} - -function Particles:update() - for circle = 1, #circles, 1 do - circles[circle]:update() - end -end - -function Particles:clearAll() - for circle = 1, #circles, 1 do - circles[circle]:clearParticles() - end -end \ No newline at end of file diff --git a/Examples/Circles/pdParticles.lua b/Examples/Circles/pdParticles.lua new file mode 120000 index 0000000..fefaa93 --- /dev/null +++ b/Examples/Circles/pdParticles.lua @@ -0,0 +1 @@ +../../pdParticles.lua \ No newline at end of file diff --git a/Examples/Particle Avoider/pdParticles.lua b/Examples/Particle Avoider/pdParticles.lua deleted file mode 100644 index ee002d7..0000000 --- a/Examples/Particle Avoider/pdParticles.lua +++ /dev/null @@ -1,578 +0,0 @@ -local particles = {} - --- base particle class -class("Particle").extends() -function Particle:init(x, y) - self.x = x or 0 - self.y = y or 0 - self.size = {1,1} - self.spread = {0,359} - self.speed = {1,1} - self.thickness = {0,0} - self.lifespan = {1,1} - self.decay = 1 - self.particles = {} - self.colour = playdate.graphics.kColorBlack - self.bounds = {0,0,0,0} - self.mode = 0 - if self.type == 2 then -- polys - self.points={3,3} - self.angular={0,0} - self.rotation={0,359} - elseif self.type == 3 then -- images - self.angular={0,0} - self.image = playdate.graphics.image.new(1,1) - self.table = nil - self.rotation = {0,359} - end - - particles[#particles+1] = self - -end - --- [[ SETTERS AND GETTERS ]] -- - --- movement -function Particle:moveTo(x,y) - self.x = x - self.y = y -end - -function Particle:moveBy(x,y) - self.x += x - self.y += y -end - -function Particle:getPos() - return self.x, self.y -end - --- size -function Particle:setSize(min, max) - self.size = {min, max or min} -end - -function Particle:getSize() - return self.size[1], self.size[2] -end - --- mode -function Particle:setMode(mode) - self.mode = mode -end - -function Particle:getMode() - return self.mode -end - --- spread -function Particle:setSpread(min,max) - self.spread = {min, max or min} -end - -function Particle:getSpread() - return self.spread[1], self.spread[2] -end - --- speed -function Particle:setSpeed(min,max) - self.speed = {min, max or min} -end - -function Particle:getSpeed() - return self.speed[1], self.speed[2] -end - --- thickness -function Particle:setThickness(min, max) - self.thickness = {min, max or min} -end - -function Particle:getThickness() - return self.thickness[1], self.thickness[2] -end - --- lifespan -function Particle:setLifespan(min,max) - self.lifespan = {min, max or min} -end - -function Particle:getLifespan() - return self.lifespan[1], self.lifespan[2] -end - --- colour -function Particle:setColor(colour) - self.colour = colour -end - -function Particle:getColor() - return self.colour -end - -function Particle:setColour(colour) - self.colour = colour -end - -function Particle:getColour() - return self.colour -end - --- bounds -function Particle:setBounds(x1, y1, x2, y2) - self.bounds = {x1,y1,x2,y2} -end - -function Particle:getBounds() - return self.bounds[1],self.bounds[2],self.bounds[3],self.bounds[4] -end - --- decay -function Particle:setDecay(decay) - self.decay = decay -end - -function Particle:getDecay() - return self.decay -end - --- particles -function Particle:getParticles() - return self.particles -end - -function Particle:clearParticles() - self.particles = {} -end - -function Particle:update() -end - --- [[ PARTICLE MODES ]] -- - -local function decay(partlist, decay) - for part = 1, #partlist, 1 do - local particle = partlist[part] - particle.size -= decay - - if particle.size <= 0 then - particle.size = 0 - end - - partlist[part] = particle - end - - for part = 1, #partlist, 1 do - local particle = partlist[part] - if particle.size <= 0 then - table.remove(partlist,part) - break - end - end - - return partlist -end - -local function disappear(partlist) - for part = 1, #partlist, 1 do - local particle = partlist[part] - particle.lifespan -= .1 - end - for part = 1, #partlist, 1 do - local particle = partlist[part] - if particle.lifespan <= 0 then - table.remove(partlist,part) - break - end - end - - return partlist -end - -local function loop(partlist, bounds) - if bounds[3] > bounds[1] and bounds[4] > bounds[2] then - local xDif , yDif = bounds[3] - bounds[1], bounds[4] - bounds[2] - for part = 1, #partlist, 1 do - local particle = partlist[part] - if particle.x > bounds[3] then particle.x -= xDif - elseif particle.x < bounds[1] then particle.x += xDif end - if particle.y > bounds[4] then particle.y -= yDif - elseif particle.y < bounds[2] then particle.y += yDif end - end - end - - return partlist -end - -local function stay(partlist, bounds) - if bounds[3] > bounds[1] and bounds[4] > bounds[2] then - local xDif , yDif = bounds[3] - bounds[1], bounds[4] - bounds[2] - for part = 1, #partlist, 1 do - local particle = partlist[part] - if particle.x > bounds[3] then table.remove(partlist,part) break - elseif particle.x < bounds[1] then table.remove(partlist,part) break - elseif particle.y > bounds[4] then table.remove(partlist,part) break - elseif particle.y < bounds[2] then table.remove(partlist,part) break end - end - end - - return partlist -end - -class("ParticleCircle", {type = 1}).extends(Particle) - -function ParticleCircle:create(amount) - for i = 1, amount, 1 do - local part = { - x = self.x, - y = self.y, - dir = math.random(self.spread[1],self.spread[2]), - size = math.random(self.size[1],self.size[2]), - speed = math.random(self.speed[1],self.speed[2]), - lifespan = math.random(self.lifespan[1],self.lifespan[2]), - thickness = math.random(self.thickness[1],self.thickness[2]), - decay = self.decay - } - - self.particles[#self.particles+1] = part - end -end - -function ParticleCircle:add(amount) - self:create(amount) -end - -function ParticleCircle:update() - local w = playdate.graphics.getLineWidth() - local c = playdate.graphics.getColor() - playdate.graphics.setColor(self.colour) - for part = 1, #self.particles, 1 do - local circ = self.particles[part] - if circ.thickness < 1 then - playdate.graphics.fillCircleAtPoint(circ.x,circ.y,circ.size) - else - playdate.graphics.setLineWidth(circ.thickness) - playdate.graphics.drawCircleAtPoint(circ.x, circ.y, circ.size) - end - - circ.x += math.sin(math.rad(circ.dir)) * circ.speed - circ.y -= math.cos(math.rad(circ.dir)) * circ.speed - - self.particles[part] = circ - end - playdate.graphics.setLineWidth(w) - playdate.graphics.setColor(c) - if self.mode == 1 then - decay(self.particles, self.decay) - - elseif self.mode == 0 then - disappear(self.particles) - - elseif self.mode == 2 then - loop(self.particles, self.bounds) - - else - stay(self.particles, self.bounds) - - end -end - -class("ParticlePoly", {type = 2}).extends(Particle) - -function ParticlePoly:getPoints() - return self.points[1], self.points[2] -end - -function ParticlePoly:setPoints(min,max) - self.points = {min, max or min} -end - --- angular -function ParticlePoly:getAngular() - return self.angular[1], self.angular[2] -end - -function ParticlePoly:setAngular(min,max) - self.angular = {min, max or min} -end - -function ParticlePoly:getRotation() - return self.rotation[1], self.rotation[2] -end - -function ParticlePoly:setRotation(min,max) - self.rotation = {min, max or min} -end - -function ParticlePoly:create(amount) - for i = 1, amount, 1 do - local part = { - x = self.x, - y = self.y, - dir = math.random(self.spread[1],self.spread[2]), - size = math.random(self.size[1],self.size[2]), - speed = math.random(self.speed[1],self.speed[2]), - lifespan = math.random(self.lifespan[1],self.lifespan[2]), - thickness = math.random(self.thickness[1],self.thickness[2]), - angular = math.random(self.angular[1],self.angular[2]), - points = math.random(self.points[1], self.points[2]), - decay = self.decay, - rotation = math.random(self.rotation[1],self.rotation[2]) - } - - self.particles[#self.particles+1] = part - end -end - -function ParticlePoly:add(amount) - self:create(amount) -end - -function ParticlePoly:update() - local w = playdate.graphics.getLineWidth() - local c = playdate.graphics.getColor() - playdate.graphics.setColor(self.colour) - for part = 1, #self.particles, 1 do - local poly = self.particles[part] - local polygon = {} - local degrees = 360 / poly.points - for point = 1, poly.points, 1 do - polygon[#polygon+1] = poly.x + math.sin(math.rad(degrees * point + poly.rotation)) * poly.size - polygon[#polygon+1] = poly.y - math.cos(math.rad(degrees * point + poly.rotation)) * poly.size - end - if poly.thickness < 1 then - playdate.graphics.fillPolygon(table.unpack(polygon)) - else - playdate.graphics.setLineWidth(poly.thickness) - playdate.graphics.drawPolygon(table.unpack(polygon)) - end - - poly.x += math.sin(math.rad(poly.dir)) * poly.speed - poly.y = poly.y - math.cos(math.rad(poly.dir)) * poly.speed - - poly.rotation += poly.angular - - self.particles[part] = poly - end - playdate.graphics.setLineWidth(w) - playdate.graphics.setColor(c) - - if self.mode == 1 then - decay(self.particles, self.decay) - - elseif self.mode == 0 then - disappear(self.particles) - - elseif self.mode == 2 then - loop(self.particles, self.bounds) - - else - stay(self.particles, self.bounds) - - end -end - -class("ParticleImage", {type = 3}).extends(Particle) - -function ParticleImage:getAngular() - return self.angular[1], self.angular[2] -end - -function ParticleImage:setAngular(min,max) - self.angular = {min, max or min} -end - -function ParticleImage:getRotation() - return self.rotation[1], self.rotation[2] -end - -function ParticleImage:setRotation(min,max) - self.rotation = {min, max or min} -end - -function ParticleImage:setImage(image) - self.image = image - self.table = nil -end - -function ParticleImage:setImageTable(image) - self.image = nil - self.table = image -end - -function ParticleImage:getImage() - return self.image -end - -function ParticleImage:getImageTable() - return self.table -end - -function ParticleImage:create(amount) - if self.image ~= nil then - for i = 1, amount, 1 do - local part = { - x = self.x, - y = self.y, - dir = math.random(self.spread[1],self.spread[2]), - size = math.random(self.size[1],self.size[2]), - speed = math.random(self.speed[1],self.speed[2]), - lifespan = math.random(self.lifespan[1],self.lifespan[2]), - thickness = math.random(self.thickness[1],self.thickness[2]), - angular = math.random(self.angular[1],self.angular[2]), - decay = self.decay, - image = self.image, - rotation = math.random(self.rotation[1],self.rotation[2]) - } - - self.particles[#self.particles+1] = part - end - else - for i = 1, amount, 1 do - local part = { - x = self.x, - y = self.y, - dir = math.random(self.spread[1],self.spread[2]), - size = math.random(self.size[1],self.size[2]), - speed = math.random(self.speed[1],self.speed[2]), - lifespan = math.random(self.lifespan[1],self.lifespan[2]), - thickness = math.random(self.thickness[1],self.thickness[2]), - angular = math.random(self.angular[1],self.angular[2]), - decay = self.decay, - image = self.table[math.random(#self.table)], - rotation = math.random(self.rotation[1],self.rotation[2]) - } - - self.particles[#self.particles+1] = part - end - end -end - -function ParticleImage:add(amount) - self:create(amount) -end - -function ParticleImage:update() - for part = 1, #self.particles, 1 do - local img = self.particles[part] - - img.image:drawRotated(img.x,img.y,img.rotation,img.size) - - img.rotation += img.angular - - img.x += math.sin(math.rad(img.dir)) * img.speed - img.y = img.y - math.cos(math.rad(img.dir)) * img.speed - - self.particles[part] = img - end - - if self.mode == 1 then - decay(self.particles, self.decay) - - elseif self.mode == 0 then - disappear(self.particles) - - elseif self.mode == 2 then - loop(self.particles, self.bounds) - - else - stay(self.particles, self.bounds) - - end -end - -class("ParticleImageBasic", {type = 3}).extends(ParticleImage) - -function ParticleImageBasic:update() - for part = 1, #self.particles, 1 do - local img = self.particles[part] - - img.image:drawScaled(img.x,img.y,img.size) - - img.rotation += img.angular - - img.x += math.sin(math.rad(img.dir)) * img.speed - img.y = img.y - math.cos(math.rad(img.dir)) * img.speed - - self.particles[part] = img - end - - if self.mode == 1 then - decay(self.particles, self.decay) - - elseif self.mode == 0 then - disappear(self.particles) - - elseif self.mode == 2 then - loop(self.particles, self.bounds) - - else - stay(self.particles, self.bounds) - - end -end - -class("ParticlePixel").extends(Particle) - -function ParticlePixel:create(amount) - for i = 1, amount, 1 do - local part = { - x = self.x, - y = self.y, - dir = math.random(self.spread[1],self.spread[2]), - speed = math.random(self.speed[1],self.speed[2]), - lifespan = math.random(self.lifespan[1],self.lifespan[2]), - } - - self.particles[#self.particles+1] = part - end -end - -function ParticlePixel:add(amount) - self:create(amount) -end - -function ParticlePixel:update() - local c = playdate.graphics.getColor() - playdate.graphics.setColor(self.colour) - for part = 1, #self.particles, 1 do - local pix = self.particles[part] - - playdate.graphics.drawPixel(pix.x,pix.y,pix.size) - - pix.x += math.sin(math.rad(pix.dir)) * pix.speed - pix.y -= math.cos(math.rad(pix.dir)) * pix.speed - - self.particles[part] = pix - end - playdate.graphics.setColor(c) - - if self.mode == 0 then - disappear(self.particles) - - elseif self.mode == 2 then - loop(self.particles, self.bounds) - - else - stay(self.particles, self.bounds) - - end -end - --- [[ GLOBAL PARTICLE STUFF ]] -- - -class("Particles").extends() - -Particles.modes = {DISAPPEAR = 0, DECAY = 1, LOOP = 2, STAY = 3} - -function Particles:update() - for particle = 1, #particles, 1 do - particles[particle]:update() - end -end - -function Particles:clearAll() - for part = 1, #particles, 1 do - particles[part]:clearParticles() - end -end \ No newline at end of file diff --git a/Examples/Particle Avoider/pdParticles.lua b/Examples/Particle Avoider/pdParticles.lua new file mode 120000 index 0000000..fefaa93 --- /dev/null +++ b/Examples/Particle Avoider/pdParticles.lua @@ -0,0 +1 @@ +../../pdParticles.lua \ No newline at end of file diff --git a/Examples/Particle Player/pdParticles.lua b/Examples/Particle Player/pdParticles.lua deleted file mode 100644 index f2c8567..0000000 --- a/Examples/Particle Player/pdParticles.lua +++ /dev/null @@ -1,377 +0,0 @@ -local particles = {} - --- base particle class -class("Particle").extends() -function Particle:init(x, y) - self.x = x or 0 - self.y = y or 0 - self.size = {10,10} - self.spread = {0,359} - self.speed = {1,1} - self.thickness = {0,0} - self.lifespan = {1,1} - self.decay = 1 - self.particles = {} - self.colour = playdate.graphics.kColorBlack - self.bounds = {0,0,0,0} - self.mode = 0 - self.angular={0,0} - self.points={3,3} - - particles[#particles+1] = self - -end - --- [[ SETTERS AND GETTERS ]] -- - --- movement -function Particle:moveTo(x,y) - self.x = x - self.y = y -end - -function Particle:moveBy(x,y) - self.x += x - self.y += y -end - -function Particle:getPos() - return self.x, self.y -end - --- size -function Particle:setSize(min, max) - self.size = {min, max or min} -end - -function Particle:getSize() - return self.size[1], self.size[2] -end - --- mode -function Particle:setMode(mode) - self.mode = mode -end - -function Particle:getMode() - return self.mode -end - --- spread -function Particle:setSpread(min,max) - self.spread = {min, max or min} -end - -function Particle:getSpread() - return self.spread[1], self.spread[2] -end - --- speed -function Particle:setSpeed(min,max) - self.speed = {min, max or min} -end - -function Particle:getSpeed() - return self.speed[1], self.speed[2] -end - --- thickness -function Particle:setThickness(min, max) - self.thickness = {min, max or min} -end - -function Particle:getThickness() - return self.thickness[1], self.thickness[2] -end - --- lifespan -function Particle:setLifespan(min,max) - self.lifespan = {min, max or min} -end - -function Particle:getLifespan() - return self.lifespan[1], self.lifespan[2] -end - --- colour -function Particle:setColor(colour) - self.colour = colour -end - -function Particle:getColor() - return self.colour -end - -function Particle:setColour(colour) - self.colour = colour -end - -function Particle:getColour() - return self.colour -end - --- bounds -function Particle:setBounds(x1, y1, x2, y2) - self.bounds = {x1,y1,x2,y2} -end - -function Particle:getBounds() - return self.bounds[1],self.bounds[2],self.bounds[3],self.bounds[4] -end - --- decay -function Particle:setDecay(decay) - self.decay = decay -end - -function Particle:getDecay() - return self.decay -end - --- particles -function Particle:getParticles() - return self.particles -end - -function Particle:clearParticles() - self.particles = {} -end - -function Particle:update() -end - --- [[ PARTICLE MODES ]] -- - -local function decay(partlist, decay) - for part = 1, #partlist, 1 do - local particle = partlist[part] - particle.size -= decay - - if particle.size <= 0 then - particle.size = 0 - end - - partlist[part] = particle - end - - for part = 1, #partlist, 1 do - local particle = partlist[part] - if particle.size <= 0 then - table.remove(partlist,part) - break - end - end - - return partlist -end - -local function disappear(partlist) - for part = 1, #partlist, 1 do - local particle = partlist[part] - particle.lifespan -= .1 - end - for part = 1, #partlist, 1 do - local particle = partlist[part] - if particle.lifespan <= 0 then - table.remove(partlist,part) - break - end - end - - return partlist -end - -local function loop(partlist, bounds) - if bounds[3] > bounds[1] and bounds[4] > bounds[2] then - local xDif , yDif = bounds[3] - bounds[1], bounds[4] - bounds[2] - for part = 1, #partlist, 1 do - local particle = partlist[part] - if particle.x > bounds[3] then particle.x -= xDif - elseif particle.x < bounds[1] then particle.x += xDif end - if particle.y > bounds[4] then particle.y -= yDif - elseif particle.y < bounds[2] then particle.y += yDif end - end - end - - return partlist -end - -local function stay(partlist, bounds) - if bounds[3] > bounds[1] and bounds[4] > bounds[2] then - local xDif , yDif = bounds[3] - bounds[1], bounds[4] - bounds[2] - for part = 1, #partlist, 1 do - local particle = partlist[part] - if particle.x > bounds[3] then table.remove(partlist,part) break - elseif particle.x < bounds[1] then table.remove(partlist,part) break - elseif particle.y > bounds[4] then table.remove(partlist,part) break - elseif particle.y < bounds[2] then table.remove(partlist,part) break end - end - end - - return partlist -end - -class("ParticleCircle", {type = 1}).extends(Particle) - -function ParticleCircle:create(amount) - for i = 1, amount, 1 do - local part = { - x = self.x, - y = self.y, - dir = math.random(self.spread[1],self.spread[2]), - size = math.random(self.size[1],self.size[2]), - speed = math.random(self.speed[1],self.speed[2]), - lifespan = math.random(self.lifespan[1],self.lifespan[2]), - thickness = math.random(self.thickness[1],self.thickness[2]), - decay = self.decay - } - - self.particles[#self.particles+1] = part - end -end - -function ParticleCircle:add(amount) - self:create(amount) -end - -function ParticleCircle:update() - local w = playdate.graphics.getLineWidth() - local c = playdate.graphics.getColor() - playdate.graphics.setColor(self.colour) - for part = 1, #self.particles, 1 do - local circ = self.particles[part] - if circ.thickness < 1 then - playdate.graphics.fillCircleAtPoint(circ.x,circ.y,circ.size) - else - playdate.graphics.setLineWidth(circ.thickness) - playdate.graphics.drawCircleAtPoint(circ.x, circ.y, circ.size) - end - - circ.x += math.sin(math.rad(circ.dir)) * circ.speed - circ.y -= math.cos(math.rad(circ.dir)) * circ.speed - - self.particles[part] = circ - end - playdate.graphics.setLineWidth(w) - playdate.graphics.setColor(c) - if self.mode == 1 then - local newCircs = decay(self.particles, self.decay) - - elseif self.mode == 0 then - local newCircs = disappear(self.particles) - - elseif self.mode == 2 then - local newCircs = loop(self.particles, self.bounds) - - else - local newCircs = stay(self.particles, self.bounds) - - end -end - -class("ParticlePoly", {type = 2}).extends(Particle) - -function ParticlePoly:getPoints() - return self.points[1], self.points[2] -end - -function ParticlePoly:setPoints(min,max) - self.points = {min, max or min} -end - --- angular -function ParticlePoly:getAngular() - return self.angular[1], self.angular[2] -end - -function ParticlePoly:setAngular(min,max) - self.angular = {min, max or min} -end - -function ParticlePoly:create(amount) - for i = 1, amount, 1 do - local part = { - x = self.x, - y = self.y, - dir = math.random(self.spread[1],self.spread[2]), - size = math.random(self.size[1],self.size[2]), - speed = math.random(self.speed[1],self.speed[2]), - lifespan = math.random(self.lifespan[1],self.lifespan[2]), - thickness = math.random(self.thickness[1],self.thickness[2]), - angular = math.random(self.angular[1],self.angular[2]), - points = math.random(self.points[1], self.points[2]), - decay = self.decay, - rotation = 0 - } - - self.particles[#self.particles+1] = part - end -end - -function ParticlePoly:add(amount) - self:create(amount) -end - -function ParticlePoly:update() - local w = playdate.graphics.getLineWidth() - local c = playdate.graphics.getColor() - playdate.graphics.setColor(self.colour) - for part = 1, #self.particles, 1 do - local poly = self.particles[part] - local polygon = {} - local degrees = 360 / poly.points - for point = 1, poly.points, 1 do - polygon[#polygon+1] = poly.x + math.sin(math.rad(degrees * point + poly.rotation)) * poly.size - polygon[#polygon+1] = poly.y - math.cos(math.rad(degrees * point + poly.rotation)) * poly.size - end - if poly.thickness < 1 then - playdate.graphics.fillPolygon(table.unpack(polygon)) - else - playdate.graphics.setLineWidth(poly.thickness) - playdate.graphics.drawPolygon(table.unpack(polygon)) - end - - poly.x += math.sin(math.rad(poly.dir)) * poly.speed - poly.y = poly.y - math.cos(math.rad(poly.dir)) * poly.speed - - poly.rotation += poly.angular - - self.particles[part] = poly - end - playdate.graphics.setLineWidth(w) - playdate.graphics.setColor(c) - - if self.mode == 1 then - local newCircs = decay(self.particles, self.decay) - - elseif self.mode == 0 then - local newCircs = disappear(self.particles) - - elseif self.mode == 2 then - local newCircs = loop(self.particles, self.bounds) - - else - local newCircs = stay(self.particles, self.bounds) - - end -end - - --- [[ GLOBAL PARTICLE STUFF ]] -- - - -class("Particles").extends() - -Particles.modes = {DISAPPEAR = 0, DECAY = 1, LOOP = 2, STAY = 3} - -function Particles:update() - for particle = 1, #particles, 1 do - particles[particle]:update() - end -end - -function Particles:clearAll() - for part = 1, #particles, 1 do - particles[part]:clearParticles() - end -end \ No newline at end of file diff --git a/Examples/Particle Player/pdParticles.lua b/Examples/Particle Player/pdParticles.lua new file mode 120000 index 0000000..fefaa93 --- /dev/null +++ b/Examples/Particle Player/pdParticles.lua @@ -0,0 +1 @@ +../../pdParticles.lua \ No newline at end of file diff --git a/Examples/Shapes/pdParticles.lua b/Examples/Shapes/pdParticles.lua deleted file mode 100644 index 09d7ccf..0000000 --- a/Examples/Shapes/pdParticles.lua +++ /dev/null @@ -1,377 +0,0 @@ -local particles = {} - --- base particle class -class("Particle").extends() -function Particle:init(x, y) - self.x = x or 0 - self.y = y or 0 - self.size = {10,10} - self.spread = {0,359} - self.speed = {1,1} - self.thickness = {0,0} - self.lifespan = {1,1} - self.decay = 1 - self.particles = {} - self.colour = playdate.graphics.kColorBlack - self.bounds = {0,0,0,0} - self.mode = 0 - self.angular={0,0} - self.points={3,3} - - particles[#particles+1] = self - -end - --- [[ SETTERS AND GETTERS ]] -- - --- movement -function Particle:moveTo(x,y) - self.x = x - self.y = y -end - -function Particle:moveBy(x,y) - self.x += x - self.y += y -end - -function Particle:getPos() - return self.x, self.y -end - --- size -function Particle:setSize(min, max) - self.size = {min, max or min} -end - -function Particle:getSize() - return self.size[1], self.size[2] -end - --- mode -function Particle:setMode(mode) - self.mode = mode -end - -function Particle:getMode() - return self.mode -end - --- spread -function Particle:setSpread(min,max) - self.spread = {min, max or min} -end - -function Particle:getSpread() - return self.spread[1], self.spread[2] -end - --- speed -function Particle:setSpeed(min,max) - self.speed = {min, max or min} -end - -function Particle:getSpeed() - return self.speed[1], self.speed[2] -end - --- thickness -function Particle:setThickness(min, max) - self.thickness = {min, max or min} -end - -function Particle:getThickness() - return self.thickness[1], self.thickness[2] -end - --- lifespan -function Particle:setLifespan(min,max) - self.lifespan = {min, max or min} -end - -function Particle:getLifespan() - return self.lifespan[1], self.lifespan[2] -end - --- colour -function Particle:setColor(colour) - self.colour = colour -end - -function Particle:getColor() - return self.colour -end - -function Particle:setColour(colour) - self.colour = colour -end - -function Particle:getColour() - return self.colour -end - --- bounds -function Particle:setBounds(x1, y1, x2, y2) - self.bounds = {x1,y1,x2,y2} -end - -function Particle:getBounds() - return self.bounds[1],self.bounds[2],self.bounds[3],self.bounds[4] -end - --- decay -function Particle:setDecay(decay) - self.decay = decay -end - -function Particle:getDecay() - return self.decay -end - --- particles -function Particle:getParticles() - return self.particles -end - -function Particle:clearParticles() - self.particles = {} -end - -function Particle:update() -end - --- [[ PARTICLE MODES ]] -- - -local function decay(partlist, decay) - for part = 1, #partlist, 1 do - local particle = partlist[part] - particle.size -= decay - - if particle.size <= 0 then - particle.size = 0 - end - - partlist[part] = particle - end - - for part = 1, #partlist, 1 do - local particle = partlist[part] - if particle.size <= 0 then - table.remove(partlist,part) - break - end - end - - return partlist -end - -local function disappear(partlist) - for part = 1, #partlist, 1 do - local particle = partlist[part] - particle.lifespan -= .1 - end - for part = 1, #partlist, 1 do - local particle = partlist[part] - if particle.lifespan <= 0 then - table.remove(partlist,part) - break - end - end - - return partlist -end - -local function loop(partlist, bounds) - if bounds[3] > bounds[1] and bounds[4] > bounds[2] then - local xDif , yDif = bounds[3] - bounds[1], bounds[4] - bounds[2] - for part = 1, #partlist, 1 do - local particle = partlist[part] - if particle.x > bounds[3] then particle.x -= xDif - elseif particle.x < bounds[1] then particle.x += xDif end - if particle.y > bounds[4] then particle.y -= yDif - elseif particle.y < bounds[2] then particle.y += yDif end - end - end - - return partlist -end - -local function stay(partlist, bounds) - if bounds[3] > bounds[1] and bounds[4] > bounds[2] then - local xDif , yDif = bounds[3] - bounds[1], bounds[4] - bounds[2] - for part = 1, #partlist, 1 do - local particle = partlist[part] - if particle.x > bounds[3] then table.remove(partlist,part) break - elseif particle.x < bounds[1] then table.remove(partlist,part) break - elseif particle.y > bounds[4] then table.remove(partlist,part) break - elseif particle.y < bounds[2] then table.remove(partlist,part) break end - end - end - - return partlist -end - -class("ParticleCircle", {type = 1}).extends(Particle) - -function ParticleCircle:create(amount) - for i = 1, amount, 1 do - local part = { - x = self.x, - y = self.y, - dir = math.random(self.spread[1],self.spread[2]), - size = math.random(self.size[1],self.size[2]), - speed = math.random(self.speed[1],self.speed[2]), - lifespan = math.random(self.lifespan[1],self.lifespan[2]), - thickness = math.random(self.thickness[1],self.thickness[2]), - decay = self.decay - } - - self.particles[#self.particles+1] = part - end -end - -function ParticleCircle:add(amount) - self:create(amount) -end - -function ParticleCircle:update() - local w = playdate.graphics.getLineWidth() - local c = playdate.graphics.getColor() - playdate.graphics.setColor(self.colour) - for part = 1, #self.particles, 1 do - local circ = self.particles[part] - if circ.thickness < 1 then - playdate.graphics.fillCircleAtPoint(circ.x,circ.y,circ.size) - else - playdate.graphics.setLineWidth(circ.thickness) - playdate.graphics.drawCircleAtPoint(circ.x, circ.y, circ.size) - end - - circ.x += math.sin(math.rad(circ.dir)) * circ.speed - circ.y -= math.cos(math.rad(circ.dir)) * circ.speed - - self.particles[part] = circ - end - playdate.graphics.setLineWidth(w) - playdate.graphics.setColor(c) - if self.mode == 1 then - local newCircs = decay(self.particles, self.decay) - - elseif self.mode == 0 then - local newCircs = disappear(self.particles) - - elseif self.mode == 2 then - local newCircs = loop(self.particles, self.bounds) - - else - local newCircs = stay(self.particles, self.bounds) - - end -end - -class("ParticlePoly", {type = 2}).extends(Particle) - -function ParticlePoly:getPoints() - return self.points[1], self.points[2] -end - -function ParticlePoly:setPoints(min,max) - self.points = {min, max or min} -end - --- angular -function ParticlePoly:getAngular() - return self.angular[1], self.angular[2] -end - -function ParticlePoly:setAngular(min,max) - self.angular = {min, max or min} -end - -function ParticlePoly:create(amount) - for i = 1, amount, 1 do - local part = { - x = self.x, - y = self.y, - dir = math.random(self.spread[1],self.spread[2]), - size = math.random(self.size[1],self.size[2]), - speed = math.random(self.speed[1],self.speed[2]), - lifespan = math.random(self.lifespan[1],self.lifespan[2]), - thickness = math.random(self.thickness[1],self.thickness[2]), - angular = math.random(self.angular[1],self.angular[2]), - points = math.random(self.points[1], self.points[2]), - decay = self.decay, - rotation = 0 - } - - self.particles[#self.particles+1] = part - end -end - -function ParticlePoly:add(amount) - self:create(amount) -end - -function ParticlePoly:update() - local w = playdate.graphics.getLineWidth() - local c = playdate.graphics.getColor() - playdate.graphics.setColor(self.colour) - for part = 1, #self.particles, 1 do - local poly = self.particles[part] - local polygon = {} - local degrees = 360 / poly.points - for point = 1, poly.points, 1 do - polygon[#polygon+1] = poly.x + math.sin(math.rad(degrees * point + poly.rotation)) * poly.size - polygon[#polygon+1] = poly.y - math.cos(math.rad(degrees * point + poly.rotation)) * poly.size - end - if poly.thickness < 1 then - playdate.graphics.fillPolygon(table.unpack(polygon)) - else - playdate.graphics.setLineWidth(poly.thickness) - playdate.graphics.drawPolygon(table.unpack(polygon)) - end - - poly.x += math.sin(math.rad(poly.dir)) * poly.speed - poly.y = poly.y - math.cos(math.rad(poly.dir)) * poly.speed - - poly.rotation += poly.angular - - self.particles[part] = poly - end - playdate.graphics.setLineWidth(w) - playdate.graphics.setColor(c) - - if self.mode == 1 then - local newCircs = decay(self.particles, self.decay) - - elseif self.mode == 0 then - local newCircs = disappear(self.particles) - - elseif self.mode == 2 then - local newCircs = loop(self.particles, self.bounds) - - else - local newCircs = stay(self.particles, self.bounds) - - end -end - - --- [[ GLOBAL PARTICLE STUFF ]] -- - - -class("Particles").extends() - -Particles.modes = {DISAPPEAR = 0, DECAY = 1, LOOP = 2, STAY = 3} - -function Particles:update() - for circle = 1, #circles, 1 do - circles[circle]:update() - end -end - -function Particles:clearAll() - for part = 1, #particles, 1 do - particles[part]:clearParticles() - end -end \ No newline at end of file diff --git a/Examples/Shapes/pdParticles.lua b/Examples/Shapes/pdParticles.lua new file mode 120000 index 0000000..fefaa93 --- /dev/null +++ b/Examples/Shapes/pdParticles.lua @@ -0,0 +1 @@ +../../pdParticles.lua \ No newline at end of file From e9953d95bebc2536ecfc7e0075dd94ac1cbe9eab Mon Sep 17 00:00:00 2001 From: GammaGames Date: Thu, 4 Jul 2024 01:08:24 -0600 Subject: [PATCH 4/9] address comments --- pdParticles.lua | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/pdParticles.lua b/pdParticles.lua index 1d2fbb3..a67791f 100644 --- a/pdParticles.lua +++ b/pdParticles.lua @@ -39,16 +39,16 @@ function Particle:init(x, y, pool_size) particles[#particles+1] = self end -function Particle:resetPart(part) +function Particle:default(part) part = part or {} -- Stub function, should be overridden by child classes return part or {} end function Particle:create(amount) - for index = 1, amount do - self.partlist[#self.partlist + 1] = self:resetPart() - self.available[index] = #self.partlist + for _ = 1, amount do + self.partlist[#self.partlist + 1] = self:default() + table.insert(self.available, #self.partlist) end end @@ -60,7 +60,7 @@ function Particle:add(amount) for _ = 1, amount do local index = table.remove(self.available) table.insert(self.active, index) - self.partlist[index] = self:resetPart(self.partlist[index]) + self.partlist[index] = self:default(self.partlist[index]) end end @@ -258,7 +258,7 @@ end class("ParticleCircle", {type = 1}).extends(Particle) -function ParticleCircle:resetPart(part) +function ParticleCircle:default(part) part = part or {} part.x = self.x part.y = self.y @@ -297,16 +297,12 @@ function ParticleCircle:update() playdate.graphics.setColor(c) if self.mode == 1 then decay(self.particles, self.decay) - elseif self.mode == 0 then disappear(self.partlist, self.active, self.available) - elseif self.mode == 2 then - loop(self.particles, self.bounds) - + loop(self.partlist, self.active, nil, self.bounds) else - stay(self.particles, self.bounds) - + stay(self.partlist, self.active, self.available, self.bounds) end end @@ -337,7 +333,7 @@ function ParticlePoly:setRotation(min,max) self.rotation = {min, max or min} end -function ParticlePoly:reset(part) +function ParticlePoly:default(part) part = part or {} part.x = self.x part.y = self.y @@ -436,7 +432,7 @@ function ParticleImage:getImageTable() return self.table end -function ParticleImage:resetPart(part) +function ParticleImage:default(part) part = part or {} part.x = self.x part.y = self.y @@ -520,7 +516,7 @@ end class("ParticlePixel").extends(Particle) -function ParticlePixel:resetPart(part) +function ParticlePixel:default(part) part = part or {} part.x = self.x part.y = self.y From 60049484d2bd301446a8ca9574728ddcfefba15e Mon Sep 17 00:00:00 2001 From: GammaGames Date: Thu, 4 Jul 2024 01:08:41 -0600 Subject: [PATCH 5/9] case sensitive for linux filesystems --- Examples/Circles/main.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Examples/Circles/main.lua b/Examples/Circles/main.lua index 467f00c..7cba1b6 100644 --- a/Examples/Circles/main.lua +++ b/Examples/Circles/main.lua @@ -1,7 +1,7 @@ local gfx = playdate.graphics -import "coreLibs/graphics" -import "coreLibs/object" +import "CoreLibs/graphics" +import "CoreLibs/object" import "pdParticles" local particleA = ParticleCircle(200, 120) @@ -44,4 +44,4 @@ function playdate.update() if playdate.buttonJustPressed(playdate.kButtonUp) then Particles:clearAll() end -end \ No newline at end of file +end From 8b8cb238ef8cb704b6390889c5eb281066f55b18 Mon Sep 17 00:00:00 2001 From: GammaGames Date: Thu, 4 Jul 2024 01:26:02 -0600 Subject: [PATCH 6/9] case sensitive --- Examples/Particle Avoider/main.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Examples/Particle Avoider/main.lua b/Examples/Particle Avoider/main.lua index bd66a58..e185c31 100644 --- a/Examples/Particle Avoider/main.lua +++ b/Examples/Particle Avoider/main.lua @@ -5,7 +5,7 @@ local showFPS = false -- SET TO 'true' TO SEE FPS import "CoreLibs/graphics" -import "coreLibs/object" +import "CoreLibs/object" import "pdParticles" local gfx = playdate.graphics @@ -29,9 +29,9 @@ function menu() gfx.clear() gfx.drawText("Press A to Play", 200 - gfx.getTextSize("Press A to Play") / 2, 120) - if playdate.buttonJustPressed(playdate.kButtonA) then - playdate.update = game - partObstacles:add(30) + if playdate.buttonJustPressed(playdate.kButtonA) then + playdate.update = game + partObstacles:add(30) end end @@ -39,7 +39,7 @@ end function game() -- [[ UPDATE ]] -- - + -- set the player's position based on player input local changeX, changeY = 0, 0 if playdate.buttonIsPressed(playdate.kButtonUp) then @@ -84,4 +84,4 @@ function game() end playdate.update = menu -- override the playdate.update() function, setting it to the menu() function --- cool lua feature, way cleaner than a state machine \ No newline at end of file +-- cool lua feature, way cleaner than a state machine From e1ae3c0a8d8b221eadfc3933c5dc53a4538ebf36 Mon Sep 17 00:00:00 2001 From: GammaGames Date: Thu, 4 Jul 2024 01:38:11 -0600 Subject: [PATCH 7/9] more refactor --- pdParticles.lua | 87 +++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 47 deletions(-) diff --git a/pdParticles.lua b/pdParticles.lua index a67791f..6dbd114 100644 --- a/pdParticles.lua +++ b/pdParticles.lua @@ -36,7 +36,7 @@ function Particle:init(x, y, pool_size) self.available[index] = index end self:create(pool_size) - particles[#particles+1] = self + table.insert(particles, self) end function Particle:default(part) @@ -181,11 +181,15 @@ end -- particles function Particle:getParticles() - return self.particles + return self.partlist +end + +function Particle:getActive() + return self.active end function Particle:clearParticles() - self.particles = {} + self.partlist = {} self.active = {} self.available = {} end @@ -200,7 +204,7 @@ local function remove(index, active, available) table.remove(active, index) end -local function decay(parts, active, available) +local function decay(parts, active, available, decay) for i = #active, 1, -1 do local index = active[i] local part = parts[index] @@ -296,7 +300,7 @@ function ParticleCircle:update() playdate.graphics.setLineWidth(w) playdate.graphics.setColor(c) if self.mode == 1 then - decay(self.particles, self.decay) + decay(self.partlist, self.active, self.available, self.decay) elseif self.mode == 0 then disappear(self.partlist, self.active, self.available) elseif self.mode == 2 then @@ -354,8 +358,9 @@ function ParticlePoly:update() local w = playdate.graphics.getLineWidth() local c = playdate.graphics.getColor() playdate.graphics.setColor(self.colour) - for part = 1, #self.particles, 1 do - local poly = self.particles[part] + for i = #self.active, 1, -1 do + local index = self.active[i] + local poly = self.partlist[index] local polygon = {} local degrees = 360 / poly.points for point = 1, poly.points, 1 do @@ -376,23 +381,19 @@ function ParticlePoly:update() poly.speed += poly.acceleration / 100 - self.particles[part] = poly + self.partlist[index] = poly end playdate.graphics.setLineWidth(w) playdate.graphics.setColor(c) if self.mode == 1 then - decay(self.particles, self.decay) - + decay(self.partlist, self.active, self.available, self.decay) elseif self.mode == 0 then - disappear(self.particles) - + disappear(self.partlist, self.active, self.available) elseif self.mode == 2 then - loop(self.particles, self.bounds) - + loop(self.partlist, self.active, self.available, self.bounds) else - stay(self.particles, self.bounds) - + stay(self.partlist, self.active, self.available, self.bounds) end end @@ -451,8 +452,9 @@ function ParticleImage:default(part) end function ParticleImage:update() - for part = 1, #self.particles, 1 do - local img = self.particles[part] + for i = #self.active, 1, -1 do + local index = self.active[i] + local img = self.partlist[index] img.image:drawRotated(img.x,img.y,img.rotation,img.size) @@ -463,29 +465,26 @@ function ParticleImage:update() img.speed += img.acceleration / 100 - self.particles[part] = img + self.partlist[index] = img end if self.mode == 1 then - decay(self.particles, self.decay) - + decay(self.partlist, self.active, self.available, self.decay) elseif self.mode == 0 then - disappear(self.particles) - + disappear(self.partlist, self.active, self.available) elseif self.mode == 2 then - loop(self.particles, self.bounds) - + loop(self.partlist, self.active, nil, self.bounds) else - stay(self.particles, self.bounds) - + stay(self.partlist, self.active, self.available, self.bounds) end end class("ParticleImageBasic", {type = 3}).extends(ParticleImage) function ParticleImageBasic:update() - for part = 1, #self.particles, 1 do - local img = self.particles[part] + for i = #self.active, 1, -1 do + local index = self.active[i] + local img = self.partlist[index] img.image:drawScaled(img.x,img.y,img.size) @@ -496,21 +495,17 @@ function ParticleImageBasic:update() img.speed += img.acceleration / 100 - self.particles[part] = img + self.partlist[index] = img end if self.mode == 1 then - decay(self.particles, self.decay) - + decay(self.partlist, self.decay) elseif self.mode == 0 then - disappear(self.particles) - + disappear(self.partlist) elseif self.mode == 2 then - loop(self.particles, self.bounds) - + loop(self.partlist, self.bounds) else - stay(self.particles, self.bounds) - + stay(self.partlist, self.bounds) end end @@ -531,8 +526,9 @@ end function ParticlePixel:update() local c = playdate.graphics.getColor() playdate.graphics.setColor(self.colour) - for part = 1, #self.particles, 1 do - local pix = self.particles[part] + for i = #self.active, 1, -1 do + local index = self.active[i] + local pix = self.partlist[index] playdate.graphics.drawPixel(pix.x,pix.y,pix.size) @@ -541,19 +537,16 @@ function ParticlePixel:update() pix.speed += pix.acceleration / 100 - self.particles[part] = pix + self.partlist[index] = pix end playdate.graphics.setColor(c) if self.mode == 0 then - disappear(self.particles) - + disappear(self.partlist, self.active, self.available) elseif self.mode == 2 then - loop(self.particles, self.bounds) - + loop(self.partlist, self.active, nil, self.bounds) else - stay(self.particles, self.bounds) - + stay(self.partlist, self.active, self.available, self.bounds) end end From 564b064643916971c05e177558cf31220e6f32f6 Mon Sep 17 00:00:00 2001 From: GammaGames Date: Thu, 4 Jul 2024 01:39:29 -0600 Subject: [PATCH 8/9] case sensitive and remove print --- Examples/Particle Player/main.lua | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Examples/Particle Player/main.lua b/Examples/Particle Player/main.lua index 9f07bc8..ed16bb0 100644 --- a/Examples/Particle Player/main.lua +++ b/Examples/Particle Player/main.lua @@ -1,7 +1,7 @@ local gfx = playdate.graphics -import "coreLibs/graphics" -import "coreLibs/object" +import "CoreLibs/graphics" +import "CoreLibs/object" import "pdParticles" local speed = 3 @@ -27,8 +27,6 @@ function playdate.update() part:add(1) Particles:update() - print(#part:getParticles()) - local vel = {0,0} if playdate.buttonIsPressed(playdate.kButtonDown) then @@ -45,4 +43,4 @@ function playdate.update() end part:moveBy(vel[1] * speed,vel[2] * speed) -end \ No newline at end of file +end From 7dd8481f82fd9dd2bf94d245bebc7fb7f494e266 Mon Sep 17 00:00:00 2001 From: GammaGames Date: Thu, 4 Jul 2024 01:44:25 -0600 Subject: [PATCH 9/9] fix duplicate indexes --- pdParticles.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/pdParticles.lua b/pdParticles.lua index 6dbd114..2efbd50 100644 --- a/pdParticles.lua +++ b/pdParticles.lua @@ -32,9 +32,6 @@ function Particle:init(x, y, pool_size) self.partlist = table.create(pool_size, 0) self.active = table.create(pool_size, 0) self.available = table.create(pool_size, 0) - for index = 1, pool_size do - self.available[index] = index - end self:create(pool_size) table.insert(particles, self) end