Game Memory, Scene recycling and scene destroy

Hi All,

I need a little clarity.

I am creating a point and click adventure game that has 78 scenes.

However, when I am testing.

I get about half way through the game and scenes start to load slowly, things start hanging etc.

I’ve made an effort to empty all variables and all images at the end of each scene.

But I’ve not “removed” the scenes themselves.

I’ve read this tutorial a number of times.

https://docs.coronalabs.com/api/library/composer/removeScene.html

But what I do not “get” is…

Do I need to recycle or completely remove the scene object.

I can not understand what the difference is.

Why would I chose one over the other.

Can anyone help a newbie out??

Thanks

Angela

UPDATE:

I thought I would implement the full scene remove approach.

But the game is still lagging and then hanging as I enter half way through the game play.

– Completely remove the scene, including its scene object

composer.removeScene( “scene1” )

Has been applied at the end of every scene.

No change - what else do I try??

UPDATE:

I have built in a collectgarbage(“count”) line of code at the start of each scene as it loads, to see what is happening with memory usage based on this tutorial:

https://docs.coronalabs.com/api/library/global/collectgarbage.html

The first dozen scenes run between…

lua memory in use: 514.4619140625

and

lua memory in use: 926.1943359375
 

then as I start to collect objects, memory jumps to…

lua memory in use: 1150.2138671875
and

lua memory in use: 1950.3701171875
this is when things start to feel like they are lagging…

but then it jumps to…

lua memory in use: 5225.3671875
and

lua memory in use: 9282.6171875

which is when I have to quite corona, by which to kill all memory in use and start again.

How can I manage the use of memory better?

Ideas, suggestions, things I can check on.

Any input greatly appreciated.

Thanks

Angela

Hi Angela,

That sounds like a good old fashioned memory leak.  Can you share your code with us please.  Remember to use the code button or enclose the code in tags.

While we wait on you to share some code, here are some common things to look for.

  1. If you’re not properly putting display objects into scene groups, removing the scene won’t do anything. Only objects added to the scene’s view group will be managed/removed.

  2. If you’re using global variables (usually without realizing it), you’re setting yourself up for memory leaks. For instance if you use the same variable in scene1 and scene2, when scene2 loads, the variable now references the scene2 version and you’ve lost the handle to the scene1 version and you get a memory leak.

Rob

Are you tracking memory usage?

You can add a basic meter by downloading this:

https://github.com/roaminggamer/SSK2/blob/master/ssk2/extras/meters.lua

memmeter.jpg

  1. Put it in the same folder as main.lua

  2. In main.lua do this:

    local meters = require “meters” meters.create_mem( True, 1 ) – Drag-able = True; Scale = 1

This will create a floating meter showing main and video mem usage.  It is draggable, so while it will stay on top of all other content, if it gets in the way, you can drag it elsewhere.

Now run your game and see what your memory usage looks like over time.  i.e. Take note of values as you go scene to scene and see if it grows and grows.

"

local composer = require( “composer” )
local scene = composer.newScene()

– for testing, report to console, how much memory in kilobytes is being used
– at this point in the game play life cycle
– turn on and off in the main.lua file
if ( myReportLuaMemoryUsage == true ) then
    local garbageMemoryUsage = collectgarbage(“count”) --returns total memory in use (in kilobytes).
 print("lua memory in use: " … garbageMemoryUsage)
 garbageMemoryUsage = nil – empty variable after use.
end


– Code outside of the scene event functions below will only be executed ONCE unless
– the scene is removed entirely (not recycled) via “composer.removeScene()”


– Save currentSceneName to device memory for game play recal and map functionality
local json = require( “json” ) – load in the JSON library so we can store data to device memory
local filePath = system.pathForFile( “playerCurrentSceneName.json”, system.DocumentsDirectory ) – create an absolute path to file to store name in the systems documents directory
local currentSceneName = composer.getSceneName( “current” ) – set a local variable to hold the name of this scene.

local groupBackground – display group for the background objects
local groupMain – display group for the main/foreground objects
local groupUserInterface – display group for the user interface controls

– Create all variables this scenes needs to use here, so we can destroy at scene end.
local backgroundImage
local btnBackPackClosed
local btnHome
local hitPath

– Create this scenes goto next scene functions
local function goto_GP6() composer.gotoScene( “GP6-ValleyPath”, { time=1000, effect=“crossFade” } ) end


– Scene event functions


– create()
function scene:create( event )

 local sceneGroup = self.view
 
 – Code here runs when the scene is first created but has not yet appeared on screen

    – Set up display groups
    groupBackground = display.newGroup()  – Display group for the background image
    sceneGroup:insert( groupBackground )  – Insert into the scene’s view group
    groupMain = display.newGroup()  – Display group for the ship, asteroids, lasers, etc.
    sceneGroup:insert( groupMain )  – Insert into the scene’s view group
    groupUserInterface = display.newGroup()    – Display group for UI objects like the score
 sceneGroup:insert( groupUserInterface )    – Insert into the scene’s view group
 
 – Set up the background image
 backgroundImage = display.newImageRect( groupBackground, “images-background/GP1.jpg”, 1024, 768 )
    backgroundImage.x = display.contentCenterX
 backgroundImage.y = display.contentCenterY

 – create btnBackPackClosed button
 btnBackPackClosed = display.newImageRect( groupUserInterface, “images-ui/backpack-closed.png”, 100, 100 )
    btnBackPackClosed.x = myLeftEdge
 btnBackPackClosed.y = myBottomEdge - 50
 btnBackPackClosed.isVisible = true

 – create btnHome button
 btnHome = display.newImageRect( groupUserInterface, “images-ui/home.png”, 60, 60 )
    btnHome.x = myRightEdge + 50
 btnHome.y = myBottomEdge - 30
 btnHome.isVisible = true

 – Set up the invisable hit area for the path
 hitPath = display.newCircle (groupUserInterface, display.contentCenterX - 50, display.contentCenterY + 30, 40)
 hitPath:setFillColor( 1, 0, 0, myAlpha ) – turn 0.5 alpha to 0 to make hit button invisable
 hitPath.isHitTestable = true – if false object wont detect hit’s when it’s invisable

 
end

– show()
function scene:show( event )

 local sceneGroup = self.view
 local phase = event.phase

 if ( phase == “will” ) then
  – Code here runs when the scene is still off screen (but is about to come on screen)
 
        – save the currentSceneName to device long-term memory
        local encodedCurrentSceneName = json.encode( currentSceneName )
        print( "Player is currently located in scene: " … encodedCurrentSceneName )
        encodedCurrentSceneName = nil

  – control show or hide of inventory btnBackPackClosed image
        – can not be a local function, as called from overlay OL1-InventoryBag
        – code placed here is this event WILL SHOW code, just before it regains focus.
  function scene:showBackPackClosed() btnBackPackClosed.isVisible = true end
  function scene:hideBackPackClosed() btnBackPackClosed.isVisible = false end
  function scene:showUI() groupUserInterface.isVisible = true end
  function scene:hideUI() groupUserInterface.isVisible = false end
       
 elseif ( phase == “did” ) then
  – Code here runs when the scene is entirely on screen

  – Add a ‘tap’ event listener to all hit objects
  hitPath:addEventListener( “tap”, goto_GP6 )
  
        btnBackPackClosed:addEventListener( “tap”, goto_myOpenInventory )
  btnHome:addEventListener( “tap”, goto_myHomeMainMenu )

 end
end

– hide()
function scene:hide( event )

 local sceneGroup = self.view
 local phase = event.phase

 if ( phase == “will” ) then
  – Code here runs when the scene is on screen (but is about to go off screen)
       
–        groupUserInterface.isVisible = false       

 elseif ( phase == “did” ) then
  – Code here runs immediately after the scene goes entirely off screen

 end
end

– destroy()
function scene:destroy( event )

 local sceneGroup = self.view
 – Code here runs prior to the removal of scene’s view

 – Remove objects/images and empty variables
 backgroundImage:removeSelf()
 backgroundImage = nil

 btnBackPackClosed: removeSelf()
 btnBackPackClosed = nil
    btnHome: removeSelf()
 btnHome = nil
 
    hitPath:removeSelf()
 hitPath = nil
 
 groupBackground:removeSelf()
 groupBackground = nil
 groupMain:removeSelf()
 groupMain = nil
 groupUserInterface:removeSelf()
 groupUserInterface = nil

 currentSceneName = nil

 collectgarbage(“collect”)

end


– Scene event function listeners


scene:addEventListener( “create”, scene )
scene:addEventListener( “show”, scene )
scene:addEventListener( “hide”, scene )
scene:addEventListener( “destroy”, scene )


return scene

"

Hi Rob,
 

Hi Rob,

Having read your two points.

I’ve been mindful not to create global variables. I know I have done, but only two to help place objects due to needing to otherwise calculate the same maths for positioning on each scene.

Point 1. objects into scene groups. 
This may well be part of the problem.
I’m a bit lost on the self.view group.
I’m using your template.

When in the CREATE event for the scene.  I use:
   

    – Set up display groups
    groupBackground = display.newGroup()  – Display group for the background image
    sceneGroup:insert( groupBackground )  – Insert into the scene’s view group
    groupMain = display.newGroup()  – Display group for the ship, asteroids, lasers, etc.
    sceneGroup:insert( groupMain )  – Insert into the scene’s view group
    groupUserInterface = display.newGroup()    – Display group for UI objects like the score
   sceneGroup:insert( groupUserInterface )    – Insert into the scene’s view group
 

So when i create my objects, they are being put into one of my three groups. So nested.

Thanks

Angela

Also… I think it’s to do with mutliple versions of the same variable name being used.

But this is where I am lost as I have created my variables “local” to each scene.

EG…

– show()
function scene:show( event )

 local sceneGroup = self.view
 local phase = event.phase

 if ( phase == “will” ) then
  – Code here runs when the scene is still off screen (but is about to come on screen)
 
        – save the currentSceneName to device long-term memory
        local encodedCurrentSceneName = json.encode( currentSceneName )
        print( "Player is currently located in scene: " … encodedCurrentSceneName )
        encodedCurrentSceneName = nil

This code simply reports the name of the scene I am in to the console, but it’s not being listed once, with every new scene I go to the count of the number of lines the name is writing. Increases. Like the variable is being created on each scene load. Adding to many existig in memory with the same name, but all holding teh same data.

Why are they not being collected by the garbage collector?

Thanks

Angela

Hi Thanks for replying.

I couldn’t see the code button so hope it shows ok.

See above and thanks for your help.

Ange

Thanks - sounds just what I need, did as you said but keeps reporting runtime errors on meters.lua:255 attempt to index field ‘ssk’ (a nil value). I’m to green at Lua to figure this out just yet!!!

:frowning:

As others have already said, it simply seems like you have a substantial memory leak. You can also try this simple memory output function. This’ll output it to your console only, however, but it is easy enough to turn into a display object as well.

local function checkMemory() collectgarbage( "collect" ) local memUsage\_str = string.format( "MEMORY = %.3f KB", collectgarbage( "count" ) ) print( memUsage\_str, "TEXTURE = "..(system.getInfo("textureMemoryUsed") / (1024 \* 1024) ) ) end timer.performWithDelay( 500, checkMemory, 0 )

It looks like you’re doing things right. The same variable name thing impacts global variables. When you declare them local in the scene, they are unique to that scene, so the overwriting problem isn’t likely in this case. 

Also, please use code formatting when publishing code. Click on the blue <> button in the row with Bold, Italic, etc and paste your code into the popup.

Rob

Thanks for your help so far Rob.

I still have the problem, but one thing I have also noticed is that when I use an overlay (to show the player their back pack / inventory of items and objects collected) on top of a scene.

I have used this tutorials structure:
https://docs.coronalabs.com/api/library/composer/hideOverlay.html

With my version of this in the “parent” scene:
 

-- Custom function for resuming the game (from pause state) function scene:resumeGame()

and then my version of this:
 

&nbsp;parent:resumeGame()

in the overlay scene.

I think one of the reasons of the repeating and increasing memory is that all my scenes have the same

function scene:resumeGame()

So for every scene I visit, this is being created.

Then when called, each instance is then being run.

I tried putting local in front of the function, but this broke my code!
 

Suggestions of fixing this.

I don’t think it will totally solve the memory leakage but might be playing a part .

Thanks one more.

Angela

 

function someobject:somefunction() associates the function with that object. It’s unique to the object. In each scene or overlay the scene object is unique to that scene, so doing that would not cause memory leaks.

Rob

Just remove this line from meters.lua (at the bottom)

\_G.ssk.meters = public &nbsp;

Try adding:

collectgarbage()

at the show event of each scene to force the garbage collector to run a full cycle.

Hiya ammar71.

I’ve just forced this line of code at the end of the SCENE HIDE DID event.
It’s reduced some of my problem, but not all.

Can I ask your reasoning behind placing it in the show… so I understand the logic and not just follow your instruction.
Thanks in advance for any assistance.

Carbage collection checks for any objects that are still in memory but have no variables refering to them. This process should happen automatically every now and then. collectgarbage() forces it to happen at a given time.

You can call it at Scene Hide or Show if you like. You can even call it from within the scene as many times as you like, but it does take time to check the memory, so it is better not to call it too often. Once at the beginning/end of a scene is OK. Calling it at the begining of a scene removes carbage from previous scenes. Calling it at the end of a scene removes carbage from this scene (and previous scenes if any).

If you use global variables then remember that garbage collection only happens if the variable is set to nil. So global variables need to be set to nil if the object they point to is no longer needed.