Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

Game Memory, Scene recycling and scene destroy
Started by AngelaMcCall Yesterday, 07:09 AM

14 replies to this topic
[TOPIC CONTROLS]
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

AngelaMcCall

[GLOBAL: userInfoPane.html]
AngelaMcCall
  • Observer

  • 10 posts
  • Corona SDK

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

 

 

 



[TOPIC: post.html]
#2

AngelaMcCall

[GLOBAL: userInfoPane.html]
AngelaMcCall
  • Observer

  • 10 posts
  • Corona SDK

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??



[TOPIC: post.html]
#3

AngelaMcCall

[GLOBAL: userInfoPane.html]
AngelaMcCall
  • Observer

  • 10 posts
  • Corona SDK

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



[TOPIC: post.html]
#4

sporkfin

[GLOBAL: userInfoPane.html]
sporkfin
  • Contributor

  • 619 posts
  • Corona SDK

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.



[TOPIC: post.html]
#5

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 26,300 posts
  • Enterprise

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



[TOPIC: post.html]
#6

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,593 posts
  • Corona SDK

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.


  • sporkfin likes this

[TOPIC: post.html]
#7

AngelaMcCall

[GLOBAL: userInfoPane.html]
AngelaMcCall
  • Observer

  • 10 posts
  • Corona SDK

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.

"

 


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

"



[TOPIC: post.html]
#8

AngelaMcCall

[GLOBAL: userInfoPane.html]
AngelaMcCall
  • Observer

  • 10 posts
  • Corona SDK

Hi Rob,
 

 

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

 

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



[TOPIC: post.html]
#9

AngelaMcCall

[GLOBAL: userInfoPane.html]
AngelaMcCall
  • Observer

  • 10 posts
  • Corona SDK

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



[TOPIC: post.html]
#10

AngelaMcCall

[GLOBAL: userInfoPane.html]
AngelaMcCall
  • Observer

  • 10 posts
  • Corona SDK

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.

 

Hi Thanks for replying.

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

See above and thanks for your help.

Ange



[TOPIC: post.html]
#11

AngelaMcCall

[GLOBAL: userInfoPane.html]
AngelaMcCall
  • Observer

  • 10 posts
  • Corona SDK

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.

 

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!!!

:-(



[TOPIC: post.html]
#12

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 981 posts
  • Corona SDK

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 )


[TOPIC: post.html]
#13

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 26,300 posts
  • Enterprise

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



[TOPIC: post.html]
#14

AngelaMcCall

[GLOBAL: userInfoPane.html]
AngelaMcCall
  • Observer

  • 10 posts
  • Corona SDK

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:
 

 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

 



[TOPIC: post.html]
#15

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 26,300 posts
  • Enterprise

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




[topic_controls]
[/topic_controls]