Here’s a snippet of my code that isolates the problem. Issue is, when two or more circles are added to the table, when one is destroyed (table.remove) the project spits out an error. Any help is appreciated, thanks!
--# Main
function setup()
displayMode(FULLSCREEN)
st = SmokeTest()
end
function draw()
st:draw()
end
function touched(touch)
st:touched(touch)
end
--# Smoke
Smoke = class()
function Smoke:init(killDistance)
self.particles = {}
self.kill = killDistance
end
function Smoke:addParticles(position, direction)
-- 1 = color, 2 = direction, 3 = start position, 4 = current positiom
local pos = table.maxn(self.particles) +1
self.particles[pos] = {}
self.particles[pos][1] = vec4(255,145,0,255)
self.particles[pos][2] = direction
self.particles[pos][3] = position
self.particles[pos][4] = position
end
function Smoke:draw()
for p = 1, table.maxn(self.particles) do
if self.particles[p][4]:dist(self.particles[p][3]) < self.kill then
fill(255, 115, 0, 255)
ellipse(self.particles[p][4].x, self.particles[p][4].y, WIDTH/10)
self.particles[p][4] = self.particles[p][4] + self.particles[p][2]
else
print(self.particles[p][4]:dist(self.particles[p][3]))
print(table.maxn(self.particles).."s2")
table.remove(self.particles, p)
print(table.maxn(self.particles).."s1")
end
end
end
function Smoke:touched(touch)
-- Codea does not automatically call this method
end
--# SmokeTest
--For testing and demonstration of smoke class. Remove in finished project!
SmokeTest = class()
function SmokeTest:init()
self.delay = 1
self.smoke = Smoke(300)
end
function SmokeTest:draw()
background(0, 149, 255, 255)
self.smoke:draw()
end
function SmokeTest:touched(touch)
if touch.state == BEGAN or MOVING then
if self.delay == 5 then
self.delay = 1
self.smoke:addParticles(vec2(CurrentTouch.x, CurrentTouch.y), vec2(2,0))
else
self.delay = self.delay + 1
end
end
end
you’re removing the item from the table in the middle of a for loop. So when it gets to the last entry in the loop it finds there’s nothing there. You either need to use an iterator like ipairs (which will request the next entry in the table if it exists each run through the loop) or loop backwards. Also you can use # instead of table.maxn. Try this in draw: for p = #self.particles, 1 , -1 do
It depends on how you add things to the table. table.insert(myTable, value) defaults to adding items at the position #myTable+1, it’s the same as myTable[#myTable+1]=value. But you could insert things at the front of the table by specifying position 1, table.insert(myTable, 1, value), then the oldest entry would be highest. Either way though, when you table.remove all items above the one deleted move down one position, and the table length decreases by one. But in your original code, you’ve already given the for loop an instruction based on the length of the table before you removed an element, so the loop goes past the end of the table and crashes. By the way, there is a dedicated color(r,g,b,a) wrapper, don’t use a vec4
If I may add my 2c worth of code change suggestions, here’s a rewrite. Not sure I didn’t introduce bugs, but just wanted to share how I would have written it, with some comments.
--# Main
function setup()
displayMode(FULLSCREEN)
st = SmokeTest()
end
function draw()
st:draw()
end
function touched(touch)
st:touched(touch)
end
--# Particle
---- Creating a class for the particle doesn't cost much, and allows to
---- really separate things that only depend on the particle itself
---- from more global things.
Particle = class()
function Particle:init(col,direction,position)
self.col = col
self.direction = direction
self.start = position
self.current = position
self.alive = true -- make the particle aware of whether it is still alive or not
en
---- this may appear in different places, so why not add it as a function on particle?
function Particle:distance()
return self.current:dist(self.start)
end
---- having the draw() function on the particle instead of inside the
---- smoke class will allow you to create particles outside of the
---- smoke class and display them, which may make debugging easier
---- (for instance, if the 'touch' features seam tricky, you can just manually
---- create a Particle using hardcoded values)
function Particle:draw()
print("Drawing")
if self.alive then -- only draw if alive
pushStyle() --- better to pushstyle before changing fill
fill(255, 115, 0, 255)
ellipse(self.current.x, self.current.y, WIDTH/10)
self.current = self.current + self.direction
popStyle() --- and popstyle when you are done so you revert to the previous fill color
end
end
--# Smoke
Smoke = class()
function Smoke:init(killDistance)
self.particles = {}
self.kill = killDistance
end
function Smoke:addParticles(position, direction)
-- not the cleanest, but a standard pattern to add stuff to a table is t[#t+1]=v
self.particles[#self.particles+1] = Particle(vec4(255,145,0,255),direction,position)
end
function Smoke:draw()
for i,p in ipairs(self.particles) do
if p ~= nil then
if p.alive and (p.distance() >= self.kill) then
print(p.distance())
print(#self.particles.."s2")
table.remove(self.particles, i)
print(#self.particles.."s1")
end
p:draw()
end
end
--# SmokeTest
--For testing and demonstration of smoke class. Remove in finished project!
SmokeTest = class()
function SmokeTest:init()
self.delay = 1
self.smoke = Smoke(300)
end
function SmokeTest:draw()
background(0, 149, 255, 255)
self.smoke:draw()
end
function SmokeTest:touched(touch)
if touch.state == BEGAN or MOVING then
if self.delay == 5 then
self.delay = 1
self.smoke:addParticles(vec2(CurrentTouch.x, CurrentTouch.y), vec2(2,0))
else
self.delay = self.delay + 1
end
end
end
Thanks. I’ll put some of this into my code. It’s quite a bit different already than the version I posted earlier, but lots of these ideas will still work. And welcome to the forum!
I decided to give your code a try, and took some time to get the errors out of it (yes, we all do that sometimes, coding something and not testing it xd)
the Particle:init isn’t completely closed (line 28) there’s ‘en’ instead of ‘end’
the Smoke:draw has 1 end short (if looking at the format, it would be the “if p ~= nil” one that isn’t closed)
in the Smoke:draw, you call the function “p.distance” 2 times, it should be “p:distance” to make it work
Note that I’m not trying to break anyone down, but just trying to help
Apologies for the few typos. I first typed this in Codea then copy pasted to my browser and that’s when I added a few remarks and more changes. Hopefully you didn’t waste too much time fixing these, but what I wanted to get across were the ideas of factoring the code in smaller pieces.