Share Your Code

Director Class

Posted by ricardorauber, Posted on October 7, 2010, Last updated August 26, 2011

Hello everybody! I'm so pleased to share with you my Director class for control scenes with or without transitions!

Version 1.4

New BOOKS, check this out!

http://rauberlabs.blogspot.com/2011/08/director-14-books.html

Video Tutorial: http://rauberlabs.blogspot.com/2010/12/director-video-tutorial.html

ATTENTION - All questions will now be answered at Director's sub-forum: http://developer.anscamobile.com/forums/director-class

It's very easy to use, just import it, add the groups to the main group and it's done! This zip file contains an example with all the transitions in this version. Also, there's a template.lua file that you can use in your own projects.

Feel free to ask anything about it at the forum or by e-mail, I'll be very pleased to ask your answers!

- Version 1.4:
https://bitbucket.org/ricardorauber/corona/downloads/director_by_ricardo_rauber_1_4.zip

- Version 1.3:
https://bitbucket.org/ricardorauber/corona/downloads/director_by_ricardo_rauber-1_3.zip

- Version 1.2:
http://bitbucket.org/ricardorauber/corona/downloads/director_by_ricardo_rauber_1_2.zip

- Version 1.1:
http://bitbucket.org/ricardorauber/corona/downloads/director_by_ricardo_rauber_1_1.zip

- Version 1.0:
http://bitbucket.org/ricardorauber/corona/downloads/director_by_ricardo_rauber.zip

See ya!

More Information: http://rauberlabs.blogspot.com/
http://twitter.com/rauberlabs

Compatibility: 
Corona 2.0

Replies

LiveToCollect
User offline. Last seen 1 year 50 weeks ago. Offline
Joined: 3 Feb 2010

Well done, Ricardo. I like what I see so far and intend to put this to use in my next project. I will let you know how it goes when I am done. Thank you for sharing your code!

Tim
User offline. Last seen 3 years 19 weeks ago. Offline
Alumni
Joined: 12 Aug 2010

Very nice, thanks for sharing!

jonbeebe
User offline. Last seen 37 weeks 3 days ago. Offline
Joined: 26 Jul 2010

This is amazing, and admittedly, much better than what I was using. I'll definitely use this class, thanks for sharing Ricardo!

MBD
User offline. Last seen 3 weeks 3 days ago. Offline
Joined: 14 Sep 2010

Perfect, perfect, perfect!
Great job!!

Stephen Lewis
User offline. Last seen 9 hours 1 min ago. Offline
Joined: 24 Sep 2010

I wired this into my project and it works great! Thank you for sharing it, Ricardo.

dario
User offline. Last seen 1 year 33 weeks ago. Offline
Joined: 18 Aug 2010

I love it. Thanx a lot :)

awesomeware14
User offline. Last seen 3 years 41 weeks ago. Offline
Joined: 3 Oct 2009

You asked for additional code changes so here are two effects I added to mine:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
        -----------------------------------
        -- EFFECT: Move From Top
        -----------------------------------
        
        elseif effect == "moveFromTop" then
        
                nextView.x = 0
                nextView.y = display.contentWidth*-1
                --
                loadScene (nextScene)
                --
                showFx = transition.to ( nextView, { y=0, time=fxTime } )
                showFx = transition.to ( currentView, { y=display.contentHeight, time=fxTime } )
                --
                timer.performWithDelay( fxTime, fxEnded )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
        -----------------------------------
        -- EFFECT: Move From Bottom
        -----------------------------------
        
        elseif effect == "moveFromBottom" then
                        
                nextView.x = 0
                nextView.y = display.contentHeight
                --
                loadScene (nextScene)
                --
                showFx = transition.to ( nextView, { y=0, time=fxTime } )
                showFx = transition.to ( currentView, { y=display.contentHeight*-1, time=fxTime } )
                --
                timer.performWithDelay( fxTime, fxEnded )

Rob2
User offline. Last seen 17 weeks 3 days ago. Offline
Joined: 14 Apr 2010

Fantastic

reddotinc
User offline. Last seen 2 years 39 weeks ago. Offline
Joined: 20 Jun 2010

@awesomeware14 your move from top code creates doesn't transition smoothly, so I fixed it :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
         -----------------------------------
        -- EFFECT: Move From Top
        -----------------------------------
        
        elseif effect == "moveFromTop" then
        
                nextView.x = 0
                nextView.y = display.contentHeight*-1
                --
                loadScene (nextScene)
                --
                showFx = transition.to ( nextView, { y=0, time=fxTime } )
                showFx = transition.to ( currentView, { y=display.contentHeight, time=fxTime } )
                --
                timer.performWithDelay( fxTime, fxEnded )

// red.

awesomeware14
User offline. Last seen 3 years 41 weeks ago. Offline
Joined: 3 Oct 2009

Oops that was intended to be be display.contentHeight, I'm glad somebody noticed lol.

thegwill
User offline. Last seen 41 weeks 4 days ago. Offline
Joined: 21 Aug 2010

Firstly, thanks for sharing this code - it's really very cool and helpful.

However, I keep getting errors:

I have a main menu scene.
I have an options menu scene.
It successfully transitions from the main menu to the options scene.

An error is occurring when when trying to transition back again to the main menu scene (from the options scene).

1
director.lua:117: ERROR: table expected. If this is a function call, you might have used '.' instead of ':'

Any pointers?

ricardorauber
User offline. Last seen 48 weeks 5 days ago. Offline
Joined: 30 Aug 2010

Try to use this:

1
director:changeScene("file","fade")

thegwill
User offline. Last seen 41 weeks 4 days ago. Offline
Joined: 21 Aug 2010

Yeah, that's what I'm using.

I think it's down to my other code. I'm doing some extensive refactoring to plumb your director class into an existing set of screens.

Thanks for replying.

alcamie
User offline. Last seen 13 weeks 5 days ago. Offline
Joined: 25 Sep 2010

I'm just not getting this.

I have a couple of questions.

1. Do you keep function "new()" as new or do you rename it for every screen file?
2. Do you keep localGroup as localGroup for every screen file?
3. do you change anything at all in main.lua or just leave it?

The reasons for my asking these questions is that I cannot get the results at all when I'm trying to use it as I do with your example.

mediakitchen
User offline. Last seen 1 year 28 weeks ago. Offline
Joined: 27 Jul 2010

@alcamie

I am no expert but I have adapted my game to use the director class.

So in answer to your questions.

1. Yes I left new as it was for each screen
2. Yes I kept localGroup as localGroup for each screen file
3. The only thing I added to main.lua was a couple of lines of code for openfeint so the answer is essentially yes.

What I did was use the example as a base and cut and paste my code into the screen1 and screen2 files then I just duplicated screen1 to create additional screens.

It works a dream and saved me loads of work so thankyou to the developer.

alcamie
User offline. Last seen 13 weeks 5 days ago. Offline
Joined: 25 Sep 2010

I have animation and other transitions happening ei. animated smoke and mist in the background, animated semi transparent slideovers left and right entering for highscores and about which both have a an event that wait for another touch before sliding closed and presenting the menu buttons again.

When I keep the original scene2 it goes to that alright but when I click on goback, thats when the issues occur.
The scene2 doesn't completely slide out and scene1 doesn't form or start.

maybe I should cut the code down to post on here..

alcamie
User offline. Last seen 13 weeks 5 days ago. Offline
Joined: 25 Sep 2010

I have tracked it down to this chunk of code.

Can someone help me out here as to why it's killing the transition?

does it have anything to do with Runtime:addEventListener ??

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
38
39
        local tPrevious = system.getTimer()
        local function moveAtmosphere(event)
        local tDelta = event.time - tPrevious
        tPrevious = event.time
        local xOffsetMist = ( mistSpeed * tDelta )
        local xOffsetSmoke = ( smokeSpeed * tDelta )
        mistLeft.x = mistLeft.x + xOffsetMist
        mistCenter.x = mistCenter.x + xOffsetMist 
        mistRight.x = mistRight.x + xOffsetMist
        smokeLeft.x = smokeLeft.x + xOffsetSmoke
        smokeCenter.x = smokeCenter.x + xOffsetSmoke
        smokeRight.x = smokeRight.x + xOffsetSmoke
 
                if mistLeft.x > display.contentWidth + mistLeft.width  then
                        mistLeft:translate( -display.contentWidth *3 + mistLeft.x, 0)
                end
                if mistCenter.x > display.contentWidth + mistCenter.width  then
                        mistCenter:translate( -display.contentWidth *3 + mistCenter.x, 0)
                end
                if mistRight.x > display.contentWidth + mistRight.width  then
                        mistRight:translate( -display.contentWidth *3 + mistRight.x, 0)
                end
 
                if smokeLeft.x > display.contentWidth + smokeLeft.width  then
                        smokeLeft:translate( -display.contentWidth * 3, 0)
                end
 
                if smokeCenter.x > display.contentWidth + smokeCenter.width  then
                        smokeCenter:translate( -display.contentWidth * 3, 0)
                end
                if smokeRight.x > display.contentWidth + smokeRight.width  then
                        smokeRight:translate( -display.contentWidth * 3, 0)
                end
                
                crankTitle.x = crankInitX + (math.sin(math.random( 1,tPrevious))*.2)
                crankTitle.y = crankInitY + (math.cos(math.random( 1,tPrevious))*.5)
                crankTitle.rotation = math.random((math.fmod (tPrevious, 2)),(math.fmod (tPrevious, 3)))
        end
        Runtime:addEventListener("enterFrame",moveAtmosphere)   

any help is appreciated.

alcamie

alcamie
User offline. Last seen 13 weeks 5 days ago. Offline
Joined: 25 Sep 2010

ok some testing shows that it lies within this code. The Runtime listener doesn't seem to kill things, so what is it about this code that kills it?

All this code does is cycles the mist and smoke in a loop.

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
        local xOffsetMist = ( mistSpeed * tDelta )
        local xOffsetSmoke = ( smokeSpeed * tDelta )
        mistLeft.x = mistLeft.x + xOffsetMist
        mistCenter.x = mistCenter.x + xOffsetMist 
        mistRight.x = mistRight.x + xOffsetMist
        smokeLeft.x = smokeLeft.x + xOffsetSmoke
        smokeCenter.x = smokeCenter.x + xOffsetSmoke
        smokeRight.x = smokeRight.x + xOffsetSmoke
                
 
                if mistLeft.x > display.contentWidth + mistLeft.width  then
                        mistLeft:translate( -display.contentWidth *3 + mistLeft.x, 0)
                end
                if mistCenter.x > display.contentWidth + mistCenter.width  then
                        mistCenter:translate( -display.contentWidth *3 + mistCenter.x, 0)
                end
                if mistRight.x > display.contentWidth + mistRight.width  then
                        mistRight:translate( -display.contentWidth *3 + mistRight.x, 0)
                end
 
                if smokeLeft.x > display.contentWidth + smokeLeft.width  then
                        smokeLeft:translate( -display.contentWidth * 3, 0)
                end
 
                if smokeCenter.x > display.contentWidth + smokeCenter.width  then
                        smokeCenter:translate( -display.contentWidth * 3, 0)
                end
 
                if smokeRight.x > display.contentWidth + smokeRight.width  then
                        smokeRight:translate( -display.contentWidth * 3, 0)
                end

any iDeas?

alcamie
User offline. Last seen 13 weeks 5 days ago. Offline
Joined: 25 Sep 2010

OK!!

I know what the issue was. I will look pretty silly I'm sure but I've had a ureka moment.

For all of you guy's out there who are as green as me to this you will find that you have to kill:
Runtime:addEventListener("enterFrame", onFrame) with
Runtime:removeEventListener("enterFrame", onFrame) where onFrame is the name of the function.
Just before you change scenes. ie.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
        local function moveScenery(event)
 
          -- Animation Code Goes Here
                
        end
 
        -- Start everything moving
        Runtime:addEventListener( "enterFrame", moveScenery );
        
        ---=======================================================
        ---================= Controller Buttons ==================
        ---=======================================================
 
        -- Touch to go back
        local function touched ( event )
                if event.phase == "ended" then
                        Runtime:removeEventListener("enterFrame", moveScenery)
                        director:changeScene("screen1","fade")
                end
        end
        backToMenu:addEventListener("touch",touched)

I hope this helps those who get as stumped as I did.

Cheers
Alcamie

mediakitchen
User offline. Last seen 1 year 28 weeks ago. Offline
Joined: 27 Jul 2010

Thank you Alcamie - I think that solves a problem I am having with my game when it switches between screens.

Definitely one that everyone should know about.

ricardorauber
User offline. Last seen 48 weeks 5 days ago. Offline
Joined: 30 Aug 2010

Hey guys, thanks for using director class but be careful of some things. For now, director goes through all display objects inserted in the localGroup and remove them. It can't stop timers, transitions and eventListeners. I will work on that for the next versions so, until that, you can do as alcamie did.

One more thing, every file must have the new() function as it is but the localGroup can have a different name. Director class explicit calls for the new() function but doesn't matter the name of the display group.

alcamie
User offline. Last seen 13 weeks 5 days ago. Offline
Joined: 25 Sep 2010

can it handle multiple group such as for instance:
If not at the moment, how can I make it handle it>??

localGroup
hudGroup
etc

I have tried

1
2
3
4
5
6
7
function new()
 
....etc
 
return localGroup, hudGroup
 
end

Cheers

Chris

ricardorauber
User offline. Last seen 48 weeks 5 days ago. Offline
Joined: 30 Aug 2010

alcamie,

If you want to return more than 1 group, you have to change the Director class. Why you want to return groups like that? You can insert groups in the localGroup.

alcamie
User offline. Last seen 13 weeks 5 days ago. Offline
Joined: 25 Sep 2010

How do you mean "insert groups in the localGroup"

Could you give me an example using my groups I gave in my example please.

Cheers

Chris

ricardorauber
User offline. Last seen 48 weeks 5 days ago. Offline
Joined: 30 Aug 2010

It's easy!

1
2
localGroup:insert(hudGroup)
localGroup:insert(otherGroup)

The localGroup will be used in transitions on Director, everything inserted in the localGroup will be inside the effects, otherwise will be above everything.

alcamie
User offline. Last seen 13 weeks 5 days ago. Offline
Joined: 25 Sep 2010

ahhhhhhh..

Cooollll.. Thanks..

Didn't realise it was that easy.. I've been racking my brain for hours :)

nglasier
User offline. Last seen 8 weeks 5 hours ago. Offline
Joined: 26 Oct 2010

Can't figure this out. Running Ricardo's demo - no problem. I can create my own version with a menu screen and a settings screen, and flip to the settings screen from the menu screen - no problem. But when I want to return, nothing happens. There are no functions on the new screen yet. The only thing I changed from screen2.lua in Ricardos project was the name of the file being called by director:changeScene, and the screen2 filename as well. The menuscreen file is in the same folder as the rest of the program (it loads just fine at the beginning of the program).

I've done some testing and the touched function is being run, and the - if event.phase - conditional is working so I've narrowed it down to the statement:

director:changeScene("menuscreen", "fade") -- but I can't see anything wrong with that

I have the statement

module(..., package.seeall)

at the top of the file, so it should see the director shouldn't it?

I tried changing the name back to screen1, and put a copy of that file in the project folder, but still no go. Even tried closing the project and then re-opening it.

Any advice would be helpful

Nick

ricardorauber
User offline. Last seen 48 weeks 5 days ago. Offline
Joined: 30 Aug 2010

Hello nglasier,

Is there any error at the terminal? Try to use that to debug your project.

Can you put here the code?

nglasier
User offline. Last seen 8 weeks 5 hours ago. Offline
Joined: 26 Oct 2010

Thanks for the reply Ricardo. I'm very new to both LUA and Corona, and hadn't used the terminal app to launch Corona. Just done that, and now I can see that the problem was with the code on the first screen (menu) after all.

I have some cogs rotating (using one of the demos on the corona site) on the menu screen.
The rotation is done using: local function animate(event )

My mistake was to put the touch event above the animate (enterFrame) event in the menuscreen file. I guess my call (in the touch event that changes screen) to remove the enterFrame eventListener, wasn't happening because the event it referred to was written further down the page. I'm used to methods being written in an implementation section where the order isn't important because they have been declared in the header file.

When I clicked on the button to switch to the second screen I got a ton of stack traceback messages in Terminal saying: runtime error- attempt to perform arithmetic operation on field 'rotation' (a nil value). The rotation arithmetic is only performed in the enterFrame event, and the animation was happening just fine when the project first loaded. Only when I tried to remove the event did I get the runtime errors.

The screens were switched though, and code on the second screen was running, which is why I thought the error was with the second screen.

Just moving the touch event to the bottom of the file so it comes after the enterFrame event code, solved the problem immediately. Still got a lot to learn about LUA, obviously :-}

Regards, Nick

iphone_2010
User offline. Last seen 3 years 29 weeks ago. Offline
Joined: 5 Nov 2010

Hello ricardorauber,
Im really confused here and really don't know what is the groups concept and how it to be used. Im sorry, forgive me for my stupid question im just new here and have been facing problem with Corona.

Im trying to learn and need your support if possible.

Thanks

ricardorauber
User offline. Last seen 48 weeks 5 days ago. Offline
Joined: 30 Aug 2010

iphone_2010,

It's very simple, just think that groups are like boxes. You can put all your display objects (images, rectangles, circles..) inside the box and then you can do actions like move it or resize it or just organize it like layers.

In the case of Director, groups are used for the current scene and the next scene, so when you change, Director creates these two groups and animate it as you choosed. You just need to insert ALL your objects in the main group of the Director and it does the job.

iphone_2010
User offline. Last seen 3 years 29 weeks ago. Offline
Joined: 5 Nov 2010

Thanks for your support.
I need to know can i use Director for moving a balloon forever on a sky background lets say moving the balloon (y-corodination).

like builing a game a person is walking,m instead i want to move the balloon ( without the Sprite Sheet object as i have no idea about it yet :)

Thanks man

ricardorauber
User offline. Last seen 48 weeks 5 days ago. Offline
Joined: 30 Aug 2010

Actualy this is not what Director does. You can use the forum or the list of examples to find something like you want. Director control scenes (files), so you can create different files for each part of your app, like intros, menus, settings, credits and levels.

jmartinho
User offline. Last seen 19 weeks 6 days ago. Offline
Joined: 25 Mar 2010

Hi ricardo
thank you for the wonderful director library

One Question:

I have created a function inside a subpage screen1.lua

function showCommonMenu ( event )
-- SOME CODE
end

but I want to call the function showCommonMenu() from the main page main.lua

how I do that?

ricardorauber
User offline. Last seen 48 weeks 5 days ago. Offline
Joined: 30 Aug 2010

jmartinho

If you want to call a function that is not declared in the main.lua file, you must import the file as a package, not as a scene in the director.

For that it's simple, just create your file with all functions you want and use it like you use Director in main.lua:

1
2
local myPackage = require("myPackage")
myPackage.showCommonMenu ()

ricardorauber
User offline. Last seen 48 weeks 5 days ago. Offline
Joined: 30 Aug 2010

Hey guys, this is important!

Talking to jmartinho by e-mail, he asked me about a custom clean() function that executes every time that scenes are changed. Director should execute this function, so he doesn't need to put the call in every file. So, I made a research and if you want the same functionality, just go to the end of the changeScene function and add the commented part below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
        -----------------------------------
        -- Clean up memory
        -----------------------------------
        
        if lastScene then
 
                -- If you want to use a custom function to clean, use this
--              if string.lower(lastScene) ~= "main" then
--                      package.loaded[lastScene].clean()
--              end
 
                package.loaded[lastScene] = nil
        end
        lastScene = nextScene
        collectgarbage("collect")

As you can see, just before Director release the loaded file, you can make a call to any function of it. You can also use this type of call to other things but remember, you must create the same function name in EVERY file that Director will use as a scene!

Any doubts just tell me!

TheGiant
User offline. Last seen 3 weeks 2 days ago. Offline
Joined: 31 May 2010

Hi Ricardo
Thanks for a wonderful library!

I have only one problem

If you press "screen2" while it is opening it will quit the simulator

To simulate just press the screen quick multiple times.

spjnowak
User offline. Last seen 1 week 9 hours ago. Offline
Joined: 21 Aug 2009

Hi Ricardo

I am getting the same problem. I'm building a story book where each page is a Director scene. This is working great as some of the pages have physics objects as well as images and text and all gets cleaned up well. However, I am using swipes to navigate from page to page (each swipe triggers a Director scene change) and if I swipe too quickly then Corona crashes.

The error is occurring at in the fxEnded function at the line ...

currentView:insert(currentScreen)

which gives this error (the line numbers will not correspond to yours as I have added my touch navigation code to director.lua)

Runtime error
/Users/stefan/Lettermen/Party_v1/director.lua:269: bad argument #-2 to 'insert' (Proxy expected, got nil)
stack traceback:
[C]: ?
[C]: in function 'insert'
/Users/stefan/Lettermen/Party_v1/director.lua:269: in function '_listener'
?: in function <?:441>
?: in function <?:214>

It looks like it may be a timing problem to do with transitions and object clean up. It may be that my additional code is causing the problem but I recall somebody else saying that there could be a problem with fxEnded being called before transitions are completed.

Any clues to fixing this?

Stefan

ricardorauber
User offline. Last seen 48 weeks 5 days ago. Offline
Joined: 30 Aug 2010

Guys,

Let's try something different. I'm without my macbook for a few days, so let's try it together.

First I'll create a new variable called newScene:

1
2
3
4
5
6
--
local currentScreen, nextScreen
local lastScene = "main"
local newScene
local fxTime = 200
--

Then, I'll cut the "clean up memory" part from the changeScene to the fxEnded:

1
2
3
4
5
6
7
8
9
10
11
        -----------------------------------
        -- EFFECT: None
        -----------------------------------
        
        else
                timer.performWithDelay( 0, fxEnded )
                loadScene (nextScene)
        end
        
        return true
end

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
------------------------------------------------------------------------        
-- EFFECT ENDED
------------------------------------------------------------------------
 
local function fxEnded ( event )
 
        currentView.x = 0
        currentView.y = 0
        currentView.xScale = 1
        currentView.yScale = 1
        --
        cleanGroups(currentView,0)
        --
        currentScreen = nextScreen
        currentView:insert(currentScreen)
        nextView.x = display.contentWidth
        nextView.y = 0
        nextView.xScale = 1
        nextView.yScale = 1
        
        -----------------------------------
        -- Clean up memory
        -----------------------------------
        
        if lastScene then
        
                -- If you want to use a custom function to clean, use this
--              if string.lower(lastScene) ~= "main" then
--                      package.loaded[lastScene].clean()
--              end
 
                package.loaded[lastScene] = nil
        end
        lastScene = newScene
        collectgarbage("collect")       
        
end

Finally, I'll populate the new variable in the changeScene:

1
2
3
4
5
6
7
8
9
10
11
12
        -----------------------------------
        -- If is the same, don't change
        -----------------------------------
        
        if lastScene then
                if string.lower(lastScene) == string.lower(nextScene) then
                        return true
                end
        end
        
        newScene = nextScene
        local showFx

With all these changes, there will be no timing problem with the transitions. So, let's see the Director:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
module(..., package.seeall)
 
--====================================================================--        
-- DIRECTOR CLASS
--====================================================================--
--
-- Version: 1.1
-- Made by Ricardo Rauber Pereira @ 2010
-- Blog: http://rauberlabs.blogspot.com/
-- Mail: ricardorauber@gmail.com
--
-- This class is free to use, feel free to change but please send new versions
-- or new features like new effects to me and help us to make it better!
--
--====================================================================--        
-- CHANGES
--====================================================================--
--
-- 06-OCT-2010 - Ricardo Rauber - Created
-- 07-OCT-2010 - Ricardo Rauber - Functions loadScene and fxEnded were
--                                taken off from the changeScene function;
--                                Added function cleanGroups for best
--                                memory clean up;
--                                Added directorView and effectView groups
--                                for better and easier control;
--                                Please see INFORMATION to know how to use it
--
--====================================================================--
-- VARIABLES
--====================================================================--
--
-- currentView:         Main display group
-- nextView:            Display group for transitions
-- currentScreen:       Active module
-- nextScreen:          New module
-- lastScene:           Active module in string for control
-- newScene:            New module in string for control
-- nextScene:           New module in string for control
-- effect:              Transition type
-- arg[N]:              Arguments for each transition
-- fxTime:              Time for transition.to
--
--====================================================================--
-- INFORMATION
--====================================================================--
--
-- * For best practices, use fps=60, scale = "zoomStretch" on config.lua
--
-- * In main.lua file, you have to import the class like this:
--
--   director = require("director")
--   local g = display.newGroup()       
--       g:insert(director.directorView)
--
-- * To change scenes, use this command [use the effect of your choice]
--
--   director:changeScene("settings","moveFromLeft")
--
-- * Every scene is a lua module file and must have a new() function that
--   must return a local display group, like this: [see template.lua]
--
--   module(..., package.seeall)
--   function new()
--         local lg = display.newGroup()
--     ------ Your code here ------
--     return lg
--   end
--
-- * Every display object must be inserted on the local display group
--
--   local background = display.newImage("background.png")
--       lg:insert(background)
--
-- * This class doesn't clean timers! If you want to stop timers when
--   change scenes, you'll have to do it manually.
--
--====================================================================--
 
directorView = display.newGroup()
currentView  = display.newGroup()
nextView     = display.newGroup()
effectView   = display.newGroup()
--
local currentScreen, nextScreen
local lastScene = "main"
local newScene
local fxTime = 200
--
directorView:insert(currentView)
directorView:insert(nextView)
directorView:insert(effectView)
 
------------------------------------------------------------------------        
-- CLEAN GROUP
------------------------------------------------------------------------
 
local function cleanGroups ( curGroup, level )
        if curGroup.numChildren then
                while curGroup.numChildren > 0 do
                        cleanGroups ( curGroup[curGroup.numChildren], level+1 )
                end
                if level > 0 then
                        curGroup:removeSelf()
                end
        else
                curGroup:removeSelf()
                curGroup = nil
                return
        end
end
 
------------------------------------------------------------------------        
-- LOAD SCENE
------------------------------------------------------------------------
 
local function loadScene ( nextScene )
 
        nextScreen = require(nextScene).new()
        nextView:insert(nextScreen)
        
end
 
------------------------------------------------------------------------        
-- EFFECT ENDED
------------------------------------------------------------------------
 
local function fxEnded ( event )
 
        currentView.x = 0
        currentView.y = 0
        currentView.xScale = 1
        currentView.yScale = 1
        --
        cleanGroups(currentView,0)
        --
        currentScreen = nextScreen
        currentView:insert(currentScreen)
        nextView.x = display.contentWidth
        nextView.y = 0
        nextView.xScale = 1
        nextView.yScale = 1
        
        -----------------------------------
        -- Clean up memory
        -----------------------------------
        
        if lastScene then
        
                -- If you want to use a custom function to clean, use this
--              if string.lower(lastScene) ~= "main" then
--                      package.loaded[lastScene].clean()
--              end
 
                package.loaded[lastScene] = nil
        end
        lastScene = newScene
        collectgarbage("collect")       
        
end
 
------------------------------------------------------------------------        
-- CHANGE SCENE
------------------------------------------------------------------------
 
function director:changeScene(nextScene, 
                              effect, 
                              arg1,
                              arg2,
                              arg3)
 
 
        -----------------------------------
        -- If is the same, don't change
        -----------------------------------
        
        if lastScene then
                if string.lower(lastScene) == string.lower(nextScene) then
                        return true
                end
        end
        
        newScene = nextScene
        local showFx
 
        -----------------------------------
        -- EFFECT: Move From Right
        -----------------------------------
        
        if effect == "moveFromRight" then
                        
                nextView.x = display.contentWidth
                nextView.y = 0
                --
                loadScene (nextScene)
                --
                showFx = transition.to ( nextView, { x=0, time=fxTime } )
                showFx = transition.to ( currentView, { x=display.contentWidth*-1, time=fxTime } )
                --
                timer.performWithDelay( fxTime, fxEnded )
                
        -----------------------------------
        -- EFFECT: Over From Right
        -----------------------------------
        
        elseif effect == "overFromRight" then
        
                nextView.x = display.contentWidth
                nextView.y = 0
                --
                loadScene (nextScene)
                --
                showFx = transition.to ( nextView, { x=0, time=fxTime } )
                --
                timer.performWithDelay( fxTime, fxEnded )
                
        -----------------------------------
        -- EFFECT: Move From Left
        -----------------------------------
        
        elseif effect == "moveFromLeft" then
        
                nextView.x = display.contentWidth*-1
                nextView.y = 0
                --
                loadScene (nextScene)
                --
                showFx = transition.to ( nextView, { x=0, time=fxTime } )
                showFx = transition.to ( currentView, { x=display.contentWidth, time=fxTime } )
                --
                timer.performWithDelay( fxTime, fxEnded )
        
        -----------------------------------
        -- EFFECT: Over From Left
        -----------------------------------
        
        elseif effect == "overFromLeft" then
        
                nextView.x = display.contentWidth*-1
                nextView.y = 0
                --
                loadScene (nextScene)
                --
                showFx = transition.to ( nextView, { x=0, time=fxTime } )
                --
                timer.performWithDelay( fxTime, fxEnded )
        
        -----------------------------------
        -- EFFECT: Over From Top
        -----------------------------------
        
        elseif effect == "overFromTop" then
        
                nextView.x = 0
                nextView.y = display.contentHeight*-1
                --
                loadScene (nextScene)
                --
                showFx = transition.to ( nextView, { y=0, time=fxTime } )
                --
                timer.performWithDelay( fxTime, fxEnded )
        
        -----------------------------------
        -- EFFECT: Over From Bottom
        -----------------------------------
        
        elseif effect == "overFromBottom" then
        
                nextView.x = 0
                nextView.y = display.contentHeight
                --
                loadScene (nextScene)
                --
                showFx = transition.to ( nextView, { y=0, time=fxTime } )
                --
                timer.performWithDelay( fxTime, fxEnded )
                
        -----------------------------------
        -- EFFECT: Fade
        -----------------------------------
        -- ARG1 = color [string]
        -----------------------------------
        -- ARG1 = red   [number]
        -- ARG2 = green [number]
        -- ARG3 = blue  [number]
        -----------------------------------
        
        elseif effect == "fade" then
        
                local r, g, b
                --
                if type(arg1) == "nil" then
                        arg1 = "black"
                end
                --
                if string.lower(arg1) == "red" then
                        r=255
                        g=0
                        b=0
                elseif string.lower(arg1) == "green" then
                        r=0
                        g=255
                        b=0
                elseif string.lower(arg1) == "blue" then
                        r=0
                        g=0
                        b=255
                elseif string.lower(arg1) == "yellow" then
                        r=255
                        g=255
                        b=0
                elseif string.lower(arg1) == "pink" then
                        r=255
                        g=0
                        b=255
                elseif string.lower(arg1) == "white" then
                        r=255
                        g=255
                        b=255
                elseif type (arg1) == "number"
                   and type (arg2) == "number"
                   and type (arg3) == "number" then
                        r=arg1
                        g=arg2
                        b=arg3
                else
                        r=0
                        g=0
                        b=0
                end
                --
                nextView.x = display.contentWidth
                nextView.y = 0
                --
                loadScene (nextScene)
                --
                local fade = display.newRect( 0 - display.contentWidth, 0 - display.contentHeight, display.contentWidth * 3, display.contentHeight * 3 )
                fade.alpha = 0
                fade:setFillColor( r,g,b )
                effectView:insert(fade)
                --
                showFx = transition.to ( fade, { alpha=1.0, time=fxTime } )
                --
                timer.performWithDelay( fxTime, fxEnded )
                --
                local function returnFade ( event )
                
                        showFx = transition.to ( fade, { alpha=0, time=fxTime } )
                        --
                        local function removeFade ( event )
                                fade:removeSelf()
                        end
                        --
                        timer.performWithDelay( fxTime, removeFade )
                        
                end
                --
                timer.performWithDelay( fxTime+1, returnFade )
                
        -----------------------------------
        -- EFFECT: Flip
        -----------------------------------
        
        elseif effect == "flip" then
        
                showFx = transition.to ( currentView, { xScale=0.001, time=fxTime } )
                showFx = transition.to ( currentView, { x=display.contentWidth*0.5, time=fxTime } )
                --
                loadScene (nextScene)
                --
                nextView.xScale=0.001
                nextView.x=display.contentWidth*0.5
                --
                showFx = transition.to ( nextView, { xScale=1, delay=fxTime, time=fxTime } )
                showFx = transition.to ( nextView, { x=0, delay=fxTime, time=fxTime } )
                --
                timer.performWithDelay( fxTime*2, fxEnded )
                
        -----------------------------------
        -- EFFECT: Down Flip
        -----------------------------------
        
        elseif effect == "downFlip" then
        
                showFx = transition.to ( currentView, { xScale=0.7, time=fxTime } )
                showFx = transition.to ( currentView, { yScale=0.7, time=fxTime } )
                showFx = transition.to ( currentView, { x=display.contentWidth*0.15,  time=fxTime } )
                showFx = transition.to ( currentView, { y=display.contentHeight*0.15, time=fxTime } )
                showFx = transition.to ( currentView, { xScale=0.001, delay=fxTime, time=fxTime } )
                showFx = transition.to ( currentView, { x=display.contentWidth*0.5, delay=fxTime, time=fxTime } )
                --
                loadScene (nextScene)
                --
                nextView.x = display.contentWidth*0.5
                nextView.xScale=0.001
                nextView.yScale=0.7
                nextView.y=display.contentHeight*0.15
                --
                showFx = transition.to ( nextView, { x=display.contentWidth*0.15, delay=fxTime*2, time=fxTime } )
                showFx = transition.to ( nextView, { xScale=0.7, delay=fxTime*2, time=fxTime } )
                showFx = transition.to ( nextView, { xScale=1, delay=fxTime*3, time=fxTime } )
                showFx = transition.to ( nextView, { yScale=1, delay=fxTime*3, time=fxTime } )
                showFx = transition.to ( nextView, { x=0, delay=fxTime*3, time=fxTime } )
                showFx = transition.to ( nextView, { y=0, delay=fxTime*3, time=fxTime } )
                --
                timer.performWithDelay( fxTime*4, fxEnded )
                
        -----------------------------------
        -- EFFECT: None
        -----------------------------------
        
        else
                timer.performWithDelay( 0, fxEnded )
                loadScene (nextScene)
        end
        
        return true
end

ricardorauber
User offline. Last seen 48 weeks 5 days ago. Offline
Joined: 30 Aug 2010

Again, I'm far from my mac, so I didn't test it. Sorry if it doesn't work but as soon as possible, I'll work on it and a few new features.

spjnowak
User offline. Last seen 1 week 9 hours ago. Offline
Joined: 21 Aug 2009

Thanks for your prompt reply, Ricardo! I've tried your modified code (and also moved my touch handlers out of director.lua to avoid confusion). It certainly is a lot more stable although I can still manage to occasionally create the error with rapid page changes. However, the error no longer crashes Corona but just messes up the page display, leaving some elements from the previous page on screen.

I will continue testing my app to see if I still have this problem.

ricardorauber
User offline. Last seen 48 weeks 5 days ago. Offline
Joined: 30 Aug 2010

spjnowak

If any object stays in the screen is because you may have forgotten to insert it in the localGroup.

Here is an ideia, when you call the changeScene, remove the eventListeners that call it before then changeScene so it will work only once.

spjnowak
User offline. Last seen 1 week 9 hours ago. Offline
Joined: 21 Aug 2009

Hi Ricardo

All my objects are definitely in the localGroup and things only get left behind when the error occurs. It looks as though the previous scene group has not been deleted.

Your idea of removing the event listener before calling the changeScene is worth a try but I will have to think about where to add it again.

Thanks

Stefan

gammabeam
User offline. Last seen 3 years 3 weeks ago. Offline
Joined: 9 Nov 2010

Hey! Awesome class, Ricardo!

I have a question: Should I place the functions I create for the scene INSIDE the "function new()"?

I've always heard that creating functions inside of functions is kinda bad, or at least not encouraged... So, what's your take on this?!

ricardorauber
User offline. Last seen 48 weeks 5 days ago. Offline
Joined: 30 Aug 2010

spjnowak

If you still have the problem, let us know!

gammabeam

No, no! You can put outside the new() function, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
local localGroup
 
local function a ()
...
end
 
local function x ()
...
end
 
function new( )
  localGroup.insert(var1)
  localGroup.insert(var2)
  localGroup.insert(var3)
  return localGroup
end

dweezil
User offline. Last seen 24 weeks 2 days ago. Offline
Joined: 23 Sep 2010

So what should be inside the new function?

gammabeam
User offline. Last seen 3 years 3 weeks ago. Offline
Joined: 9 Nov 2010

That is great! Thanks a lot Ricardo!

However, I noticed that taking the functions out of the "new()" is returning me a nil value when I try to insert things inside the "localGroup". What could be the best way to solve this? (I am putting the functions before the new)

spjnowak
User offline. Last seen 1 week 9 hours ago. Offline
Joined: 21 Aug 2009

Hi Ricardo

I've removed the touch event listener before calling the changeScene as you suggested then added it back at the end of the new function for the next scene. That seems to work OK and I'm not getting errors in the simulator but I can still crash the device if I swipe to navigate very quickly. Not being able to debug the Corona code on the device makes it hard to work out what's going wrong!

I'm checking texture memory to see that I'm not leaking anything between scenes and that seems to be OK.

Thanks for you help so far!

Stefan

ricardorauber
User offline. Last seen 48 weeks 5 days ago. Offline
Joined: 30 Aug 2010

I just forgot the "return localGroup" but it's now there.

dweezil
User offline. Last seen 24 weeks 2 days ago. Offline
Joined: 23 Sep 2010

So what should be inside the new function?