[Tips] Optimization 101

46 replies [Last post]
Danny
User offline. Last seen 2 years 26 weeks ago. Offline
Joined: 17 Aug 2011

http://developer.anscamobile.com/content/performance-and-optimization

Hey guys, just want to share some optimization techniques here with you. I will update this list when I can or add any techniques you guys may suggest.

1) Localization : Localizing Lua functions provides a decent > massive speed difference. How do you do this ? Like so :

The slower way (Bad)

1
local myRand = math.random(1, 10)

The faster way (Good)

1
2
3
local mRand = math.random
 
local myRand = mRand(1, 10)

Other examples of this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
local mRandSeed = math.randomseed
 
--Call it
mRandSeed(os.time())
 
local sFormat = string.format
 
--Call it
sFormat("%02d", myVar)
 
local tRemove = table.remove
 
--Call it
tRemove(myTable)

2) Avoid moving objects manually. Moving objects by incrementing it's x,y values each frame is slower than using translate.

Example :

The bad way

1
2
obj.x = obj.x + 1
obj.y = obj.y + 1

The good way

1
obj:translate(1, 1)

You can also use translate even if you only need to just increase the x or y and not both as shown below :

1
2
3
4
5
--Only increment the x value
obj:translate(1, 0)
 
--Only increment the y value
obj:translate(0, 1)

3) Avoid heavy string comparison. If you are doing a lot of string comparison checks like below :

1
2
3
local myString = "none"
 
if myString == "hi" or myString == "do" or myString == "lol" then

It is faster to use a table of enums. That way you can still know the values but you are comparing numbers which is much faster than comparing strings.

1
2
3
4
5
6
7
8
9
local myString = 1
 
local enumTable = {
    ["hi"] = 1,
    ["do"] = 2,
    ["lol"] = 3,
}
 
if myString == enumTable["hi"] or myString == enumTable["do"] or myString == enumTable["lol"] then

4) For loops. Certain types of for loop execute faster than others.

Fastest :

1
2
3
for i = 1, #myTable do
--
end

Second fastest

1
2
3
4
local fpairs = ipairs
 
for i, v in fpairs(myTable) do
end

The difference between the two is roughly 120ms.

5) Make use of tables to cut down on local variable use and organise your code.

Bad practice for a group of data.

1
2
3
4
5
local mySheet1 = sprite.newSpriteSheetFromData("mySheet1.png", require("mySheet1").getSpriteSheetData())
 
local mySheet2 = sprite.newSpriteSheetFromData("mySheet2.png", require("mySheet2").getSpriteSheetData())
 
local mySheet3 = sprite.newSpriteSheetFromData("mySheet3.png", require("mySheet3").getSpriteSheetData())

Better practice for group of data.

1
2
3
4
5
local mySheets = {
    ["Sheet1"] = sprite.newSpriteSheetFromData("mySheet1.png", require("mySheet1").getSpriteSheetData())
    ["Sheet2"] = sprite.newSpriteSheetFromData("mySheet2.png", require("mySheet2").getSpriteSheetData())
    ["Sheet3"] = sprite.newSpriteSheetFromData("mySheet3.png", require("mySheet3").getSpriteSheetData())
}

To give an advantage of the benefits of this, despite the obvious ones of memory savings and organized blocks of code, take this scenario.

Attempting to dispose of the 3 sprite sheets and nil the variables using method 1 (the bad practice)

1
2
3
4
5
6
7
mySheet1:dispose()
mySheet2:dispose()
mySheet3:dispose()
 
mySheet1 = nil
mySheet2 = nil
mySheet3 = nil

Now disposing of the sprite sheets and nil the table. (the better practice)

1
2
3
4
5
6
7
8
local tRemove = table.remove
 
for i, v in pairs(mySheets)  do
    mySheets[i]:dispose()
    mySheets[i] = nil
end
 
tRemove(mySheets)

See how much easier that is to manage and remove? Thats one of the other countless benefits of using tables.

Note: Overuse of tables will consume more lua memory. So use appropriately.

6) Functions : Use less of them.

Say for instance you have 4 buttons on screen that each have a tap event. You don't need four functions (one for each), just the one will suffice.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
--Table to store the buttons
local myButtons = {}
 
myButtons["Pause"] = display.newImage("pause.png")
myButtons["Pause"].myId = "Pause"  --Set the buttons id
 
myButtons["Shoot"] = display.newImage("shoot.png")
myButtons["Shoot"].myId = "Shoot" --Set the buttons id
 
myButtons["Move"] = display.newImage("move.png")
myButtons["Move"].myId = "Move" --Set the buttons id
 
myButtons["Retry"] = display.newImage("retry.png")
myButtons["Retry"].myId = "Retry" --Set the buttons id
 
--Function to handle our buttons
local function handleButtons(event)
     local target = event.target
 
     --Handle action for each different button
     if target.myId == "Pause" then
         --Pause
     elseif target.myId == "Shoot" then
         --Shoot
     elseif target.myId == "Move" then
         --Move
     elseif target.myId == "Retry" then
         ---Retry
     end
      
     return true
end
 
--Add event listeners for all the buttons
for i = 1, #myButtons do
    myButtons[i]:addEventListener("tap", handleButtons)
end

See one function that handles all your buttons. This saves using 4 separate functions for something that can be achieved in one :)

7) Updating Objects : Only do it when you have to.

Say for instance you are creating a space shooter and have a text object on screen to display your current score. You obviously want to update it to reflect the current score, but you don't need to do this every frame.

The bad way

1
2
3
4
5
6
local function updateScore(event)
    score = score + 1
    score.text = "score " .. score
end
 
Runtime:addEventListener("enterFrame", updateScore)

The good way:

1
2
3
4
5
6
--In your collision handler (when enemy gets hit with a bullet you fired
local function onCollision(event)
    --Where Bullet collides with enemy
    score = score + 1
    score.text = "score" .. score
end

That way your only updating an object when you need to, rather than wasting cpu cycles updating something when it doesn't need updating.

8) Creating objects : Things to always remember.

This is covered in greater depth here: http://blog.anscamobile.com/2011/09/how-to-spawn-objects-%E2%80%94-the-right-way/

When you have a function to create an object (or series of objects) you must return the object at the end of the function otherwise you have no way to clear the object from memory. This is also good programming practice.

Bad example:

1
2
3
4
5
6
7
8
9
10
11
local function spawnAGuy(amount)
    local guy = display.newImage("guy.png")
    guy.x = 100
    guy.y = 100
end
 
--Create the guy (note in this case your local reference is only creating a reference the the function not the created "guy" object)
local spawnedGuy = spawnAGuy
 
--Remove it
you cant

Better example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
local function spawnAGuy(group)
    local guy = display.newImage("guy.png")
    guy.x = 100
    guy.y = 100
    
    --Insert the guy into the specified group if it exists (then you can easily clear it from display
    if group then
        group:insert(guy)
    end
    
    return guy
end
 
--Create the guy
local spawnedGuy = spawnAGuy(localGroup)
 
--Remove it
display.remove(spawnedGuy)
spawnedGuy = nil

Best example (use parameters)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
local function spawnAGuy(params)
    local guy = display.newImage("guy.png")
    guy.x = params.x or 100
    guy.y = params.y or 100
    
    --Insert the guy into the specified group if it exists (then you can easily clear it from display
    if params.group then
       prams.group:insert(guy)
    end
    
    return guy
end
 
--Create the guy
local spawnedGuy = spawnAGuy({x = 20, y = 130, group = localGroup})
 
--Remove it
display.remove(spawnedGuy)
spawnedGuy = nil

Using paramater passing is a hugely powerful feature, you can even create a function in the spawning function to destroy/remove the guy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
local function spawnAGuy(params)
    local guy = display.newImage("guy.png")
    guy.x = params.x or 100
    guy.y = params.y or 100
    
    --Insert the guy into the specified group if it exists (then you can easily clear it from display
    if params.group then
       prams.group:insert(guy)
    end
    
    --Create a function to remove the guy
    function guy:destroy()
        display.remove(self)
        self = nil
    end
    
    return guy
end
 
--Create the guy
local spawnedGuy = spawnAGuy({x = 20, y = 130, group = localGroup})
 
--Remove it
spawnedGuy:destroy()

--------------

More coming soon!, enjoy!

Replies

PixelEnvision
User offline. Last seen 47 weeks 22 hours ago. Offline
Joined: 22 Oct 2010

Nice post, thank you!

Specially #2, I wasn't aware of that till now...

darkconsoles
User offline. Last seen 7 years 6 weeks ago. Offline
Joined: 17 Jan 2011

very cool examples, thanks

Danny
User offline. Last seen 2 years 26 weeks ago. Offline
Joined: 17 Aug 2011

Just added #6, functions. Enjoy :)

naveen_pcs
User offline. Last seen 5 days 22 hours ago. Offline
Joined: 9 Apr 2011

For #2 would it still be better to use translate if your core game loop is inside an enterFrame listener (I currently increment x and y values within enterFrame).

Would it actually be a noticeable performance difference?

Danny
User offline. Last seen 2 years 26 weeks ago. Offline
Joined: 17 Aug 2011

Yeah it would be better to use translate and it would be faster yeah.

don
User offline. Last seen 5 years 1 week ago. Offline
Joined: 24 Jan 2011

I've done tests. #2 works even better by caching the access of x and y. It's the accessing that's expensive not the modification.

My test was to use the uma horse sprite. Display 225 of them and have them loop the animation.
Looping over all the sprites ever frame with this code produced 40-45fps*

1
2
d.x = 0 -- just this access will slow things down significantly
d.y = 0

This however produced 56-60fps*

1
d:translate(0, 0)

In other words, when you construct your display objects:

1
2
3
4
5
6
7
8
9
10
11
local d = display.newGroup()
d.cx = d.x
d.cy = d.y
 
-- .. somewhere modify cx, cy
local dx = d.cx + 1
local dy = d.cy + 1
 
d:translate(dx - d.cx, dx - d.cy)
d.cx = dx
d.cy = dy

Is much faster for large numbers of display objects over x, y, rotation, xScale, yScale. The alpha and isVisible properties however are not affected.

This has been confirmed by yanuar and I. But don't take my word for it, test it yourself.

"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil" -Donald Knuth

* numbers reported testingon 4th gen ipod touch, your will be different if you use a different sprite or device due to fillrate or other device dependent characteristics

jose2
User offline. Last seen 4 years 42 weeks ago. Offline
Joined: 27 Apr 2011

This post need to be sticky !!

EDIT: Ok, sticked !! XD

Puzzle Runner
User offline. Last seen 3 years 14 weeks ago. Offline
Joined: 8 Feb 2011

I do agree with Don. I avoid doing stuff I know is horrible, but beyond that I will only start optimizing if my iPhone 3GS has significant performance issues. (iPhone 3GS is what I consider the "bare minimum" right now).

@Danny I'd be interested to see the performance comparison between using the transition functions versus other possible solutions. (example, trying to transition with timers instead) Also, the information you provided above should be added to the existing optimization document in the docs section so that it remains easily accessible.

jose2
User offline. Last seen 4 years 42 weeks ago. Offline
Joined: 27 Apr 2011

+1 @blasterv , i used several times transition.to in enterframe and i need to get better perfomance.

Danny
User offline. Last seen 2 years 26 weeks ago. Offline
Joined: 17 Aug 2011

@jose2 : yeah i made it a sticky for convenience ;)

@blasterv, sure. Plenty of other things will be covered too. Once i feel the list is extensive enough I will make a blog post about it and add it to the official api optimization listing.

Puzzle Runner
User offline. Last seen 3 years 14 weeks ago. Offline
Joined: 8 Feb 2011

@jose2 I try to keep enterframe fairly empty. Typically I use timers and transition API for Artifical Intelligence (when enemies must take/not take action based on actions of the player)

jose2
User offline. Last seen 4 years 42 weeks ago. Offline
Joined: 27 Apr 2011

@danny, about the 4th. Is more faster user the second or the first ???. Is not clearly explained.

I read somewhere (a blog about lua optimization) about the for..do perfomance vs ipairs and tolds is more faster use for..do if you know the table length, is true ?

jose2
User offline. Last seen 4 years 42 weeks ago. Offline
Joined: 27 Apr 2011

@blasterv yes but i need to use enterframe loop to know some positions of my sprites to add new graphics.

For example, a sprite who needs a "glasses" sprite i do this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for i = eyeeffectsGroup.numChildren,1,-1 do
                
        if (eyeeffectsGroup[i]~= nil) then
                local xo = eyeeffectsGroup[i].padre.x
                local yo = eyeeffectsGroup[i].padre.y   
                local degrees = eyeeffectsGroup[i].padre.rotation
                local radians = ((degrees-90)*math.pi)/180
                eyeeffectsGroup[i].x = xo+15*math.cos(radians)
                eyeeffectsGroup[i].y = yo+15*math.sin(radians)
                eyeeffectsGroup[i].rotation = eyeeffectsGroup[i].padre.rotation
 
        end
end     
                        

I put the glasses sprite in the eyeeffects display group and call the previous "padre" (parent) graphic to know the exactly x, y from parent and made the rotation. It's ok?

Naomi
User offline. Last seen 2 years 34 weeks ago. Offline
Joined: 6 Jun 2011

This is awesome! Thank you, Danny!

Please keep more tips coming!

Naomi

Naomi
User offline. Last seen 2 years 34 weeks ago. Offline
Joined: 6 Jun 2011

[deleting the double post]

Danny
User offline. Last seen 2 years 26 weeks ago. Offline
Joined: 17 Aug 2011

@jose2: I stated above the code snippets. The first one is the fastest :)

jose2
User offline. Last seen 4 years 42 weeks ago. Offline
Joined: 27 Apr 2011

Thanks @danny !!

So, for example in the code in #13 what's the best way to optimize this ??

This code execute on enterframe

don
User offline. Last seen 5 years 1 week ago. Offline
Joined: 24 Jan 2011

@Danny

What do you think about creating a series of tests so we can measure this stuff and have everyone report back? I created 65 tests just to figure out what's slowing stuff down. It would be cool if there was a place like on github to share with everyone and collate results so that we're not all coding in the dark.

Here are my findings based on those tests. Stuff that kills sprite performance.
1. fill rate! large graphics that need to be updated use smaller sprites (scaling up affects fill rate, scaling down improves perfromance)
2. resorting of sprites (create / destroy, toBack, toFront) do this a lot and it will slow things down a lot
3. accessing x, y, rotation, xScale, yScale is very slow (use translate, rotate and scale and cache the properties for update, see my earlier post for an example)
4. avoid destroying your sprites, instead make them invisible (isVisible = false or alpha = 0) Toggling sprite visibility has some overhead, but may really improve performance.
5. if your sprites are hidden, pause their playback if you can. (you can have 10k hidden sprites not playing and get 60fps, but only 400 hidden sprites playing at 60fps)
6. if the 300 uma sprites are visible when in the viewport, what is performance like when they are out of the view port are they culled? performance improves by about 10 fps when they are offscreen (would doing manual culling help? needs to tested)

All my sprite tests were done with the uma sprite sheet tested on 4th gen ipod touch.

I assume this will extend to any display object, but I haven't confirmed this since my entire game is using only sprites.

Further useful test should be done with physics, timers, variable scoping (globals are slow, but how local is local? what if you access a variable local to a file from inside a function?)

jose2
User offline. Last seen 4 years 42 weeks ago. Offline
Joined: 27 Apr 2011

I made a mistake. I don't use transition.to in enterframe but i have several loops using it:

Normally in my game swaps around 40 sprites using transition.to loops:

1
2
3
4
5
6
7
8
9
10
11
12
local a1
local a2
 
function a1()
  transition.to (ball, {xScale =0.9, yScale=1.1, onComplete=a2, time=200})
end
 
function a2()
  transition.to (ball, {xScale =1.1, yScale=0.9, onComplete=a1, time=200})
end
 
a1()

I do this to achieve a bounce effect in balls and every ball loops it.

A better way to optimize this ?

@RSCdev
User offline. Last seen 6 years 10 weeks ago. Offline
Joined: 6 Sep 2011

Hello Danny,

Would like to say just a big THANK YOU!

Those TIPS are just A.w.e.s.o.m.E! :)

PS: Tip #2 and #6 are wonderful.

Please, let it come even more. haha

Cheers,
Rodrigo.

don
User offline. Last seen 5 years 1 week ago. Offline
Joined: 24 Jan 2011
@RSCdev
User offline. Last seen 6 years 10 weeks ago. Offline
Joined: 6 Sep 2011

I agree, sure it should be at the TOP!

+1.

Danny
User offline. Last seen 2 years 26 weeks ago. Offline
Joined: 17 Aug 2011

@Don, @Rodrigo: Your wish is my command :)

@RSCdev
User offline. Last seen 6 years 10 weeks ago. Offline
Joined: 6 Sep 2011

wOw!

@Danny, very kind of you doing so as it can be even more helpful (besides your own Tips) for many others Corona Members as well as it`s for myself! :)

Thanks!

Cheers,
Rodrigo.
Twitter: @RSCdev

Naomi
User offline. Last seen 2 years 34 weeks ago. Offline
Joined: 6 Jun 2011

I'd like to know what might be the best practice to load and play short sound clips.

I have almost two dozen sound files. Majority of them are 8KB in size. Only three of them are 12KB and 16KB. Under the audio.loadSound() section of the audio API, it says You should use this to load all your short sounds, especially ones you may play frequently. For best results, load all the sounds at the launch of your app or the start of a new level.

So I do the following, but I'm not entirely sure if this is the right way to go:

My main.lua

1
2
3
4
5
6
7
8
-- load all sound effects when game app launches
-- most of these sounds are played often in each game modules
_G.mySound1 = audio.loadSound("mySound1.aac")
_G.mySound2 = audio.loadSound("mySound2.aac")
_G.mySound3 = audio.loadSound("mySound3.aac")
_G.mySound4 = audio.loadSound("mySound4.aac")
 
-- I continue to add more, but you get the idea

In my game module where sounds need to play:

1
2
3
4
5
6
7
8
9
10
11
-- localize  globally loaded sound
-- I include ones that are played in this module
-- in other words, I don't localize all globally loaded sound in each module
local mySound2 = _G.mySound2
local mySound3 = _G.mySound3
local mySound4 = _G.mySound4
 
--when I need to play the sound
audio.play(mySound2)
audio.play(mySound3)
audio.play(mySound4)

I localized the globally loaded sounds because I hear localizing is important for performance (and tip #1 says I should too). In addition, I remember reading something about table-look-up impacting performance, and _G is a table, isn't it?

So, instead of doing audio.play(_G.mySound1), I do audio.play(mySound1) by localizing the globally loaded sound. By doing this, I don't need to load & dispose and then load the same sound again when I play one level after another (or when I reload a same level to play again.)

The thing is, currently, I don't dispose the local variables such as mySound1 when I unload the module even though the audio.loadSound() section of the audio API says: Please note that you are responsible for unloading (cleaning up) any audio files you load with this API. Use the audio.dispose() API to clean up audio handles you are completely done with them and want to unload from memory to get back more RAM.

When I try disposing localized version of the globally loaded sound like audio.dispose(mySound1), it gave me an error, so I decided not to dispose these local variables at all. And, I've been monitoring if there's any memory leak associated with this practice -- but so far, I haven't seen any issue.

However, I'm not entirely sure how I set up my sound load/play is a good practice. Is it better if I load the sound at the start of each level (and not use _G table at all)?

But then, if I don't use the _G table and load/dispose sound at start/end of each game level, I will essentially be unloading a dozen sound files and then reloading the same dozen sound files immediately after disposing them. So this feels wrong too.

Any thoughts? I'd like to hear how others are handling a case like this, and if there's a better approach I should use.

Naomi

Jadynut
User offline. Last seen 3 weeks 6 days ago. Offline
Joined: 7 Jul 2011

Great post! very useful!
I'll apply these tips to the next update of my game.

Thanks

Pixin
User offline. Last seen 1 year 9 weeks ago. Offline
Joined: 21 Feb 2011

Fantastic!! Thank you!

vishwas.zambre
User offline. Last seen 7 years 12 weeks ago. Offline
Joined: 2 Dec 2011

I want to develop application for enterprise level,for instance,a software for hotel in which a customer will navigated through tab(application software) for accessing the facilities available which i'm developing with the help of android which i find flexible for development but this same scenario i want to repeat for iphone i find corona SDK for cross platform development which uses Lua Scripting but its syntax is basically for game(as i saw)and i thing it will lead me to complexity in future while development so which one will be suitable language (which i can use)?

Tom
User offline. Last seen 2 years 27 weeks ago. Offline
Joined: 13 Jul 2010

@Danny,

Some nice tips in your post. There is one issue with tip #6. You can't add event listeners to the table objects like you're doing because you created an associate array instead of an indexed array. #myButtons will not return the number of elements in an associate array. The "for" loop needs to be modified as follows:

1
2
3
4
--Add event listeners for all the buttons
for i,v in pairs(myButtons) do
    myButtons[i]:addEventListener("tap", handleButtons)
end

don
User offline. Last seen 5 years 1 week ago. Offline
Joined: 24 Jan 2011

Performance tip: If you're sure that you're not leaking memory. Disable the garbage collector in critical performance code. Reenable it when you need to reclaim memory.

1
2
3
4
5
6
7
8
9
collectgarbage('stop')  -- prevents the gc from running
 
-- in a level doing stuff
 
-- exiting level 
-- clean up and nil no longer used variables
 
collectgarbage('restart') -- restart the gc
collectgarbage('collect') -- reclaim memory

jeff472
User offline. Last seen 6 years 5 weeks ago. Offline
Joined: 2 Dec 2011

For number 5, couldn't you optimise it using the previous hints too?

1
2
3
4
5
local mySheets = {
    ["Sheet1"] = sprite.newSpriteSheetFromData("mySheet1.png", require("mySheet1").getSpriteSheetData())
    ["Sheet2"] = sprite.newSpriteSheetFromData("mySheet2.png", require("mySheet2").getSpriteSheetData())
    ["Sheet3"] = sprite.newSpriteSheetFromData("mySheet3.png", require("mySheet3").getSpriteSheetData())
}

to:

1
2
3
4
5
6
local newSprite =sprite.newSpriteSheetFromData
local mySheets = {
    ["Sheet1"] = newSprite("mySheet1.png", require("mySheet1").getSpriteSheetData())
    ["Sheet2"] = newSprite("mySheet2.png", require("mySheet2").getSpriteSheetData())
    ["Sheet3"] = newSprite("mySheet3.png", require("mySheet3").getSpriteSheetData())
}

ewing
User offline. Last seen 1 year 46 weeks ago. Offline
Alumni
Joined: 13 Jul 2010

Be careful here. There is a famous quote: "Premature optimization is the root of all evil." (I encourage you all to research it.) Quite often you'll do the wrong thing. Benchmark and verify.

#1) People are too worried about local vs. global. Local is faster, but you'll never know the difference unless you are in a tight inner loop that is being called many times. You may (or may not) also be trading off other things (like memory) in return for speed. There are good reasons to use local variables other than speed, so use them for that. But if global variables make sense, don't be afraid of them.

#2) I think this is correct, though the reasons are mired in detailed. 'Property' accesses are implemented as the ultimate fallback and then we must sort out what to do on the C-side whereas calling a (translate) function avoids this. Batch operations (e.g. setting both x and y in one call) will also be faster since you run fewer instructions.

#3) This is wrong. In Lua, string comparisons are pointer comparisons which are as fast as you can get. In the suggested work-around, you will incur table look ups for each comparison and then number comparisons which will be slower. Not to mention that the table look ups by string key will essentially be doing the exact string comparison behind the scenes you were trying to avoid in the first place. (Ahem, premature optimization is evil.)

#4) A totally different reason to not use ipairs: It has been deprecated in Lua 5.2.

#5) This is more coding style than optimization. They are reasonable suggestions to consider though.

#6) This will really depend on the situation. Sometimes having more functions will be faster. Sometimes it will not.

don
User offline. Last seen 5 years 1 week ago. Offline
Joined: 24 Jan 2011

Another performance tip, I used in Cannon Cat. Use simple physics shapes over complex ones.

Unless you really absolutely need the accuracy swap out your complex multibody physics shapes for rectangles and circles. This is useful for hit detection. If your objects are moving at high speed and the collision response doesn't require accurate physics this can be a good thing to try if you have lots of objects.

Danny
User offline. Last seen 2 years 26 weeks ago. Offline
Joined: 17 Aug 2011

Added two more sections to the first post

7) Updating Objects : Only do it when you have to.

8) Creating objects : Things to always remember.

Naomi
User offline. Last seen 2 years 34 weeks ago. Offline
Joined: 6 Jun 2011

Thank you, Danny! I love the tip on creating objects, especially the one with passing the param plus destroy function. Wow.

I have a question, though. Is the guy:destroy() function inside the spawnAGuy function local function? I mean, if there's a function B inside a local function A, does it automatically make the function B local to the function A?

Naomi

toby2
User offline. Last seen 36 weeks 1 day ago. Offline
Joined: 11 Mar 2011

Naomi,

It's roughly equivalent to

1
2
3
4
5
6
7
local guy = {}
 
guy.stuff = "Some stuff."
 
guy.thing = function(param) return param end
 
function guy:destroy() self:removeSelf(); self=nil; return true end

"Guy" is a local variable that happens to be a table. The key/value pairs you add to that table, even if the value happens to be a function, don't get scoped differently. The 'guy' table is local to its scope regardless of its contents.

Likewise

1
2
3
4
5
6
7
8
local function fA(param)
 
    local function fB(param)
        return param
    end
 
    return fB(param)
end

fA is local to whatever scope in which its defined and fB is local to fA (and therefore meaningless outside of fA().

Naomi
User offline. Last seen 2 years 34 weeks ago. Offline
Joined: 6 Jun 2011

Hey, @toby2, thank you for clarifying it. The reason why I asked is because Danny's example looks like this:

1
2
3
4
5
6
local function fA(param)
    function fB(param)
        return param
    end
    return fB(param)
end

I suppose function fB doesn't need to be declared local to be local to function fA.

Thanks again!
Naomi

mroberti
User offline. Last seen 2 weeks 4 days ago. Offline
Joined: 20 Nov 2010

Late to the party and just wanna give a big thank you to Danny for this great post!!! DEFINITELY going in my bookmarks for sure!!!

-Mario

toby2
User offline. Last seen 36 weeks 1 day ago. Offline
Joined: 11 Mar 2011

@Naomi,

Danny's example is actually more like the first example I posted. Stripped down it looks like this:

1
2
3
4
5
6
7
8
9
10
local function spawnAGuy(params)
    local guy = display.newImage("guy.png")
 
    function guy:destroy()
        display.remove(guy)
        guy = nil
    end
    
    return guy
end

The function guy:destroy() function declaration is equivalent to guy.destroy = function(self). "Guy" is already a local variable and you're adding a key/value pair named "destroy" with a function value to a table.

In the example you just posted, fB ends up being a global function when you remove 'local' from the declaration. I hope you can see the difference despite my less-than-stellar explanation.

Naomi
User offline. Last seen 2 years 34 weeks ago. Offline
Joined: 6 Jun 2011

@toby2, ah, of course. I didn't see (or rather my brain didn't quite register) the guy+colon there in Danny's example. Yes, now you've made it super clear to me. Thank you so much!

Naomi

Danny
User offline. Last seen 2 years 26 weeks ago. Offline
Joined: 17 Aug 2011

Updated the destroy part of the spawn example as you can simply pass self to display.remove and nil out self as self is passed in the declaration.

Beloudest
User offline. Last seen 3 years 8 weeks ago. Offline
Joined: 15 Jan 2012

Number 2 is not totally correct from my tests, if you cache obstacle.x in a local variable within a local function then update the cahced variable and update the objetc.x later it seems to be quicker than translate from my tests with parralax scrolling.

Beloudest
User offline. Last seen 3 years 8 weeks ago. Offline
Joined: 15 Jan 2012

Opps just read the previous posts, Don already mentioned it in a sense.

jeff472
User offline. Last seen 6 years 5 weeks ago. Offline
Joined: 2 Dec 2011

I thought that transition.to would give me the same kind of boost

so I turned this:

theActual.xScale = _G.currentScale;
theActual.yScale = _G.currentScale;
theActual.x = x*(40 * _G.currentScale)+(20 * _G.currentScale)
theActual.y = y* (40 * _G.currentScale)+(20 * _G.currentScale)

into this:
transition.to(theActual,{time=0,xScale = _G.currentScale,yScale = _G.currentScale, x = x*(40 * _G.currentScale)+(20 * _G.currentScale), y= y* (40 * _G.currentScale)+(20 * _G.currentScale) })

And it slowed down
Is that to be expected?

(I am about to reduce the calcs independently of this.. this issue is that I was expecting transition.to to produce an improvement in the same way as the translate() tip)

Any comments?

OwenYang
User offline. Last seen 1 year 4 weeks ago. Offline
Joined: 22 Jan 2011

wow....damn good!!!

gtt
User offline. Last seen 44 min 43 sec ago. Offline
Joined: 2 Aug 2011

Great post! thanks @danny, a few of these optimizations are new to me and we're going to take them into consideratio in our next game :)

Viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.