Quick Links: Gideros Home | Download Gideros | Developer Guide
When destroying a body, game says it is already destroyed.
  • ZizanymanZizanyman +1 -1
    Member
    I have this piece of code to destroy an object if it goes below a certain point. This code runs every frame in the "onEnterFrame" function.
     for i, val in pairs(self.balls) do
    if val:getY() > 500 then
    self.world:destroyBody(val.body)
    end
    end


    However, whenever the object goes below that point, an error comes up saying:
    Scenes/game.lua:74: Body is already destroyed.
    I tried adding code that checks if val.body is nil, and doesn't destroy it if so, but the error message still came up. Does anybody know why this is happening?
  • antixantix +1 -1
    Member
    Maybe you could add another variable to your balls and check that before attempting removal.
     for i, ball in pairs(self.balls) do
    if ball.alive then
    if ball:getY() > 500 then
    ball.alive = false
    self.world:destroyBody(ball.body)
    end
    end
    end

    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor
  • ZizanymanZizanyman +1 -1
    Member
    Thanks for the suggestion. When I tried this, I thought that it would work, but for some reason, it still doesn't. I'm really confused.
  • pie +1 -1
    Member
    I had this too: I am not sure to remember how, but I think that I fixed it referring directly to self.balls[i] and/or skipping some frames (nobody would notice if you do this every 3/5 frames instead of each one)
  • Hi @Zizanyman, also another solution is
    for _, obj in pairs(self.balls) do
    if obj:getY() > 500 then
    Timer.delayedCall(0.5, function() --because world is locked
    obj.body:setActive(false)
    obj:setVisible(false)
    end)
    end
    end

    And re-use this body in another moment for example "a box" only destroy fixture here a simple idea:
    for _, obj in pairs(self.balls) do
    if not obj.body:isActive() then
    Timer.delayedCall(0.5, function()
    obj.body:destroyFixture(obj.body.fixture)
    obj:setTexture(Texture.new("myBox",true))
     
     
    local poly = b2.PolygonShape.new()
    poly:setAsBox(obj:getWidth(), obj:getHeight())
     
    local fixture = body:createFixture{shape = poly, density = 1.0, friction = 0.1, restitution = 0.2}
    obj.body.fixture = fixture -- you can have a setup function for your physic
     
    obj.body:setActive(true)
    obj:setVisible(true)
    break --if you don't need more
    end)
    end
    end

    It save a lot memory because it works like a sprite pool


  • Yes I forgot it
    http://docs.giderosmobile.com/reference/physics/b2World/destroyBody#b2.World:destroyBody

    So another solution if you are not going to need the object anymore is:
    for _, obj in pairs(self.balls) do
    if obj:getY() > 500 then
    obj:removeFromParent() --because on function() yourClass:onEnterFrame "loop for" takes num child on scene
    Timer.delayedCall(0.5, function() --because world is locked
    self.world:destroyBody(obj.body)
    obj.body = nil -- this line is optional
    end)
    end
    end


    I think everything will depend on what you want to do



  • ZizanymanZizanyman +1 -1
    Member
    Thank you for the feedback, but no matter which method I try, I keep getting error messages. However, I changed around the game a bit so that I don't have to destroy the ball once it goes under a certain level. Thank you all for the feedback anyway though, even though it didn't work in my case it may help others who are having a similar issue!
  • Hi @Zizanyman
    It's good you have found your own solution, but just out of curiosity is using your own script or, are you using Box2dEasy or any? If you are using your own script, how are you definied running the world (EnterFrame)? for future references.

  • ZizanymanZizanyman +1 -1
    Member
    I was using Box2d for this game.
  • john26john26 +1 -1
    Maintainer
    It's not obvious from your code that val.body is actually a body. It seems val is a Sprite but Sprites do not have bodies by default in Gideros. So when you create the body you need to create your own field body and set it equal to the box2d body.

    Could you perhaps write a short, complete project which illustrates the problem and gives the error.

    In Nebula Retro, I do the same thing: destroy crates when they fall off the screen (ie I destroy both the Sprite and the body), I didn't get an error, see here for how I did it:

    https://github.com/john-blackburn/nebularetro/blob/master/main.lua#L1079

    This function is called on ENTER_FRAME as well.

    The .body field has been set up earlier (manually by me, it is not a standard Sprite field)
  • ZizanymanZizanyman +1 -1
    Member
    Every ball object had a box2d body attached to it. I was able to access it using val.body in other parts of the script, it just didn't work when destroying it for some reason.
  • hgy29hgy29 +1 -1 (+2 / -0 )
    Maintainer
    Maybe a silly remark, but do you actually remove your object from the balls array at same point ? If not then your object will be destroyed again and again each frame

    Likes: antix, john26

  • ZizanymanZizanyman +1 -1
    Member
    I tried adding code that set self.balls[i] to nil, and it still didn't work.
  • Hi @Zizanyman as said @hgy29 you must remove object from the balls array because "nil" doesn't work try the next:
    local l={1,2,3,4,5,6}
    --case 1
    for k, v in pairs(l) do print (v); if v==4 then l[k] = nil end end
    --case 2
    for k, v in pairs(l) do print(k,v) end
     
    -- case 3
    table.remove(l,4)
    for k, v in pairs(l) do print(k,v) end
     
    --Output case 1
    --1,2,3,4,5,6
     
    --[[ Output case 2
    1 1
    2 2
    3 3
    5 5
    6 6
    number four is not appear but index of number 6 is six and not five
    ]]

     
    --[[Output case 3
    1 1
    2 2
    3 3
    4 5
    5 6
    nil was remove of table "l" and six has index five
    ]]


  • antixantix +1 -1
    Member
    By starting at the end of the table and reversing to the beginning of the table is the best method.
    local objects = {
    {x = 0, y = 0, },
    {x = 0, y = 500, },
    {x = 0, y = 0, },
    }
     
    for i = #objects, 1, -1 do -- reverse through table
    local remove = table.remove
    local object = objects[i]
    if object.y >= 500 then
    remove(objects, i) -- removes object
    end
    end

    In this example the second object will be removed, whilst maintaining the sanity of the table.

    This is the way (IMHO) to work with tables of game objects.. pairs is slow.
    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor
  • "Yep, that's right" @antix also I've used reversing in collision when I must destroy object but currently I'm not using it because I'm reusing my objects.

  • antixantix +1 -1
    Member
    @HubertRonald, ahh okay. You can always keep two tables (active and inactive) and move them between tables as required :)
    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Login with Facebook Sign In with Google Sign In with OpenID

In this Discussion

Top Posters