Share Your Code

Director Slim - Stripped down Director Class

Posted by Lerg, Posted on October 29, 2011, Last updated November 4, 2011

GitHub URL: 
https://github.com/Lerg/director-slim/zipball/master

I've managed to strip it down to only 15 lines of code! That's right. Plus 6 lines for the header.
Works faster, with lower overhead - hence higher performance.

UPDATE 2011-11-05: Reduced to 15 lines. Fully working example added to GitHub.

I didn't like director 1.3 when it came out, so I decided to stick with version 1.2a - the one with improved group cleaning function. I was fine with it thanks to Ricardo and Jonathan. Until I looked into the code more closely. Original source code size is over 500 lines long!

I am not using any of the effects - instead I created my own effects and call them directly from scene modules. So first thing I wiped out all the effects related stuff - there were tons of code for it. Next it was using way too many display groups - got rid of all of them! Now a localGroup you create in a scene module - is the top group, there are no groups above it like in the original director class.

Next I removed all unnecessary variables and functions. Also got rid of repeating code, collapsed several functions and optimized what's left.

And it doesn't use deprecated module() function.

Surprisingly, what I have got after such refactoring doesn't look like an original director class at all.

I must say, of course, that this module is not fully compatible with the original director class. You must use it only if you know what are you doing. You still use director:changeScene() function to change between scenes, however if you need to get a name of currently loaded module you use director.scene variable.

You DON'T need to write in main.lua

1
2
3
director = require( "director" )
local mainGroup = display.newGroup()       
mainGroup:insert(director.directorView)

Instead you just require the module and after it you can use director:changeScene() right away.

Sample main.lua file:

1
2
3
4
5
director = require('director')
local function main()
    director:changeScene('scene')
end
main()

Sample scene.lua

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
-- Module table
local _M = {}
 
-- Some global variable that we want to clean on exit
unboundedVariable = 100
 
-- Main function - MUST return a display.newGroup()
function _M.new()
    local localGroup = display.newGroup()
 
    local goToMenu = function()
        director:changeScene( 'here_goes_another_lua_scene_module' )
    end
 
    timer.performWithDelay(1500, goToMenu, 1)
 
    function _M.clean ()
        -- Clear here everything what is outside this function scope or localGroup
        -- Don't do localGroup:removeSelf()
        _G.unboundedVariable = nil
    end
 
    return localGroup
end
 
return _M

clean() function works as before. I fixed a major bug I encountered by using director 1.2a. The bug was that the clean() function was called only after a next scene is loaded - it was messing up my game and it was a final drop to start refactoring.

Now, here it is - Stripped down Director Class. In all it's glory.
director.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- Director Slim - Stripped down Director Class
-- Author: Lerg
-- Release date: 2011-10-30
-- Version: 1.0
-- License: MIT
-- Web: http://developer.anscamobile.com/code/director-class-24-lines-code
local director = {scene = 'main'}
function director:changeScene (moduleName)
    if type(moduleName) == 'nil' or self.scene == moduleName then return end
    local loadedModule = package.loaded[self.scene]
    if type(loadedModule) == 'table' and type(loadedModule.clean) == 'function' then
        loadedModule.clean()
    end
    if self.view then self.view:removeSelf() end
    if self.scene ~= 'main' and type(loadedModule) == 'table' then
        package.loaded[self.scene], self.view, loadedModule = nil
        collectgarbage('collect')
    end
    self.view, self.scene = require(moduleName).new(), moduleName
end
return director

From the GitHub you can download complete sample showing how to use this module. It includes simple scene changing effect.


Replies

madmusic6
User offline. Last seen 7 weeks 5 days ago. Offline
Joined: 23 Mar 2011

Will try this out now, thanks Lerg! Looks cool, clean and short.
Like you said, if people want effects then they can add them in.
Especially like the non module code (which you have been using for a long time, before we all realised)
I was doing my own, but it was taking too long, rather leave general functional stuff to the masters, until I become one.

Will use this along with your awesome pause timers and transitions!

madmusic6
User offline. Last seen 7 weeks 5 days ago. Offline
Joined: 23 Mar 2011

Hi Lerg. Just looked for you on irc channel. Getting this eror.

director.lua:28: module 'main_menu' not found:resource (main_menu.lu) does not exist in archive
no field package.preload['main_menu']

madmusic6
User offline. Last seen 7 weeks 5 days ago. Offline
Joined: 23 Mar 2011

It's OK, I see now, the "main_menu" needs to be a new scene i want to go to. So I have to make a main_menu.lua (another page)
Thanks

madmusic6
User offline. Last seen 7 weeks 5 days ago. Offline
Joined: 23 Mar 2011

If anyone wants effects, try this on every page:

Instead of: local localGroup = display.newGroup()

Put:
local localGroup = display.newGroup()
localGroup.alpha = 0
local trans = function()
transition.to( localGroup, { time=1500, alpha=1.0} )
end
trans()

Joe Kool
User offline. Last seen 36 weeks 2 days ago. Offline
Joined: 1 Jun 2011

So, if i choose to use this director, will I have to go back and change all the references to the old director?

Lerg
User offline. Last seen 2 hours 14 min ago. Offline
Joined: 8 May 2011

Thank you. Also fixed 'main_menu' issue - renamed it to be obvious.

Lerg
User offline. Last seen 2 hours 14 min ago. Offline
Joined: 8 May 2011

You just need to replace old director.lua with this director.lua.
Also remove from main.lua

1
2
local mainGroup = display.newGroup()       
mainGroup:insert(director.directorView)

And if you are lucky you won't need to change anything else. Basically if you are not using director effects and use only director:changeScene('scene_name') function - you are fine.

Also it must be noted that behavior of display objects outside localGroup have slightly changed. If they are instantiated in a new() function, then you might need to call localGroup:toFront() to make it appear above those objects.

flyingaudio
User offline. Last seen 45 weeks 6 days ago. Offline
Joined: 24 Mar 2011

Thanks Lerg. This has eliminated a flicker problem I get with Director1.4 on a scene change.
With yours, I can't get the clean to trigger. I have spent hours trying things as I look through the code, but obviously I am not understanding things.

In your sample scene you start with local _M = {}. Is that required? I have not done that in Director. I tried setting up my scene the same way, but no success.

With Director1.4, I can get clean to trigger two different ways:
function clean ( event ) -- I have this after module ( ..., package.seeall )
print("clean function")
end
or
localGroup.clean = function()
print("cleaned from localGroup.clean")
end

On line 21 of your code it looks like it calls the clean(), but I get nothing.
From line 20, it looks like a table is required.

On a different note, in main.lua I have been declaring
local director = require('director'), but I see you are using global. Is that important?

Any ideas would be most appreciated.

Lerg
User offline. Last seen 2 hours 14 min ago. Offline
Joined: 8 May 2011

Hi flyingaudio,
I've added a complete fully working example for the Director Slim, please download it from GitHub. It has clean() function calls with both module definitions, also it has nice scene changing effect included.

module ( ..., package.seeall ) - this thing is deprecated, try not to use it. Instead local _M = {} thing is introduced.

director variable should be global in main.lua. Local would not work.

See clean() functions in the example.

flyingaudio
User offline. Last seen 45 weeks 6 days ago. Offline
Joined: 24 Mar 2011

That did it Lerg.
I didn't realize _M was replacing module, so I had both.
You're examples are great.

Your 24 line solution had a cleanGroups section after a getmetatable thingy. Is your 15 line solution still doing all that cleaning, or do I need to cover that somewhere else (whatever that stuff maybe)?

There is one thing that broke, so I will figure out a different way to do it. With Director1.4 I can changeScene( { myParm = var}, "new scene" ) to pass a parameter into the new scene, but not with your version.

Thank you for sharing your work on this tight abbreviated version of Director.

Lerg
User offline. Last seen 2 hours 14 min ago. Offline
Joined: 8 May 2011

flyingaudio,
It still does all the cleanup. cleanGroups() was intended for very very old corona builds, so I removed it. There is still one case in which this function would be useful, but whoever need it can put this function back on their own (it's when you override default :removeSelf() function).

For passing parameters you can use different ways.
1) Attach parameters to director class:

1
2
director.params =  {myParam = var}
director:changeScene(...)

and in the scene file:
1
local myParam = director.params.myParam

2) Attach it to a global table. It's like first one, but you don't use director:

1
2
params =  {myParam = var}
director:changeScene(...)

and in the scene file:
1
local myParam = params.myParam

3) Use setOption() and getOption with this module http://pastebin.com/Wmgt262x. Require it in main lua as
require('options')

1
2
setOption('myParam', var)
director:changeScene(...)

and in the scene file:
1
local myParam = getOption('myParam')

flyingaudio
User offline. Last seen 45 weeks 6 days ago. Offline
Joined: 24 Mar 2011

Lergs,
Thanks for clarifying each point. It works like a charm. I practiced methods 1&2 above. Since they are both global, is there any advantage to either one?

ignis075
User offline. Last seen 1 year 14 weeks ago. Offline
Joined: 4 Oct 2010

Thank you SO MUCH for this Lerg! As much as I appreciate Ricardo's efforts and continued support/enhancement of Director, there seems to be a "trend" in coding to just add, add, add, with little regard to the bloat, weight, and overall efficiency of the module (subtle it might be, but on mobile devices you can never have something "too light").

So thank you for providing this code. I have already been using my own "lite" Director based on version 1.2, but it's around 100 lines versus your ultra-lite 15. :) So I will try to implement yours soon, especially since (like you) I tend to put the transition effects directly into the modules, or into "main.lua" itself.

Brent Sorrentino
Ignis Design LLC

Lerg
User offline. Last seen 2 hours 14 min ago. Offline
Joined: 8 May 2011

flyingaudio, there is not much difference between those methods, use whichever suits you better.

IgnisDesign, totally agree. You are welcome.

mobilefun4me
User offline. Last seen 2 weeks 1 day ago. Offline
Joined: 26 Apr 2010

License: MIT?

Looks very interesting and because of the benefits I'm very interested.

However, what are the implications if I use this in my application and/or game and publish it in the App Store and/or Android Market, etc.... and I earn cash?

-- Director Slim - Stripped down Director Class
-- Author: Lerg
-- Release date: 2011-10-30
-- Version: 1.0
-- License: MIT?

Thanks, and look forward to your continued publications!

Lerg
User offline. Last seen 2 hours 14 min ago. Offline
Joined: 8 May 2011

Hi mobilefun4me,
Why didn't you google the license meaning? It's one of the most popular freeware licenses.

You can do almost whatever you want with this code. Definitely can use it for your paid apps.

mobilefun4me
User offline. Last seen 2 weeks 1 day ago. Offline
Joined: 26 Apr 2010

Corona "ui.lua" Issue/Concern

Thanks Lerg:

I tried using the Corona "ui.lua" on my menu screen and when the code ran the following, the program hung:

-- Insert and run dimming effect
localGroup:insert(dimRect)
transition.to(dimRect, {time = effects.dimDelay, alpha = 0})

-- MUST return a display.newGroup()
return localGroup

It appears that when it ran through Director Slim, it returned to main.lua

I just added a normal button image in the sample code and everything was just great, but ui.lua seems to give it fits.

Thanks!

Lerg
User offline. Last seen 2 hours 14 min ago. Offline
Joined: 8 May 2011

mobilefun4me,
when it hangs try to put a simple print('whatever') before return localGroup line. I think Corona might not like sometimes initiating transitions right before the function end.

dubcanada
User offline. Last seen 1 year 11 weeks ago. Offline
Joined: 18 Mar 2011

Lerg, is there a way to delete all previous scenes? I get to my main game I don't want to have all the texture from the previous scenes slowing down my game.

I don't see any cleaning functionality in your code?

Lerg
User offline. Last seen 2 hours 14 min ago. Offline
Joined: 8 May 2011

Hi dubcanada,
There shouldn't be any problems with object removal. As long as you attach everything to localGroup or remove it yourself in clean() function it should be fine.
As soon as all objects using a certain spritesheet are deleted that asset is removed from the texture memory.
It is calling garbage collector on each scene change so that should wipe all the garbage out for sure.

flyingaudio
User offline. Last seen 45 weeks 6 days ago. Offline
Joined: 24 Mar 2011

Lerg,
Now that Storyboard is out, will you use it, or stick with your Director Slim? Why?

Lerg
User offline. Last seen 2 hours 14 min ago. Offline
Joined: 8 May 2011

flyingaudio,
I will consider Storyboard later. Director Slim advantage is that it's super simple and I know exactly what's going on inside it. Can't say the same for Storyboard.

bragbase
User offline. Last seen 25 weeks 4 days ago. Offline
Joined: 14 Oct 2011

Lerg, got your sample from Github and its running fine, so I added a few objects to the scene_modern.lua file (to simulate a level for example), anyway, I added some display objects then add them to the local group, then i added a runtime event handler for enterFrame, basically to move the group of objects around, everything still works fine and enter frame code executes.

The problem is, when i use Runtime: then the click to go back fails to work. Can I use Runtime: to add an event hander for enterFrame inside these scene.lua files? Or where / how would i create handlers for enterFrame etc?

hope this makes sense :)
anyway, really like your solution and hope to use it.
thanks

bragbase
User offline. Last seen 25 weeks 4 days ago. Offline
Joined: 14 Oct 2011

any ideas?
thanks

Lerg
User offline. Last seen 2 hours 14 min ago. Offline
Joined: 8 May 2011

Hi bragbase,
Your issue doesn't ring a bell for me. Runtime listeners should work just fine if they are properly handled. My guess it has nothing to do with the click event.
You can zip and leave a link to your setup so I can run it and see what's wrong.

bragbase
User offline. Last seen 25 weeks 4 days ago. Offline
Joined: 14 Oct 2011

You are right its not the click event, i am just not sure where to place the scene specific runtime listeners.
Will see if i can zip up some time, can I email it to you?

Lerg
User offline. Last seen 2 hours 14 min ago. Offline
Joined: 8 May 2011

bragbase, do you get any errors when you click to go back? My guess is that you trying to use a group in the enterFrame listener which is no longer available (removed).
Have you tried to remove the listener in the clean function?
There is no messaging system on the site so I can't tell you my email privately.

Pasz72
User offline. Last seen 9 weeks 6 days ago. Offline
Joined: 13 Jul 2011

Really nice. Going to try to setup it along with your pause timers and transitions code.

I really need the following effect from the old director class.

1
2
3
4
5
6
7
if effect == "moveFromRight" then
        
        nextView.x = _W
        nextView.y = 0
        --
        showFx = transition.to( nextView, { x = 0,   time = fxTime } )
        showFx = transition.to( currView, { x = -_W, time = fxTime, onComplete = fxEnded } )

What's the nicest way to do this ? Implement it within effects.lua ?

Lerg
User offline. Last seen 2 hours 14 min ago. Offline
Joined: 8 May 2011

Pasz72, no support for such effect.
Consider storyboard as well in this case.

Kulpritt
User offline. Last seen 2 years 12 weeks ago. Offline
Joined: 5 Jan 2012

Works great....thank you very much.

lemsim
User offline. Last seen 1 year 31 weeks ago. Offline
Joined: 30 Mar 2011

Love this reduced size Director! I was wondering if anybody has an app(s) out that uses this? So far i do not see any issues on my own upcoming app but I am very interested in knowing if apps in the wild are running fine.

Thanks for sharing this piece of amazing code!

Mo

ignis075
User offline. Last seen 1 year 14 weeks ago. Offline
Joined: 4 Oct 2010

Hi Lerg,
Once again, thank you for the excellent and light piece of code for "Director Slim". As I try to understand what's going on behind the scenes with your code, my question is this:

What is the essential purpose of the "clean()" function built into each template? I think this was at some point "callClean()" in the original Director...

As far as I can tell, on scene change, it's calling this function, but perhaps the purpose eludes me. All I want to do is this:

1) clear the display objects in the scene (already done via "self.view:removeSelf()").
2) completely clear/void the module and its functions from memory, which appears to happen in these lines (it removes it from Lua's "package.loaded" table):

1
2
3
4
if ( self.scene ~= "main" and type(loadedModule) == "table" ) then
  package.loaded[self.scene], self.view, loadedModule = nil
  collectgarbage("collect")
end

Assuming that I manually clear/nil my listeners, timers, etc. before changing scenes, shouldn't the above 2 steps fully and permanently clear out ALL aspects of the scene and free up its memory completely?

If so, what is the purpose of calling the "clean()" function within the scene being cleared? I'm sure there's a good and valid reason, but again, it's somehow eluding me...

Any help is appreciated; I genuinely hope to use your Director Slim versus Director 1.4 (now 2000+ lines, albeit 1000+ lines are probably comments and blank lines). I also hesitate to use Storyboard, which is very promising but seems to have a few thorny issues, minor bugs, and performance issues at this point.

Sincerely,
Brent Sorrentino
Ignis Design

Lerg
User offline. Last seen 2 hours 14 min ago. Offline
Joined: 8 May 2011

Hi IgnisDesign,

clean() function in your module should wipe out everything that is not possible to clean by director module. Such as globals, timers, something in other modules, maybe connections and files. Rule is when you use something in the scene think about how to clean it later right away, in case of local objects inside the localGroup you don't have to do anything special.
Yes this function is called on scene change.

Yes, it should.

You can put your timers/listeners clean up code inside the clean() function and it would be called automatically - that's all.

I hope this helped.

Dr.Mabuse
User offline. Last seen 21 weeks 1 day ago. Offline
Joined: 22 Feb 2012

First of all, thank you for a great piece of code.

If I have a seperate file I want to include in one of my scenes (let's say ui.lua), do I have to edit this file in the same way as your example (adding the _M module) or will director take care of it as long as it is called (and required) from within the _M.new function in my scene?

Lerg
User offline. Last seen 2 hours 14 min ago. Offline
Joined: 8 May 2011

Dr.Mabuse,

Thank you for thanking.
It's better to avoid module(..., package.seeall) stuff and use _M style everywhere. But you don't have to actually. It will work both ways.

Dr.Mabuse
User offline. Last seen 21 weeks 1 day ago. Offline
Joined: 22 Feb 2012

Just to check if I understand this correctly; if I want to implement a 3rd-party tool like Lime, Text Cady or Particle Candy, I then have to edit all their lua-files and add the _M stuff to these?

Lerg
User offline. Last seen 2 hours 14 min ago. Offline
Joined: 8 May 2011

Dr.Mabuse,
No, you don't have to.

Axie Studios
User offline. Last seen 1 year 8 weeks ago. Offline
Joined: 1 Feb 2012

Man, this is really great and solved some of the issues my game was having!

I have a question though, how would I reload my scene with this?

Let's same the player dies in my game and I want to reload the same level, how would I go about doing this?

Lerg
User offline. Last seen 2 hours 14 min ago. Offline
Joined: 8 May 2011

Axie Studios,
Hi make a restart.lua with similar content to this:

1
2
3
4
5
6
7
8
9
local _M = {}
 
function _M.new()
    local localGroup = display.newGroup()
    timer.performWithDelay(100, function () director:changeScene('level') end)
    return localGroup
end
 
return _M

Which come backs to the level view.

Axie Studios
User offline. Last seen 1 year 8 weeks ago. Offline
Joined: 1 Feb 2012

Lerg, thanks a lot man! Works perfectly!

Amazing job on this whole class man.

kensla.creations
User offline. Last seen 44 weeks 1 day ago. Offline
Joined: 21 Feb 2012

One question: If I call changeScene("A") from function a:method(), director will then call a:clean() and then return back to function a:method(). But "a" has already been cleaned. Is this a problem for Lua? Do I have to be really careful not to have code in function "a" following the call to changeScene?

I am a bit uncomfortable with code where code "a" calls code "b" calls code "a". Warning for endless loops and all.

Thanks for the code Lerg.