Removing and Adding things to Tables

Hi Everyone,
                    So the jist is I’m trying to have draggable objects in a table. When you drag them together, they both get removed and a new one appears. This is repeated and repeated.

The problem is I struggle to remove the objects from the table and then add new ones in without throwing up errors later down the line. I think it’s because when you remove one from the table it moves all the next objects down (So #4 is now #3 and #5 is now #4 etc)

So this means when it looks up for a object in the table to delete it doesn’t find it where it should be.

The error is “attempt to perform arithmetic on field ‘x’” 
I tried looking online but couldn’t identify how to successfully remove and add on the fly like this for my game.

Here’s the simple code.

 

module(..., package.seeall); function new() local localGroup = display.newGroup(); local onTouch local MonMon = {} for i = 1,4 do MonMon[i] = display.newImageRect( "Images/Monmon.png", 320, 320 ) MonMon[i].x = math.random (15, \_W-15) MonMon[i].y = math.random (15, \_H-15) MonMon[i].ID = i localGroup:insert(MonMon[i]) --print(i) end -- Spawn an item local function spawnMonMon( ) local newcreature = display.newImageRect( "Images/Monmon.png", 320, 320 ) newcreature.x = math.random (15, \_W-15) newcreature.y = math.random (15, \_H-15) newcreature.ID = MonMon[#MonMon+1] localGroup:insert(newcreature) newcreature:addEventListener( "touch", onTouch) MonMon[#MonMon+1] = newcreature print(newcreature.ID) end -- Circle-based collision detection local function hasCollidedCircle( obj1, obj2 ) if ( obj1 == nil ) then -- Make sure the first object exists return false end if ( obj2 == nil ) then -- Make sure the other object exists return false end local dx = obj1.x - obj2.x local dy = obj1.y - obj2.y local distance = math.sqrt( dx\*dx + dy\*dy ) local objectSize = (obj2.contentWidth/2) + (obj1.contentWidth/2) if ( distance \< objectSize ) then return true end return false end -- touch listener function onTouch = function( event ) local self = event.target local phase = event.phase if "began" == phase then print("Pressed") -- Make target the top-most object local parent = self.parent parent:insert( self ) display.getCurrentStage():setFocus( self ) -- Spurious events can be sent to the target, e.g. the user presses -- elsewhere on the screen and then moves the finger over the target. -- To prevent this, we add this flag. Only when it's true will "move" -- events be sent to the target. self.isFocus = true -- Store initial position self.x0 = event.x - self.x self.y0 = event.y - self.y elseif self.isFocus then if "moved" == phase then --print(table.maxn(MonMon)) self.x = event.x - self.x0 self.y = event.y - self.y0 elseif "ended" == phase or "cancelled" == phase then for i = 1, table.maxn(MonMon) do if ( self ~= MonMon[i] and hasCollidedCircle( self, MonMon[i] )) then -- Snap in place print("Collided") local midX = (self.x + MonMon[i].x)\*0.5 local midY = (self.y + MonMon[i].y)\*0.5 transition.to(MonMon[i], {time=500, x=midX, y=midY, transition= easing.inOutExpo, onComplete=function() MonMon[i]:removeEventListener("touch", onTouch) display.remove(MonMon[i]); MonMon[i] = nil table.remove(MonMon, MonMon[MonMon[i]]) end}) transition.to(self, {time=500, x=midX, y=midY, transition= easing.inOutExpo, onComplete=function() self:removeEventListener("touch", onTouch) display.remove(self); MonMon[self.ID] = nil table.remove(MonMon, MonMon[self.ID]) end}) display.getCurrentStage():setFocus( nil ) self.isFocus = false spawnMonMon() return true end end display.getCurrentStage():setFocus( nil ) self.isFocus = false end end return true end -- make all starting objects listen for touch events for i=1, table.maxn(MonMon) do MonMon[i]:addEventListener( "touch", onTouch) end return localGroup; end

Without looking at your code, I can give you a tip that should help you.

Do not index by numbers, index by object IDs.

i.e. DO NOT do this:

local myObjects = {} local tmp = display.newCircle( 10, 10, 10 ) myObjects[#myObjects+1] = tmp

Why?  Because it is a pain to remove objects from this table later.  

However, if you do this, it is easy:

local myObjects = {} local tmp = display.newCircle( 10, 10, 10 ) myObjects[tmp] = tmp tmp.touch = function( self, event ) if(event.phase == "ended") then myObjects[self] = nil -- remove from table display.remove(self) -- destroy touched object end return true end tmp:addEventListener( "touch" )

Without looking at your code, I can give you a tip that should help you.

Do not index by numbers, index by object IDs.

i.e. DO NOT do this:

local myObjects = {} local tmp = display.newCircle( 10, 10, 10 ) myObjects[#myObjects+1] = tmp

Why?  Because it is a pain to remove objects from this table later.  

However, if you do this, it is easy:

local myObjects = {} local tmp = display.newCircle( 10, 10, 10 ) myObjects[tmp] = tmp tmp.touch = function( self, event ) if(event.phase == "ended") then myObjects[self] = nil -- remove from table display.remove(self) -- destroy touched object end return true end tmp:addEventListener( "touch" )