Preload game assets

So I’ve already looked at quite a few posts on this already, but none actually answer my question so here I am again with another question! Currently my level is facing slight lag when loading in its assets at the beginning of the level. Enough lag in fact that it makes it un-playable. Obstacles randomly spawn off screen, move on screen, then move off screen again on the other side of the device. The lag occurs when one of those objects spawn off screen. So I am trying to figure out a way to preload the assets and still have the game function the way it should.

When the obstacles move off screen after the player has made it through that image gets removed and nil’ed so I can free up memory for the others. So I can’t simply put the obstacle images in scene:create and use a loadScene() command. Should I use a global in my myData.lua and store the information in its variable? The issue that I can’t grasp is to be able to call it, remove it, and call it again without sacrificing game memory every time it loads in. Any thoughts?

Here is an example of one of my obstacle functions. There is 15 total right now, looking to add more.

local opMove2 = function() local op2 = display.newImageRect(sceneGroup, "levels/normalMode/level1/obstacle2.png",478,450) physics.addBody(op2, "kinematic", physicsData:get("obstacle2") ) op2.x = 1000 op2.y = 200 op2.type = "obstacle" local function op2Remove(target) physics.removeBody(target) target:removeSelf() target = nil end transition.to(op2,{time = 10000,x=(W-2000), onComplete = op2Remove, tag = "transTag"}) end

Thanks for reading

EDIT: Just to add to this, I have put the display.newImageRect and its parameters in the scene:create and did test it out. Images only run once, get deleted, and then an error occurs because the transition is trying to move something that is nil’ed and has no value.

Creating/removing game objects and adding/removing physics is very expensive, so I found the best way to handle images is to:

Create as many, if not all, during scene:create().  I create them and then isVisible = false them.

I add event listeners and physics either in scene:show() or in another init function.  For physics, I used isBodyActive to temporarily halt (but not remove) physics from these objects.

At this point, I do not add/remove additional objects or physics during the lifetime of this scene.

For any object needed on the screen, use isBodyActive = true and isVisible = true.

For any object that is no longer needed, or needed later, use isBodyActive = false and isVisible = false.

isBodyActive is great because that game object is removed from the  physics simulation, but without the expense of add/removing physics on the object.

I see this question come up quite a bit, and was planning on writing a blog on it. If anyone is interested in having me push this up on my “To Do” list, please let me know.

Hope this helps.

That is awesome, thanks for letting me know about that. I think you should write it because it would help out a lot of people. And when the objects go off screen and that object is called again it doesn’t take up additional memory? Because I randomly generate through 15 obstacles right now using math.random. I’ll be pushing up to 30 for level 1, so memory consumption is really important.

It doesn’t take up additional memory since you are creating all objects before the game starts.  The memory is allocated then.  You just reuse them by changing invisibility and moving the object to a new position before making visible again.

You basically create enough objects you need to be on the screen at any one time.  So if you know you will need 30 max, then create 30 up front.  Just note that even if there is only 1 object on the screen, the other 29 are taking up memory, but this is better than creating/removing objects throughout the lifetime of the game.

It’s raining here, so I think I will take this opportunity to work on this blog post that I have been meaning to write for some time.  Hope to get something out very soon, and will update here.

thanks,

–John

Yeah that’s a very good point. I’ll give it a try in a little and let you know how it goes. Finishing up homework before I started programming again. Looking forward t reading your blog!

This thread has motivated me to finally finish an article I started a while ago.  Finally published here.

Thanks!

Thank you! I just read the post. It’s awesome and helps a lot! If I run into any issues I’ll post in here again. In the future will you be adding to the tutorial? Like an advanced one? I saw that you talked about Sprite swapping so seeing one where you handle characters would be amazing. There is no good character selection tutorials out there dealing with pools like you have. Would be awesome to see one. Thanks for your help, I really appreciate it.

Sure!  Thanks for reading it and for the motivation. I had this half-written article on the back-burner for a while.

I did think about a follow-up article… probably will happen at some point in the near future.

Glad I could help.

Thanks!

–John

You’re welcome! So I tried it out and I am running into an issue. So lets say obstacle 1 spawns and before that gets to the x coordinate I need it to be at to set eveeything to false, another spawns due to random generation. When The first object finally reaches that point, it sets isVisible to false for both even though the second object is still in the middle of gameplay. Is there a way I can restrict the random generation from choosing the same number until the image visible is false? So lets say:

if random == 1 then opMove1()

  if op1.isVisible == true then

    (Restrict from generating 1 until isVisible is to false)

  end

end

I’m not fully sure I understand, but let me give it a shot.

If you want to prevent the random generator from using a number, you can do it a few ways.  Best way depends on your game situation:

  1. You can write a loop to let math.random return a number and then check to see if it is allowed.  This is ok for a few instances, but might become a problem if you want to exclude a lot of numbers.
  2. Change math.random() to return values outside of the excluded numbers.
  3. Create a temporary table of valid numbers, and return a random number from 1 to length of table, then use the value at that index.  Ex: myValue = TempIntTable[math.random(1, #TempIntTable)]

I am not sure if/how this relates to your object pooling question, since the pool will never return an object already in use, and the position of the object in the table is nothing you should be worrying about.  If you need to track more than one live object on the screen, you will need to manage that by using multiple variables for each object, or another table of live objects.

In my game Space Mission Survival, I let the physics functions tell me which enemy was hit and return that enemy to the pool.

I have another game with an 8x8 grid of tiles that’s not under control of physics, and I store the reference to those tiles in a table called LiveTiles and I added a few properties (such as isAlive, isMoving, currentRow, currentColumn) to assist me with managing which tiles to move and remove from the screen when needed.

Hope this help.

If I didn’t understand your question, please let me know.

I’ll post the code so you can see what I’m doing. My game is an endless runner so the obsatcles last until the player dies. In addition to that I am using 15 different obstacle images. Every obstacle has its own style and size. Here is the code I am using right now:

-- Located in the scene:create to load in the obstacle. Have 15 different images op1 = display.newImageRect(sceneGroup, "levels/normalMode/level1/obstacle1.png", 491, 450) op1.isVisible = false op1.isBodyActive = false --[[Located in scene:show, methods controling each individual obstacle due to them having their own animations or rotations. Obstacles travel off screen to the x coordinate of screen width-2500 then gets cycled out]]-- local opMove1 = function() physics.addBody(op1, "kinematic", physicsData:get("obstacle1") ) op1.x = 1000 op1.y = 200 op1.isVisible = true op1.isBodyActive = true op1.type = "obstacle" local function op1Remove(target) op1.isVisible = false op1.isBodyActive = false physics.removeBody(target) end transition.to(op1,{time = 12500,x=(W-2500), onComplete = op1Remove, tag = "transTag"}) end -- The random selection and generation of the obsstacles local function ranGen() local obs = {1, 2, 3, 4, 5, 6} local index = math.random(#obs) local obstacle = obs[index] if index == 1 then opMove1() if op1.isVisible == true then table.remove(obs, 1) elseif op1.x == (W-2500) then table.insert(obs, 1, 1) end elseif index == 2 then opMove2() if op2.isVisible == true then table.remove(obs, 2) elseif op2.x == (W-2500) then table.insert(obs, 2, 2) end elseif index == 3 then opMove3() if op3.isVisible == true then table.remove(obs, 3) elseif op3.x == (W-2500) then table.insert(obs, 3, 3) end elseif index == 4 then opMove4() if op4.isVisible == true then table.remove(obs, 4) elseif op4.x == (W-2500) then table.insert(obs, 4, 4) end elseif index == 5 then opMove5() if op5.isVisible == true then table.remove(obs, 5) elseif op5.x == (W-2500) then table.insert(obs, 5, 5) end elseif index == 6 then opMove6() if op6.isVisible == true then table.remove(obs, 6) elseif op6.x == (W-2500) then table.insert(obs, 6, 6) end else end end objTimer = timer.performWithDelay(2500, ranGen, -1)

So what’s happening is math.random chooses a number, continues to go through iteration, but sometimes calls lets say 3, then 3 again right afterwards. This makes obstacle 3 vanish and restart at the beginning before it reaches the point of where isVisible = false and isBodyActive = false. Multiple obstacles are selected before one another goes off screen. At one given time 3 obstacles could be on screen, so I am trying to restrict it from choosing the same number to avoid it calling a method twice if that method/obsatcle is still on screen.

Can something like this be done using your pooling method if I were to make a random selection of the images and use a get.width and get.height for the display.newImageRect()? Something like:

local obs = {{filename = "normalMode/level1/obstacle5", width = 598, height = 450, tag = "obstacle5"}, {filename = "normalMode/level1/obstacle4", width = 562, height = 450, tag = "obstacle4"}, {filename = "normalMode/level1/obstacle6", width = 379, height = 396, tag = "obstacle6"}, {filename = "normalMode/level1/obstacle2", width = 478, height = 450, tag = "obstacle2"}, {filename = "normalMode/level1/obstacle1", width = 491, height = 450, tag = "obstacle1"}} local index = math.random(#obs) local obstacle = obs[index] local obstacles = 15 local obj = {} for i = 1, obstacles do obj[i] = display.newImageRect(sceneGroup, obstacle.filename..".png", obstacle.width, obstacle.height); obj[i].isVisible = false obj[i].isBodyActive = false end

Using this method doesn’t allow me to preload the images using composer.loadScene(), so I’d have to use display.newImage to get the width and heigh of the image then call it into that method. Thanks for your help, I appreciate it!!!

Pooling just allows you to preload images/physics so you don’t have to create/recreate them during game play.  Everything else should be handled exactly as before.

Looking at your code, I would do this (note that I didn’t run or test this so it might need a bit of tweaking):

In function randGen, Line 28, determine what valid numbers can be called:

[lua]

local obs = {}

if ob1.isVisible then obs[#obs + 1] = 1

if ob2.isVisible then obs[#obs + 1] = 2

if ob3.isVisible then obs[#obs + 1] = 3

if ob4.isVisible then obs[#obs + 1] = 4

if ob5.isVisible then obs[#obs + 1] = 5

if ob6.isVisible then obs[#obs + 1] = 6

local index = math.random(#obs)

local obsticle = obs[index]

[/lua]

…so, in your example, if “3” was returned, the next time obs would be {1, 2, 4, 5, 6}, so “3” cannot be returned until that object is off screen.

–john

   

Here is what I have so far:

local function ranGen() local obs = {} if op1.isVisible == false then obs[#obs + 1] = 1 elseif op2.isVisible == false then obs[#obs + 1] = 2 elseif op3.isVisible == false then obs[#obs + 1] = 3 elseif op4.isVisible == false then obs[#obs + 1] = 4 elseif op5.isVisible == false then obs[#obs + 1] = 5 elseif op6.isVisible == false then obs[#obs + 1] = 6 end --local random = obs[math.random(#obs)]; local index = math.random(#obs) local obstacle = obs[index] if obstacle == 1 then opMove1() if op1.isVisible == true then table.remove(obs, 1) end end if obstacle == 2 then opMove2() if op2.isVisible == true then table.remove(obs, 2) end end if obstacle == 3 then opMove3() if op3.isVisible == true then table.remove(obs, 3) end end if obstacle == 4 then opMove4() if op4.isVisible == true then table.remove(obs, 4) end end if obstacle == 5 then opMove5() if op5.isVisible == true then table.remove(obs, 5) end end if obstacle == 6 then opMove6() if op6.isVisible == true then table.remove(obs, 6) end end end

This will iterate through the obstacles exactly like you said, but its not random. It goes through the iteration of them so 1-6. I’m not going to complain about that though considering I’ll have 30 or more obsatcles so the player may never even see the repeat of number iterations. Your help has been absolutely amazing, truly. You definely helped me get through a hurdle. I am beyond grateful. Do you see the change you’d make to make it random again?

I think the problem is the if statement at line 4… You want to build a table of available obstacle numbers and this won’t do it. It will only create a table with one entry. I think if you remove the else if and use multiple ifs for each obstacle you will have a table of obstacles that’s are not visible that the random function can then use.

Just got it working, thanks! I just added your website to my favorites as well. I can’t even tell you how grateful I am to you. With the lag eliminated from the levels I can now move forward with adding character select and options, so this was beyond a huge help. Thank you so much. With corona now free for everyone, I hope to see more developers like you and one day be as good at dealing with tables and arrays as you are. That was a huge weaknes of mine in Advanced Programming even if I pulled a great grade. Again, thank you.

Glad to help!  It was fun and got me off my butt to finish my article as well.  :slight_smile:

Creating/removing game objects and adding/removing physics is very expensive, so I found the best way to handle images is to:

Create as many, if not all, during scene:create().  I create them and then isVisible = false them.

I add event listeners and physics either in scene:show() or in another init function.  For physics, I used isBodyActive to temporarily halt (but not remove) physics from these objects.

At this point, I do not add/remove additional objects or physics during the lifetime of this scene.

For any object needed on the screen, use isBodyActive = true and isVisible = true.

For any object that is no longer needed, or needed later, use isBodyActive = false and isVisible = false.

isBodyActive is great because that game object is removed from the  physics simulation, but without the expense of add/removing physics on the object.

I see this question come up quite a bit, and was planning on writing a blog on it. If anyone is interested in having me push this up on my “To Do” list, please let me know.

Hope this helps.

That is awesome, thanks for letting me know about that. I think you should write it because it would help out a lot of people. And when the objects go off screen and that object is called again it doesn’t take up additional memory? Because I randomly generate through 15 obstacles right now using math.random. I’ll be pushing up to 30 for level 1, so memory consumption is really important.

It doesn’t take up additional memory since you are creating all objects before the game starts.  The memory is allocated then.  You just reuse them by changing invisibility and moving the object to a new position before making visible again.

You basically create enough objects you need to be on the screen at any one time.  So if you know you will need 30 max, then create 30 up front.  Just note that even if there is only 1 object on the screen, the other 29 are taking up memory, but this is better than creating/removing objects throughout the lifetime of the game.

It’s raining here, so I think I will take this opportunity to work on this blog post that I have been meaning to write for some time.  Hope to get something out very soon, and will update here.

thanks,

–John

Yeah that’s a very good point. I’ll give it a try in a little and let you know how it goes. Finishing up homework before I started programming again. Looking forward t reading your blog!