Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
Kill level? — Gideros Forum

Kill level?

Paulo777Paulo777 Member
edited March 2018 in User experience
Hi all!
Can anyone tell me what can I do to literally kill a level? I want to restart a level, tried everything and the only thing that makes me go into here is when I tried everything and nothing absolutely nothing works.

I tried to collect garbage, no works
I tried to change where some sprites must start, (self, stage) just got trouble.

Is there a thing that I didn't try? Is there a thing that I still don't know?

I am since morning thinking hard and I don't know what to do anymore.

I have a level, and in this level I add a table with sprites in it. Inside a sprite, there is another sprite (bullets) that I load and inside this sprite it is generated inside 'stage'. It is working until I'm getting to restart the level... when the level is 'restarted' the sprites just gets invisible and 'shooting', it doesn't disappear and I'm getting hardly frustrated of how I can't understand how it works...

What I want is simple. Restart a level and kill everthing that has in it.
«1

Comments

  • antixantix Member
    edited March 2018
    A simple restart level function is possible as long as you keep track of everything you created in that level.

    Here is an example that creates a table of sprites, each with random numbers of child and grandchild sprites.

    The table is then iterated and all sprites recursively removed.

    In your own code you would have to remove any event listeners that each sprite might have (although in a perfect world they should not have any besides touch listeners if required).

    Once all of the sprites have been removed, if there are no references to them anywhere else in your code, they should be garbage collected.
    -- counters
    local created = 0
    local killed = 0
     
    -- an empty table of sprites
    local tableOfSprites = {}
     
    -- create a random number of sprites with random numbers of children and grandchildren
    for i = 1, math.random(2, 12) do
      -- create a new sprite
      local sprite = Sprite.new()
       -- each sprite in table gets a random number of children sprites
      for i = 1, math.random(2, 10) do
        sprite:addChild(Sprite.new())
        created += 1
        -- and a random number of grandchildren sprites
        for i = 1, math.random(2, 5) do
          sprite:getChildAt(math.random(1, sprite:getNumChildren())):addChild(Sprite.new())
          created += 1
        end
      end
      table.insert(tableOfSprites, sprite) -- add sprite to the table
      stage:addChild(sprite)
      created += 1
    end
    print('created ' .. created .. ' created')
     
    -- kill all sprites
    for i = #tableOfSprites, 1, -1 do -- always process from last to first
      local function killChild(sprite) -- a function to recursively kill a sprite, its children, grandchildren, etc, etc
        local count = sprite:getNumChildren()
        if count > 0 then
          for i = count, 1, -1 do -- always process from last to first
            killChild(sprite:getChildAt(i))
          end
        end
        -- actually kill the sprite
        sprite:removeFromParent() -- remove the sprite
        --
        -- remove event listeners here
        --
        killed += 1
      end
     
      killChild(table.remove(tableOfSprites, i)) -- kill it!!
    end
     
    print('killed ' .. killed .. ' sprites')
     
    -- tableOFSprites will now be an empty table
     
    collectgarbage('collect')
    I hope this helps you grasp the concept. If not let us know ;)

    Likes: Apollo14

    +1 -1 (+1 / -0 )Share on Facebook
  • for i = #tableOfSprites, 1, -1 do -- always process from last to first
      local function killChild(sprite)
    @antix thanks a lot! It wasn't my question, but the answer actually is extremely useful for me.

    I guess function 'killChild(sprite)' would better be global?
    > Newcomers roadmap: from where to start learning Gideros
    "What one programmer can do in one month, two programmers can do in two months." - Fred Brooks
    “The more you do coding stuff, the better you get at it.” - Aristotle (322 BC)
  • Paulo777Paulo777 Member
    edited March 2018
    Hello! Thank you for the answer!

    To load my sprites, I'm doing this way:
    counter += 1
    local gears = {
    		Gear.new(ship),
    		Gear.new(ship),
    		Gear.new(ship),
    		Gear.new(ship),
    		Gear.new(ship),
    	}
     
    if counter == 10 then
    self:addChild(gears[1])
    end
     
    -- so on with the others and when I load the last one:
    if counter == 500 then
    self:addChild(gears[5])
    counter = 0
    end
    when the couter is 0 repeat and load more sprites.

    Is that a wrong way to do?
  • jdbcjdbc Member
    Just use SceneManager to create new instance of the level class

    GameScene = Core.class(Sprite)

    function GameScene:init()
    # do some staff
    end

    In main.lua define your scenes

    scenes = {"menu", "game"}
    sceneManager = SceneManager.new({
    ["menu"] = MenuScene,
    ["game"] = GameScene,
    })

    to change to GameScene
    sceneManager:changeScene(scenes[2]);

    it will create new GameScene from scratch.

    Likes: Apollo14

    +1 -1 (+1 / -0 )Share on Facebook
  • antixantix Member
    edited March 2018
    @Apollo14 It could be global but if it was just being used for that one table I'd make it local.

    @jdbc That would work I suppose but if the scene to be discarded contains persistent game data, it would also be recycled. The best approach is most likely to manage the resources internally in the scene.

    @Paulo777 So at the start of the scene you create 5 gears and then as counter increments you add gears to the stage at preset times right? If it works, then its fine. You can reiterate later and optimize it. So you could use something like this for your project..
    local gears = {}
     
    -- create a number of gears
    local function createGears(count)
      for i = 1, count do
        gears[#gears + 1] = Gear.new(ship)
      end
    end
     
    -- kill all gears
    local function killGears()
      local function kill(gear) -- recursively kill a sprite, its children, grandchildren, etc, etc
        local count = gear:getNumChildren()
        if count > 0 then
          for i = count, 1, -1 do
            kill(gear:getChildAt(i))
          end
        end
        gear:removeFromParent()
      end
     
      for i = #gears, 1, -1 do
        kill(table.remove(gears, i))
      end
    end
  • jdbcjdbc Member
    edited March 2018
    antix said:

    @Apollo14 It could be global but if it was just being used for that one table I'd make it local.

    @jdbc That would work I suppose but if the scene to be discarded contains persistent game data, it would also be recycled. The best approach is most likely to manage the resources internally in the scene.

    @Paulo777 So at the start of the scene you create 5 gears and then as counter increments you add gears to the stage at preset times right? If it works, then its fine. You can reiterate later and optimize it. So you could use something like this for your project..

    local gears = {}
     
    -- create a number of gears
    local function createGears(count)
      for i = 1, count do
        gears[#gears + 1] = Gear.new(ship)
      end
    end
     
    -- kill all gears
    local function killGears()
      local function kill(gear) -- recursively kill a sprite, its children, grandchildren, etc, etc
        local count = gear:getNumChildren()
        if count > 0 then
          for i = count, 1, -1 do
            kill(gear:getChildAt(i))
          end
        end
        gear:removeFromParent()
      end
     
      for i = #gears, 1, -1 do
        kill(table.remove(gears, i))
      end
    end
    Use datasaver before reload (change) scene to store your data. Use Core.class() and SceneManager, it is easier to keep the game state. I have done this in all my games and I have developed more than 10.

    Take a look to the source code of my complete game. It has ads, social, leaderboard, scenemanager...
    https://github.com/jdbcdev/Dots

    All my scenes, ads and social manager, leaderboard are just classes. Easier and clean code.

    Do not use global variables or functions to keep game state, it is really difficult to keep it.

    Likes: Paulo777, Apollo14

    +1 -1 (+2 / -0 )Share on Facebook
  • antix said:

    @Apollo14 It could be global but if it was just being used for that one table I'd make it local.

    @jdbc That would work I suppose but if the scene to be discarded contains persistent game data, it would also be recycled. The best approach is most likely to manage the resources internally in the scene.

    @Paulo777 So at the start of the scene you create 5 gears and then as counter increments you add gears to the stage at preset times right? If it works, then its fine. You can reiterate later and optimize it. So you could use something like this for your project..

    local gears = {}
     
    -- create a number of gears
    local function createGears(count)
      for i = 1, count do
        gears[#gears + 1] = Gear.new(ship)
      end
    end
     
    -- kill all gears
    local function killGears()
      local function kill(gear) -- recursively kill a sprite, its children, grandchildren, etc, etc
        local count = gear:getNumChildren()
        if count > 0 then
          for i = count, 1, -1 do
            kill(gear:getChildAt(i))
          end
        end
        gear:removeFromParent()
      end
     
      for i = #gears, 1, -1 do
        kill(table.remove(gears, i))
      end
    end
    I didn't understand the part of the 'gear'... woudn't that be "Gear"? I mean "gear" doesn't exists
  • piepie Member
    @Paulo777 gear exists in
    local function kill(gear)
    It is how antix named the (parameter), which is the same as writing
     local function kill(par)  
    local gear = par

    Likes: Paulo777

    +1 -1 (+1 / -0 )Share on Facebook
  • pie said:

    @Paulo777 gear exists in

    local function kill(gear)
    It is how antix named the (parameter), which is the same as writing
     local function kill(par)  
    local gear = par
    Omg! What an idiot I am! Thank you! hahah

    Likes: Paulo777, antix

    +1 -1 (+2 / -0 )Share on Facebook
  • jdbc said:

    antix said:

    @Apollo14 It could be global but if it was just being used for that one table I'd make it local.

    @jdbc That would work I suppose but if the scene to be discarded contains persistent game data, it would also be recycled. The best approach is most likely to manage the resources internally in the scene.

    @Paulo777 So at the start of the scene you create 5 gears and then as counter increments you add gears to the stage at preset times right? If it works, then its fine. You can reiterate later and optimize it. So you could use something like this for your project..

    local gears = {}
     
    -- create a number of gears
    local function createGears(count)
      for i = 1, count do
        gears[#gears + 1] = Gear.new(ship)
      end
    end
     
    -- kill all gears
    local function killGears()
      local function kill(gear) -- recursively kill a sprite, its children, grandchildren, etc, etc
        local count = gear:getNumChildren()
        if count > 0 then
          for i = count, 1, -1 do
            kill(gear:getChildAt(i))
          end
        end
        gear:removeFromParent()
      end
     
      for i = #gears, 1, -1 do
        kill(table.remove(gears, i))
      end
    end
    Use datasaver before reload (change) scene to store your data. Use Core.class() and SceneManager, it is easier to keep the game state. I have done this in all my games and I have developed more than 10.

    Take a look to the source code of my complete game. It has ads, social, leaderboard, scenemanager...
    https://github.com/jdbcdev/Dots

    All my scenes, ads and social manager, leaderboard are just classes. Easier and clean code.

    Do not use global variables or functions to keep game state, it is really difficult to keep it.
    I took a brief look at your game. I felt really impressed about it. Congrats! It is really clean code and simple...
  • antixantix Member
    @jdbc yep you can totally save state between levels but I feel that is just wearing out the storage for no good reason :)

    I find it pretty easy to manage global vars myself. I only save to the device when the user decides to (with options screens and stuff).

    I use classes for pretty much everything too just because it makes things easier to manage (20,000+ lines of code in one file is daunting!) and it makes things easy to reuse in future projects.
  • antixantix Member
    @jdbc I was also just looking at your code for your dots game, great stuff :)

    I noticed that in the key events you are using stopPropagation() which the Gideros documentation specifies is only required for mouse and touches.. unless the documentation is wrong?
  • Paulo777Paulo777 Member
    edited April 2018
    @antix unfortunatelly I tried and keeps the same... The 'gears' on level restart gets invisible and shooting... it works for they don't appear in the game but they are not killed... keeps receiving events and shooting and they are only dead when I shoot with the 'ship' even invisible they receive events and die...
  • antixantix Member
    edited April 2018
    @Paulo777 If your gears have event listeners attached to them then you will need to remove those also...
    local function killGears()
    local function kill(gear) -- recursively kill a sprite, its children, grandchildren, etc, etc
      local count = gear:getNumChildren()
      if count > 0 then
        for i = count, 1, -1 do
          kill(gear:getChildAt(i))
        end
      end
     
      -- REMOVE EVENT LISTENERS HERE
     
      gear:removeFromParent()
    end
    Are you attaching an EnterFrame event to each one or other events?

    Likes: jdbc

    +1 -1 (+1 / -0 )Share on Facebook
  • hgy29hgy29 Maintainer
    All of this makes me wonder: wouldn’t it be cool to have a Sprite:removeAllListeners(recursive) kind of call ? To cleanup a whole sprite hierarchy
    +1 -1 (+6 / -1 )Share on Facebook
  • Paulo777Paulo777 Member
    edited April 2018
    @antix yes there is an enterFrame for its movement, and also the event... Strange that when I 'fire' I've just set a removefromparent inside gear class, so that when I fire, if it gets a given number of 'damage' it removes, and it works, why is it not working? I ask why should I have to worry about removing event listener if the other way (firing up against) works? I tried set Level = nil but also nothing...

    @hgy29 that'd be great! I'd love that, hahah
  • jdbcjdbc Member
    edited April 2018
    antix said:

    @jdbc I was also just looking at your code for your dots game, great stuff :)

    I noticed that in the key events you are using stopPropagation() which the Gideros documentation specifies is only required for mouse and touches.. unless the documentation is wrong?

    The documentation about Event class and stopPropagation

    http://docs.giderosmobile.com/reference/gideros/Event/stopPropagation#Event:stopPropagation

    Where did you see that is only for Touch or Mouse events? May be key events does nothing calling this method but it is working this code.

    SceneManager creates a new instance of your GameScene class when changes the current scene, I guess the previous GameScene instance is destroyed and all listeners disabled. May be we could check SceneManager source code to make sure about it.
  • antixantix Member
    @Paulo777 If your gears have event listeners attached to them then they will still be receiving those events and therefore be processed each frame.

    One way to make this work is to have a bool for each gear that lets the game logic know if it is alive or dead (should be processed or not).
    Gear = Core.class(Sprite)
     
    function Gear:init()
      self.alive = false
    end
     
    function Gear:respawn()
      self.alive = true
    end
     
    function Gear:update(dt)
      if self.alive then
        -- process gear logic here
      end
    end
    As well as that it seems that when you create each gear you are attaching an enter frame listener to it. It would be better to process the gears in your main enter frame listener.
    function Game:update(e) -- update every frame
      local dt = e.deltaTime
     
      local gears = self.gears
      for i = #gears, 1, -1 do
        local gear = gears[i]
        gear:update(dt) -- process each gears AI etc here
      end
     
    end
  • piepie Member
    As far as I know, to completely get rid of a scene in scenemanager you should not changeScene to itself: just create a dummy/empty scene to changescene to, that 'redirects' to your game scene.

    Ie.
    Scenes:
    Game
    Dummy

    When you are in Game and need to restart it changeScene to Dummy. In Dummy set the code to changeScene to Game. It should work.
    If I remember, the issue in changing scene to itself is that you never stop referencing scene vars and objects, and gc does not collect everything properly.

    Hope this helps :)

    Likes: Apollo14

    +1 -1 (+1 / -0 )Share on Facebook
  • antixantix Member
    @pie I'll remember that tip :)

    Still, it's pretty silly to just start a new blank scene, then start a new copy of the scene you want. In my opinion you really need to learn how to reset stuff without resorting to such methods :)

    Likes: jdbc

    +1 -1 (+1 / -0 )Share on Facebook
  • piepie Member
    @antix it's not elegant but it's fast to setup and effective: just add a random ingame hint in the dummy scene to look like it's made on purpose.. :p

    Likes: antix

    +1 -1 (+1 / -0 )Share on Facebook
  • Paulo777Paulo777 Member
    edited April 2018
    People... Unfortunatelly I'm getting disapointed with Gideros... The code @antix posted simply doesn't works, once I set prints to see what it was doing, okay but now I see that it is not entering the functions... Actually I have no way to go there is nothing that kills them! Simply cannot understand its behaviour... I realized that The gears I set, the code is not taking them, It seems that gideros is allocating them in a part of the memory that is unrechable. Tried to set booleans inside Gear class, NOTHING! The only way to solve that is to call the creator of the engine... To at least understand what's going on!! I'm getting bored cuz I spend energy on that to nothing
  • piepie Member
    @Paulo777 a bug is always possible, but I don't think that the issue is in the Engine at this level. Someone would have already noticed that. Which code exactly is not working? Sounds strange to be disappointed by gideros.. I guess you're missing something ;) sleep over it and look it again tomorrow. You could also zip your project, attach it here, and probably someone will tell you what's wrong there and why :)
  • Paulo777Paulo777 Member
    edited April 2018
    Thanks for the help... I will really zip my project because I have no any resources and if I keep on It I will go to nowhere. If anyone is willing to help me by testing my project and dive into my codes i'm quite appreciated. I'm brazilian but all my code is in english then I guess it is understandable oh if you could tell me tips and best practices also on my code I'm also very appreciated. :smile: :smile: :smile:
    zip
    zip
    ShootOut.zip
    1M
  • jdbcjdbc Member
    Paulo777 said:

    Thanks for the help... I will really zip my project because I have no any resources and if I keep on It I will go to nowhere. If anyone is willing to help me by testing my project and dive into my codes i'm quite appreciated. I'm brazilian but all my code is in english then I guess it is understandable oh if you could tell me tips and best practices also on my code I'm also very appreciated. :smile: :smile: :smile:

    It seems that songs are missing
  • jdbcjdbc Member
    jdbc said:

    Paulo777 said:

    Thanks for the help... I will really zip my project because I have no any resources and if I keep on It I will go to nowhere. If anyone is willing to help me by testing my project and dive into my codes i'm quite appreciated. I'm brazilian but all my code is in english then I guess it is understandable oh if you could tell me tips and best practices also on my code I'm also very appreciated. :smile: :smile: :smile:

    It seems that songs are missing
    Why are you using this code?

    self:removeEventListener(Event.ENTER_FRAME, self.onDetectCollision, self)

    You are removing a wrong EventListener, it should be just
    self:removeEventListener(Event.ENTER_FRAME, self.onEnterFrame)


    and this?
    stage:removeEventListener(Event.ENTER_FRAME, self.shot.onFire, self.shot)

    why are you using stage instead of self (current scene)
  • jdbcjdbc Member
    edited April 2018
    Why are you using this code?
    self:removeEventListener(Event.ENTER_FRAME, self.onDetectCollision, self)
    You are removing a wrong EventListener, it should be just
    self:removeEventListener(Event.ENTER_FRAME, self.onEnterFrame)
    and this?
    stage:removeEventListener(Event.ENTER_FRAME, self.shot.onFire, self.shot)
    why are you using stage instead of self (current scene)

    You have to stop music and removelisteners before change scene (reset same scene),
    for instance:
    self:removeEventListener(Event.ENTER_FRAME, self.onEnterFrame,self)
    self:removeEventListener(Event.TOUCHES_BEGIN, self.onPaddleTouches, self)
    self:removeEventListener(Event.TOUCHES_MOVE, self.onPaddleTouches, self)
    self:removeEventListener(Event.TOUCHES_END, self.onPaddleTouchesEnd, self)
    removes onEnterFrame listener, and touch listeners.
  • jdbcjdbc Member
    edited April 2018
    I guess you are not removing listeners properly.

  • Paulo777Paulo777 Member
    edited April 2018
    @jdbc I had to remove songs beucause of file lenght, just comment that ones.
  • jdbc said:

    Why are you using this code?

    self:removeEventListener(Event.ENTER_FRAME, self.onDetectCollision, self)
    ANSWER: It is an EnterFrame event to detect if a gear shot collided with ship.

    You are removing a wrong EventListener, it should be just
    self:removeEventListener(Event.ENTER_FRAME, self.onEnterFrame)
    and this?
    stage:removeEventListener(Event.ENTER_FRAME, self.shot.onFire, self.shot)
    why are you using stage instead of self (current scene)
    ANSWER: I had trouble using 'self', maybe something simple to solve but as it was working I left it this way.

    You have to stop music and removelisteners before change scene (reset same scene),
    for instance:
    self:removeEventListener(Event.ENTER_FRAME, self.onEnterFrame,self)
    self:removeEventListener(Event.TOUCHES_BEGIN, self.onPaddleTouches, self)
    self:removeEventListener(Event.TOUCHES_MOVE, self.onPaddleTouches, self)
    self:removeEventListener(Event.TOUCHES_END, self.onPaddleTouchesEnd, self)
    removes onEnterFrame listener, and touch listeners.
    This when? Level? Ship? Gear? all? Is this functions to add?
Sign In or Register to comment.