Inheriting a display object?

This is a question in regards to Jonathan Beebe’s Tutorial: Modular Classes in Corona Blog (awesome by the way). Here is my question.

I have the following code, and it returns nil, nil when I ask dog1:rollOver(). I want it to print that instance’s x and y position (10, 10). Can someone tell me what’s wrong? I suspect it has something to do with setmetatable and display objects in Corona. Thanks!

dog.lua [code]
local dog = {}
local dog_mt = { __index = dog }

function dog.new( x, y, width, height )
local newDog = display.newRect( x, y, width, height )
return setmetatable( newDog, dog_mt )
end

function dog:rollOver()
print( self.x, self.y )
end

return dog[/code]

main.lua local dog = require 'dog' local dog1 = dog.new(10,10,10,10) dog1:rollOver() -- prints nil, nil? [import]uid: 7721 topic_id: 16262 reply_id: 316262[/import]

I can close this already! I knew I saw this somewhere!

Display Objects vs. Tables: A Technical Discussion

At the beginning of this section, we described display objects as behaving “similarly” to Lua tables. An exception to this rule is that you cannot set the metatables of display objects. This is due to the nature of our C++/Lua integration in this particular case.

Corona display objects are native C++ objects internally. The way Lua implements binding native pointers is with “userdata”, which sets the metatable. Userdata metatables cannot be replaced by Lua code; this is a measure defined by Lua (see Programming in Lua for details).

Normally, native objects bound to Lua in this way do not behave like tables, and there are a number of examples in Corona of this. Such objects may have properties and/or methods, but are not extensible. In contrast, Corona display objects do behave like tables — this is a convenience feature, and the alternative would have been having a field for “user data”, allowing you to access your own data associated with a display object. Raising this functionality directly into the display object itself is simpler and more extensible for the end user, which is why this was done this way. So the underlying native bridge is constrained by what is allowed in Lua, and we view this architectural choice as a fundamental design feature of Corona. Ultimately, Corona is a C++/OpenGL engine with frameworks designed for rapid development, and the product’s premise is that these advantages justify this use of metatables on the display objects. [import]uid: 7721 topic_id: 16262 reply_id: 60537[/import]

Oh yeah, I figure I should post my “work around” to my problem! I decided to get rid of metatables all together but still wanted stay away from LUA’s module() function like Beebe suggested. So here is my functions-in-function work around!

dog.lua [code]local dog = {}

function dog.new( x, y, width, height )
local newDog = display.newRect( x, y, width, height )

function newDog:rollOver()
print( self.x, self.y )
end

return newDog
end

return dog[/code]

main.lua local dog = require 'dog' local dog1 = dog.new(10,10,10,10) dog1:rollOver() -- prints 15, 15 [import]uid: 7721 topic_id: 16262 reply_id: 60542[/import]

It is possible to assign a metatable to Corona display objects by using a proxy table as detailed in

http://tekarak.blogspot.com/2010/08/displayobject-inheritance-in-corona.html

[import]uid: 26397 topic_id: 16262 reply_id: 60996[/import]

Nice solution jonlaidler! I will have to break it down, so maybe I can understand it. Here is the code you have provided from the blog link.

[lua]function makeProxy(params)
– Create image
local t = display.newImage(params.background)
local _t = t

– create proxy
local t = {}

– create metatable
local mt = {
__index = function (t,k)
print("*access to element " … tostring(k))
return _t[k] – access the original table
end,
__newindex = function (t,k,v)
print("*update of element " … tostring(k) …
" to " … tostring(v))
_t[k] = v – update original table
end
}
setmetatable(t, mt)

function t.bar()
print(“bar on you!”)
end

return t
end

aPuppy = makeProxy({background=“puppy.jpeg”})
aPuppy.y = 200

function aPuppy:foo()
print(“foo on you!”)
end

aPuppy:foo()
aPuppy:bar()[/lua]

Sort of blowing my mind, but at the same time, Complex solution I think? So when we call makeProxy, we make an image display object. We then make a empty table t and metatable where it’s __index is a function that returns a value from our display object, and where __newindex assigns a value to our display object. It seems that we are still only metatabling (is that word?) t which is a table with function foo, but sort of “side referencing” or “proxying” the display object. Nice solution. I haven’t thoroughly tested it, but I assume it works just as good as if setting a metatable on a display object was possible. Cool. [import]uid: 7721 topic_id: 16262 reply_id: 61766[/import]

Alright, rewrote some of our favorite classes for module-less usage. Use at your own risk!

director.lua by Ricardo Rauber Pereira, v1.4m[lua]–====================================================================–
– DIRECTOR CLASS
–====================================================================–

–[[

******************

  • INFORMATION
    ******************

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

  • Please take a look on the template.lua file and don’t forget to always
    insert your display objects into the localGroup.

  • If you like Director Class, please help us donating at my blog, so I could
    keep doing it for free. http://rauberlabs.blogspot.com/

******************

  • HISTORY
    ******************

  • 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;

  • 14-NOV-2010 - Ricardo Rauber - Bux fixes and new getScene function to get
    the name of the active scene(lua file);

  • 14-FEB-2011 - Ricardo Rauber - General Bug Fixes;

  • 26-APR-2011 - Ricardo Rauber - cleanGroups() changed; Added Pop Up;

  • 21-JUN-2011 - Ricardo Rauber - Added error control; cleanGroups() removed;
    Added touch protection; loadScene() changed;
    Effects improved; Send Parameters; Bug fixes;

  • 28-JUN-2011 - Ricardo Rauber - Added Books;

  • 05-JUL-2011 - Ricardo Rauber - Added rebuildGroup(), initVars() and Toggle Degug;

  • 21-JUL-2011 - Ricardo Rauber - Search for missed objects to insert into the
    right group and prevent keeping on the screen;

  • 25-AUG-2011 - Ricardo Rauber - Bug fixes;

– ]]

local director = {}
local loaded = {} – Will use this table in lieu of package.loaded

print( “-----------------------------------------------” )

–====================================================================–
– TOGGLE DEBUG
–====================================================================–

director.showDebug = true
–showDebug = false

–====================================================================–
– CONTENT INFO
–====================================================================–

local _W = display.contentWidth
local _H = display.contentHeight

–====================================================================–
– DISPLAY GROUPS
–====================================================================–

director.directorView = display.newGroup()
local directorView = director.directorView

local currView = display.newGroup()
local prevView = display.newGroup()
local nextView = display.newGroup()
local protectionView = display.newGroup()
local popupView = display.newGroup()
local effectView = display.newGroup()

local initViews = function()
directorView:insert( currView )
directorView:insert( prevView )
directorView:insert( nextView )
directorView:insert( protectionView )
directorView:insert( popupView )
directorView:insert( effectView )
end

–====================================================================–
– VARIABLES
–====================================================================–

local prevScreen, currScreen, nextScreen, popupScreen
local prevScene, currScene, nextScene, popupScene = “main”, “main”, “main”, “main”
local newScene
local fxTime = 200
local safeDelay = 50
local isChangingScene = false
local popUpOnClose
local isBook = false
local bookPages = {}
local currBookPage = 1
local moveBookPage

prevView.x = -_W
prevView.y = 0
currView.x = 0
currView.y = 0
nextView.x = _W
nextView.y = 0
popupView.x = 0
popupView.y = 0

–====================================================================–
– GET COLOR
–====================================================================–

local getColor = function( strValue1, strValue2, strValue3 )


– Variables

local r, g, b


– Test Parameters

if type( strValue1 ) == “nil” then
strValue1 = “black”
end


– Find Color

if string.lower( tostring( strValue1 ) ) == “red” then
r = 255
g = 0
b = 0
elseif string.lower( tostring( strValue1 ) ) == “green” then
r = 0
g = 255
b = 0
elseif string.lower( tostring( strValue1 ) ) == “blue” then
r = 0
g = 0
b = 255
elseif string.lower( tostring( strValue1 ) ) == “yellow” then
r = 255
g = 255
b = 0
elseif string.lower( tostring( strValue1 ) ) == “pink” then
r = 255
g = 0
b = 255
elseif string.lower( tostring( strValue1 ) ) == “white” then
r = 255
g = 255
b = 255
elseif type( strValue1 ) == “number”
and type( strValue2 ) == “number”
and type( strValue3 ) == “number” then
r = strValue1
g = strValue2
b = strValue3
else
r = 0
g = 0
b = 0
end


– Return

return r, g, b

end

–====================================================================–
– SHOW ERRORS
–====================================================================–

local showError = function( errorMessage, debugMessage )
local message = "Director ERROR: " … tostring( errorMessage )
local function onComplete( event )
print()
print( “-----------------------” )
print( message )
print( “-----------------------” )
print( debugMessage )
print( “-----------------------” )
error()
end

if director.showDebug then
local alert = native.showAlert( “Director Class - ERROR”, message, { “OK” }, onComplete )
else
onComplete()
end
end

–====================================================================–
– GARBAGE COLLECTOR
–====================================================================–

local garbageCollect = function( event )
collectgarbage( “collect” )
end

–====================================================================–
– IS DISPLAY OBJECT ?
–====================================================================–

local coronaMetaTable = getmetatable( display.getCurrentStage() )

local isDisplayObject = function( aDisplayObject )
return( type( aDisplayObject ) == “table” and getmetatable( aDisplayObject ) == coronaMetaTable )
end

–====================================================================–
– PROTECTION
–====================================================================–


– Rectangle

local protection = display.newRect( -_W, -_H, _W * 3, _H * 3 )
protection:setFillColor( 255, 255, 255 )
protection.alpha = 0.01
protection.isVisible = false
protectionView:insert( protection )


– Listener

local fncProtection = function( event )
return true
end
protection:addEventListener( “touch”, fncProtection )
protection:addEventListener( “tap”, fncProtection )

–====================================================================–
– CHANGE CONTROLS
–====================================================================–


– Effects Time

function director:changeFxTime( newFxTime )
if type( newFxTime ) == “number” then
fxTime = newFxTime
end
end


– Safe Delay

function director:changeSafeDelay( newSafeDelay )
if type( newSafeDelay ) == “number” then
safeDelay = newSafeDelay
end
end

–====================================================================–
– GET SCENES
–====================================================================–


– Previous

function director:getPrevScene()
return prevScene
end


– Current

function director:getCurrScene()
return currScene
end


– Next

function director:getNextScene()
return nextScene
end

–====================================================================–
– REBUILD GROUP
–====================================================================–

local rebuildGroup = function( target )


– Verify which group

– Prev
if target == “prev” then


– Clean Group

prevView:removeSelf()


– Search for the localGroup.clean() function

if prevScreen then
if prevScreen.clean then


– Clean Object

local handler, message = pcall( prevScreen.clean )

if not handler then
showError( “Failed to clean object '” … prevScene … “’ - Please verify the localGroup.clean() function.”, message )
return false
end

end

end


– Create Group

prevView = display.newGroup()

– Curr
elseif target == “curr” then


– Clean Group

currView:removeSelf()


– Search for the localGroup.clean() function

if currScreen then
if currScreen.clean then


– Clean Object

local handler, message = pcall( currScreen.clean )

if not handler then
showError( “Failed to clean object '” … currScene … “’ - Please verify the localGroup.clean() function.”, message )
return false
end

end

end


– Create Group

currView = display.newGroup()

– Next
elseif target == “next” then


– Clean Group

nextView:removeSelf()


– Search for the localGroup.clean() function

if nextScreen then
if nextScreen.clean then


– Clean Object

local handler, message = pcall( nextScreen.clean )

if not handler then
showError( “Failed to clean object '” … nextScene … “’ - Please verify the localGroup.clean() function.”, message )
return false
end

end

end


– Create Group

nextView = display.newGroup()

– Popup
elseif target == “popup” then


– Clean Group

popupView:removeSelf()


– Search for the localGroup.clean() function

if popupScreen then
if popupScreen.clean then


– Clean Object

local handler, message = pcall( popupScreen.clean )

if not handler then
showError( “Failed to clean object '” … popupScene … “’ - Please verify the localGroup.clean() function.”, message )
return false
end

end

end


– Create Group

popupView = display.newGroup()

end


– Init

initViews()

end

–====================================================================–
– INITIATE VARIABLES
–====================================================================–

local initVars = function( target )


– Verify which group

– Prev
if target == “prev” then


– Search for the localGroup.initVars() function

if prevScreen then
if prevScreen.initVars then


– Init Vars

local handler, message = pcall( prevScreen.initVars )

if not handler then
showError( “Failed to initiate variables of object '” … prevScene … “’ - Please verify the localGroup.initVars() function.”, message )
return false
end

end
end

– Curr
elseif target == “curr” then


– Search for the localGroup.initVars() function

if currScreen then
if currScreen.initVars then


– Init Vars

local handler, message = pcall( currScreen.initVars )

if not handler then
showError( “Failed to initiate variables of object '” … currScene … “’ - Please verify the localGroup.initVars() function.”, message )
return false
end

end

end

– Next
elseif target == “next” then


– Search for the localGroup.initVars() function

if nextScreen then
if nextScreen.initVars then


– Init Vars

local handler, message = pcall( nextScreen.initVars )

if not handler then
showError( “Failed to initiate variables of object '” … nextScene … “’ - Please verify the localGroup.initVars() function.”, message )
return false
end

end

end

– Popup
elseif target == “popup” then


– Search for the localGroup.initVars() function

if popupScreen then
if popupScreen.initVars then


– Init Vars

local handler, message = pcall( popupScreen.initVars )

if not handler then
showError( “Failed to initiate variables of object '” … popupScene … “’ - Please verify the localGroup.initVars() function.”, message )
return false
end

end

end

end

end

–====================================================================–
– UNLOAD SCENE
–====================================================================–

local unloadScene = function( moduleName )


– Test parameters

if moduleName ~= “main” then


– Verify if it was loaded

if type( loaded[moduleName] ) ~= “nil” then


– Search for the clean() function

if loaded[moduleName].clean then


– Execute

local handler, message = pcall( loaded[moduleName].clean )

if not handler then
showError( “Failed to clean module '” … moduleName … “’ - Please verify the clean() function.”, message )
return false
end

end


– Try to free memory

loaded[moduleName] = nil

local function garbage( event )
garbageCollect()
end

timer.performWithDelay( fxTime, garbage )

end

end

end

–====================================================================–
– LOAD SCENE
–====================================================================–

local loadScene = function( moduleName, target, params )


– Test parameters

if type( moduleName ) ~= “string” then
showError( "Module name must be a string. moduleName = " … tostring( moduleName ) )
return false
end


– Load Module

if not loaded[moduleName] then
local handler, message = pcall( require, moduleName )
if not handler then
showError( “Failed to load module '” … moduleName … “’ - Please check if the file exists and it is correct.”, message )
return false
else
loaded[moduleName] = message
end
end


– Serach new() Function

if not loaded[moduleName].new then
showError( “Module '” … tostring( moduleName ) … “’ must have a new() function.” )
return false
end

local functionName = loaded[moduleName].new


– Variables

local handler


– Load choosed scene

– Prev
if string.lower( target ) == “prev” then


– Rebuild Group

rebuildGroup( “prev” )


– Unload Scene

if prevScene ~= currScene and prevScene ~= nextScene then
unloadScene( moduleName )
end


– Start Scene

handler, prevScreen = pcall( functionName, params )

if not handler then
showError( “Failed to execute new( params ) function on '” … tostring( moduleName ) … “’.”, prevScreen )
return false
end


– Check if it returned an object

if not isDisplayObject( currScreen ) then
showError( “Module " … moduleName … " must return a display.newGroup().” )
return false
end


– Books Touch Area

local bookBackground = display.newRect( 0, 0, _W, _H )
bookBackground.alpha = 0.01
bookBackground:addEventListener( “touch”, moveBookPage )
prevView:insert( bookBackground )


– Finish

prevView:insert( prevScreen )
prevScene = moduleName


– Initiate Variables

initVars( “prev” )

– Curr
elseif string.lower( target ) == “curr” then


– Rebuild Group

rebuildGroup( “curr” )


– Unload Scene

if prevScene ~= currScene and currScene ~= nextScene then
unloadScene( moduleName )
end


– Start Scene

handler, currScreen = pcall( functionName, params )

if not handler then
showError( “Failed to execute new( params ) function on '” … tostring( moduleName ) … “’.”, currScreen )
return false
end


– Check if it returned an object

if not isDisplayObject( currScreen ) then
showError( “Module " … moduleName … " must return a display.newGroup().” )
return false
end


– Books Touch Area

local bookBackground = display.newRect( 0, 0, _W, _H )
bookBackground.alpha = 0.01
bookBackground:addEventListener( “touch”, moveBookPage )
currView:insert( bookBackground )


– Finish

currView:insert( currScreen )
currScene = moduleName


– Initiate Variables

initVars( “curr” )

– Next
else


– Rebuild Group

rebuildGroup( “next” )


– Unload Scene

if prevScene ~= nextScene and currScene ~= nextScene then
unloadScene( moduleName )
end


– Start Scene

handler, nextScreen = pcall( functionName, params )

if not handler then
showError( “Failed to execute new( params ) function on '” … tostring( moduleName ) … “’.”, nextScreen )
return false
end


– Check if it returned an object

if not isDisplayObject( nextScreen ) then
showError( “Module " … moduleName … " must return a display.newGroup().” )
return false
end


– Books Touch Area

local bookBackground = display.newRect( 0, 0, _W, _H )
bookBackground.alpha = 0.01
bookBackground:addEventListener( “touch”, moveBookPage )
nextView:insert( bookBackground )


– Finish

nextView:insert( nextScreen )
nextScene = moduleName


– Initiate Variables

initVars( “next” )

end


– Return

return true

end


– Load prev screen

local loadPrevScene = function( moduleName, params )
loadScene( moduleName, “prev”, params )
prevView.x = -_W
end


– Load curr screen

local loadCurrScene = function( moduleName, params )
loadScene( moduleName, “curr”, params )
currView.x = 0
end


– Load next screen

local loadNextScene = function( moduleName, params )
loadScene( moduleName, “next”, params )
nextView.x = _W
end

–====================================================================–
– EFFECT ENDED
–====================================================================–

local fxEnded = function( event )


– Reset current view

currView.x = 0
currView.y = 0
currView.xScale = 1
currView.yScale = 1


– Rebuild Group

rebuildGroup( “curr” )


– Unload scene

if currScene ~= nextScene then
unloadScene( currScene )
end


– Next -> Current

currScreen = nextScreen
currScene = newScene
currView:insert( currScreen )


– Reset next view

nextView.x = _W
nextView.y = 0
nextView.xScale = 1
nextView.yScale = 1
nextScene = “main”
nextScreen = nil


– Finish

isChangingScene = false
protection.isVisible = false


– Return

return true

end

–====================================================================–
– CHANGE SCENE
–====================================================================–

function director:changeScene( params,
nextLoadScene,
effect,
arg1,
arg2,
arg3 )


– If is changing scene, return without do anything

if isChangingScene then
return true
else
isChangingScene = true
end


– If is book, return without do anything

if isBook then
return true
end


– Test parameters

if type( params ) ~= “table” then
arg3 = arg2
arg2 = arg1
arg1 = effect
effect = nextLoadScene
nextLoadScene = params
params = nil
end

if type( nextLoadScene ) ~= “string” then
showError( "The scene name must be a string. scene = " … tostring( nextLoadScene ) )
return false
end


– If is popup, don’t change

if popupScene ~= “main” then
showError( “Could not change scene inside a popup.” )
return false
end


– Verify objects on current stage

local currentStage = display.getCurrentStage()

for i = currentStage.numChildren, 1, -1 do


– Verify directorId

if type( currentStage[i].directorId ) == “nil” then
currentStage[i].directorId = currScene
end


– Insert into the CURR group if it’s needed

if currentStage[i].directorId == currScene
and currentStage[i].directorId ~= “main” then
currScreen:insert( currentStage[i] )
end

end


– Prevent change to main

if nextLoadScene == “main” then
return true
end


– Protection

protection.isVisible = true


– Variables

newScene = nextLoadScene
local showFx


– Load Scene

loadNextScene( newScene, params )


– Verify objects on current stage

for i = currentStage.numChildren, 1, -1 do


– Verify directorId

if type( currentStage[i].directorId ) == “nil” then
currentStage[i].directorId = newScene
end


– Insert into the NEXT group if it’s needed

if currentStage[i].directorId == newScene
and currentStage[i].directorId ~= “main” then
nextScreen:insert( currentStage[i] )
end

end


– EFFECT: Move From Right

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 } )


– EFFECT: Over From Right

elseif effect == “overFromRight” then

nextView.x = _W
nextView.y = 0

showFx = transition.to( nextView, { x = 0, time = fxTime, onComplete = fxEnded } )


– EFFECT: Move From Left

elseif effect == “moveFromLeft” 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 } )


– EFFECT: Over From Left

elseif effect == “overFromLeft” then

nextView.x = -_W
nextView.y = 0

showFx = transition.to( nextView, { x = 0, time = fxTime, onComplete = fxEnded } )


– EFFECT: Move From Top

elseif effect == “moveFromTop” then

nextView.x = 0
nextView.y = -_H

showFx = transition.to( nextView, { y = 0, time = fxTime } )
showFx = transition.to( currView, { y = _H, time = fxTime, onComplete = fxEnded } )


– EFFECT: Over From Top

elseif effect == “overFromTop” then

nextView.x = 0
nextView.y = -_H

showFx = transition.to( nextView, { y = 0, time = fxTime, onComplete = fxEnded } )


– EFFECT: Move From Bottom

elseif effect == “moveFromBottom” then

nextView.x = 0
nextView.y = _H

showFx = transition.to( nextView, { y = 0, time = fxTime } )
showFx = transition.to( currView, { y = -_H, time = fxTime, onComplete = fxEnded } )


– EFFECT: Over From Bottom

elseif effect == “overFromBottom” then

nextView.x = 0
nextView.y = _H

showFx = transition.to( nextView, { y = 0, time = fxTime, onComplete = fxEnded } )


– EFFECT: Crossfade

elseif effect == “crossfade” then

nextView.x = 0
nextView.y = 0

nextView.alpha = 0

showFx = transition.to( currView, { alpha = 0, time = fxTime * 2 } )
showFx = transition.to( nextView, { alpha = 1, time = fxTime * 2, onComplete = fxEnded } )


– EFFECT: Fade

– ARG1 = color [string]

– ARG1 = red [number]
– ARG2 = green [number]
– ARG3 = blue [number]

elseif effect == “fade” then

nextView.x = _W
nextView.y = 0

local fade = display.newRect( -_W, -_H, _W * 3, _H * 3 )
fade.alpha = 0
fade:setFillColor( getColor( arg1, arg2, arg3 ) )
effectView:insert( fade )

local function returnFade( event )
currView.x = _W
nextView.x = 0

local function removeFade( event )
fade:removeSelf()
fxEnded()
end

showFx = transition.to( fade, { alpha = 0, time = fxTime, onComplete = removeFade } )
end

showFx = transition.to( fade, { alpha = 1.0, time = fxTime, onComplete = returnFade } )


– EFFECT: Flip

elseif effect == “flip” then

nextView.xScale = 0.001

nextView.x = _W / 2

local phase1, phase2

showFx = transition.to( currView, { xScale = 0.001, time = fxTime } )
showFx = transition.to( currView, { x = _W / 2, time = fxTime } )

phase1 = function( e )
showFx = transition.to( nextView, { xScale = 0.001, x = _W / 2, time = fxTime, onComplete = phase2 } )
end

phase2 = function( e )
showFx = transition.to( nextView, { xScale = 1, x = 0, time = fxTime, onComplete = fxEnded } )
end

showFx = transition.to( nextView, { time = 0, onComplete = phase1 } )


– EFFECT: Down Flip

elseif effect == “downFlip” then

nextView.x = _W / 2
nextView.y = _H * 0.15

nextView.xScale = 0.001
nextView.yScale = 0.7

local phase1, phase2, phase3, phase4

phase1 = function( e )
showFx = transition.to( currView, { xScale = 0.7, yScale = 0.7, x = _W * 0.15, y = _H * 0.15, time = fxTime, onComplete = phase2 } )
end

phase2 = function( e )
showFx = transition.to( currView, { xScale = 0.001, x = _W / 2, time = fxTime, onComplete = phase3 } )
end

phase3 = function( e )
showFx = transition.to( nextView, { x = _W * 0.15, xScale = 0.7, time = fxTime, onComplete = phase4 } )
end

phase4 = function( e )
showFx = transition.to( nextView, { xScale = 1, yScale = 1, x = 0, y = 0, time = fxTime, onComplete = fxEnded } )
end

showFx = transition.to( currView, { time = 0, onComplete = phase1 } )


– EFFECT: None

else
timer.performWithDelay( safeDelay, fxEnded )
end


– Return

return true

end

–====================================================================–
– OPEN POPUP
–====================================================================–

function director:openPopUp( params, newPopUpScene, onClose )


– Test parameters

if type( params ) ~= “table” then
onClose = newPopUpScene
newPopUpScene = params
params = nil
end

if type( newPopUpScene ) ~= “string” then
showError( "Module name must be a string. moduleName = " … tostring( newPopUpScene ) )
return false
end

if type( onClose ) == “function” then
popUpOnClose = onClose
else
popUpOnClose = nil
end


– Test scene name

if newPopUpScene == currScene
or newPopUpScene == nextScene
or newPopUpScene == “main”
then
return false
end


– If is inside a popup, don’t load

if popupScene ~= “main” then
showError( “Could not load more then 1 popup.” )
return false
end


– Rebuild Group

rebuildGroup( “popup” )


– Unload Scene

unloadScene( newPopUpScene )
popupScene = “main”
popupScreen = nil


– Load scene

local handler, message = pcall( require, newPopUpScene )

if not handler then
showError( “Failed to load module '” … newPopUpScene … “’ - Please check if the file exists and it is correct.”, message )
return false
else
loaded[newPopUpScene] = message
end


– Serach for new() function

if not loaded[newPopUpScene].new then
showError( “Module '” … tostring( newPopUpScene ) … “’ must have a new() function.” )
return false
end


– Execute new() function

local functionName = loaded[newPopUpScene].new

handler, popupScreen = pcall( functionName, params )

if not handler then
showError( “Failed to execute news( params ) function on '” … tostring( moduleName ) … “’.”, popupScreen )
return false
end


– Test if a group was returned

if not isDisplayObject( currScreen ) then
showError( “Module " … moduleName … " must return a display.newGroup().” )
return false
end

popupView:insert( popupScreen )
popupScene = newPopUpScene


– Initiate Variables

initVars( “popup” )


– Protection

protection.isVisible = true


– Return

return true

end

–====================================================================–
– CLOSE POPUP
–====================================================================–

function director:closePopUp()


– If no popup is loaded, don’t do anything

if popupScene == “main” then
return true
end


– Rebuild Group

rebuildGroup( “popup” )


– Unload Scene

unloadScene( newPopUpScene )
popupScene = “main”
popupScreen = nil


– Protection

protection.isVisible = false


– Call function

if type( popUpOnClose ) == “function” then
popUpOnClose()
end


– Return

return true

end

–====================================================================–
– VERIFY IF IS BOOK
–====================================================================–

function director:isBook()
return isBook
end

–====================================================================–
– GET CURRENT PAGE NAME
–====================================================================–

getCurrBookPage = function()
if bookPages[currBookPage] then
return bookPages[currBookPage]
end
end

–====================================================================–
– GET CURRENT PAGE NUMBER
–====================================================================–

getCurrBookPageNum = function()
return currBookPage
end

–====================================================================–
– GET PAGE COUNT
–====================================================================–

getBookPageCount = function()
return table.maxn( bookPages )
end

–====================================================================–
– INSERT PAGES
–====================================================================–

function director:newBookPages( pageList, fade )


– If is not book, return without do anything

if not isBook then
return true
end


– Test parameters

if type( pageList ) ~= “table” then
return true
end


– Clean it

while getBookPageCount() > 0 do
table.remove( bookPages )
end


– Mount

local i = 1
while pageList[i] do
if type( pageList[i] ) == “table” then
bookPages[i] = pageList[i]
i = i + 1
end
end


– Fade

local fadeFx = display.newRect( 0, 0, _W, _H )
fadeFx:setFillColor( 0, 0, 0 )
fadeFx.alpha = 0
fadeFx.isVisible = false
effectView:insert( fadeFx )

local fxEnded = function( e )


– Remove Fade

fadeFx:removeSelf()
fadeFx = nil

end


– Go to page

local change = function( e )


– Rebuild Group

rebuildGroup( “prev” )


– Unload Scene

if prevScene ~= bookPages[1] and prevScene ~= bookPages[2] then
unloadScene( prevScene )
end

prevScene = “main”


– Rebuild Group

rebuildGroup( “next” )


– Unload Scene

if nextScene ~= bookPages[1] and nextScene ~= bookPages[2] then
unloadScene( nextScene )
end

nextScene = “main”


– Load pages 1 and 2

if bookPages[1] then
loadCurrScene( bookPages[1].page, bookPages[1].params )
currBookPage = 1
end

if bookPages[2] then
loadNextScene( bookPages[2].page, bookPages[2].params )
end


– Search for the localGroup.start() function

if currScreen then
if currScreen.start then


– Start Page

local handler, message = pcall( currScreen.start )

if not handler then
showError( “Failed to start page of object '” … currScene … “’ - Please verify the localGroup.start() function.”, message )
return false
end

end
end


– Complete Fade

local showFx = transition.to( fadeFx, { alpha = 0, time = fxTime, onComplete = fxEnded } )

end


– Execute Fade

if fade then
fadeFx.isVisible = true
local showFx = transition.to( fadeFx, { alpha = 1, time = fxTime, onComplete = change } )
else
change()
end

end

–====================================================================–
– CHANGE BOOK PAGE
–====================================================================–

function director:changeBookPage( target )


– If is not book, return without do anything

if not isBook then
return true
end


– If is changing scene, return without do anything

if isChangingScene then
return true
else
isChangingScene = true
end


– Test parameters

if type( target ) == nil then
return true
end

if string.lower( target ) == “next” then
if getCurrBookPageNum() + 1 < getBookPageCount() then
target = getCurrBookPageNum() + 1
else
target = getBookPageCount()
end
elseif string.lower( target ) == “prev” then
if getCurrBookPageNum() - 1 > 1 then
target = getCurrBookPageNum() - 1
else
target = 1
end
end

if not type( target ) == “number” then
return true
end

local showFx


– Prevent page -1

if target < 1 or
target > getBookPageCount() or
target == getCurrBookPageNum() then

local bookFxEnded = function( event )
isChangingScene = false
end

showFx = transition.to( prevView, { x = -_W, time = fxTime, transition = easing.outQuad } )
showFx = transition.to( currView, { x = 0, time = fxTime, transition = easing.outQuad } )
showFx = transition.to( nextView, { x = _W, time = fxTime, transition = easing.outQuad, onComplete = bookFxEnded } )

else


– Animate to the correct side

– Moved left
if target > getCurrBookPageNum() then

local bookFxEnded = function( event )


– Rebuild Group

rebuildGroup( “prev” )


– Unload Scene

if prevScene ~= currScene and prevScene ~= nextScene then
unloadScene( prevScene )
end


– Curr -> Prev

prevScreen = currScreen
prevScene = currScene
prevView:insert( prevScreen )
prevView.x = -_W


– Initiate Variables

initVars( “prev” )


– Next -> Curr

currScreen = nextScreen
currScene = nextScene
currView:insert( currScreen )
currView.x = 0

nextScreen = nil


– Load Scene

if bookPages[target + 1] then
loadNextScene( bookPages[target + 1].page, bookPages[target + 1].params )
end

nextView.x = _W


– Finish

currBookPage = target
isChangingScene = false


– Search for the localGroup.start() function

if currScreen then
if currScreen.start then


– Start Page

local handler, message = pcall( currScreen.start )

if not handler then
showError( “Failed to start page of object '” … currScene … “’ - Please verify the localGroup.start() function.”, message )
return false
end

end
end

end

showFx = transition.to( prevView, { x = -_W, time = fxTime, transition = easing.outQuad } )
showFx = transition.to( currView, { x = -_W, time = fxTime, transition = easing.outQuad } )
showFx = transition.to( nextView, { x = 0, time = fxTime, transition = easing.outQuad, onComplete = bookFxEnded } )

– Moved right
else

local bookFxEnded = function( event )


– Rebuild Group

rebuildGroup( “next” )


– Unload Scene

if prevScene ~= nextScene and currScene ~= nextScene then
unloadScene( nextScene )
end


– Curr -> Next

nextScreen = currScreen
nextScene = currScene
nextView:insert( nextScreen )
nextView.x = _W


– Initiate Variables

initVars( “next” )


– Prev -> Curr

currScreen = prevScreen
currScene = prevScene
currView:insert( currScreen )
currView.x = 0

prevScreen = nil


– Load Scene

if bookPages[target - 1] then
loadPrevScene( bookPages[target - 1].page, bookPages[target - 1].params )
end

prevView.x = -_W


– Finish

currBookPage = target
isChangingScene = false


– Search for the localGroup.start() function

if currScreen then
if currScreen.start then


– Start Page

local handler, message = pcall( currScreen.start )

if not handler then
showError( “Failed to start page of object '” … currScene … “’ - Please verify the localGroup.start() function.”, message )
return false
end

end
end

end

showFx = transition.to( prevView, { x = 0, time = fxTime, transition = easing.outQuad } )
showFx = transition.to( currView, { x = _W, time = fxTime, transition = easing.outQuad } )
showFx = transition.to( nextView, { x = _W, time = fxTime, transition = easing.outQuad, onComplete = bookFxEnded } )

end

end

end

–====================================================================–
– MOVE TO CHANGE BOOK PAGE
–====================================================================–

moveBookPage = function( event )


– If is not book, return without do anything

if not isBook then
return true
end


– If is changing scene, return without do anything

if isChangingScene then
return true
end


– Verify event

if event.phase == “moved” then


– Move pages while touching

if event.xStart > event.x then
prevView.x = -_W - ( event.xStart - event.x )
currView.x = 0 - ( event.xStart - event.x )
nextView.x = _W - ( event.xStart - event.x )
else
prevView.x = -_W + ( event.x - event.xStart )
currView.x = 0 + ( event.x - event.xStart )
nextView.x = _W + ( event.x - event.xStart )
end

elseif event.phase == “ended” then


– If page reach limit then change

local limit = 0.2

if currView.x < -_W * limit then
director:changeBookPage( “next” )
elseif currView.x > _W * limit then
director:changeBookPage( “prev” )
else
director:changeBookPage( getCurrBookPageNum() )
end

end

end

–====================================================================–
– GO TO BOOK PAGE
–====================================================================–

function director:goToPage( params, target, fade )


– Test parameters

if type( params ) ~= “table” then
fade = target
target = params
params = nil
end

if type( target ) ~= “number” then
showError( "The page name must be a number. page = " … tostring( target ) )
return false
end

if target < 1 then
showError( "Cannot change to page lower then 1. page = " … tostring( target ) )
return false
end


– Fade

local fadeFx = display.newRect( -_W, -_H, _W * 3, _H * 3 )
fadeFx:setFillColor( 0, 0, 0 )
fadeFx.alpha = 0
fadeFx.isVisible = false
effectView:insert( fadeFx )

local fxEnded = function( e )


– Remove Fade

fadeFx:removeSelf()
fadeFx = nil

end


– Go to page

local change = function( e )

– First page
if target == 1 then


– Rebuild Group

rebuildGroup( “prev” )


– Unload Scene

if prevScene ~= currScene and prevScene ~= nextScene then
unloadScene( prevScene )
end


– Load Scenes

if bookPages[1] then
loadCurrScene( bookPages[1].page, params or bookPages[1].params )
currBookPage = 1
end

if bookPages[2] then
loadNextScene( bookPages[2].page, bookPages[2].params )
end

– Last page
elseif target == getBookPageCount() then


– Load Scenes

if bookPages[target] then
loadCurrScene( bookPages[target].page, params or bookPages[target].params )
currBookPage = target
end

if bookPages[target - 1] then
loadPrevScene( bookPages[target - 1].page, bookPages[target - 1].params )
end


– Rebuild Group

rebuildGroup( “next” )


– Unload Scene

if prevScene ~= nextScene and currScene ~= nextScene then
unloadScene( nextScene )
end

– Somewhere in the middle
else


– Load Scenes

if bookPages[target-1] then
loadPrevScene( bookPages[target - 1].page, bookPages[target - 1].params )
end

if bookPages[target] then
loadCurrScene( bookPages[target].page, params or bookPages[target].params )
currBookPage = target
end

if bookPages[target + 1] then
loadNextScene( bookPages[target + 1].page, bookPages[target + 1].params )
end

end


– Search for the localGroup.start() function

if currScreen then
if currScreen.start then


– Start Page

local handler, message = pcall( currScreen.start )

if not handler then
showError( “Failed to start page of object '” … currScene … “’ - Please verify the localGroup.start() function.”, message )
return false
end

end
end


– Complete Fade

local showFx = transition.to( fadeFx, { alpha = 0, time = fxTime, onComplete = fxEnded } )

end


– Execute Fade

if fade then
fadeFx.isVisible = true
local showFx = transition.to( fadeFx, { alpha = 1, time = fxTime, onComplete = change } )
else
change()
end

end

–====================================================================–
– TURN TO BOOK OR SCENES
–====================================================================–

function director:turnToBook()
isBook = true
end

function director:turnToScenes()


– Toggle Books

isBook = false


– Rebuild Group

rebuildGroup( “prev” )


– Unload Scene

if prevScene ~= currScene and prevScene ~= nextScene then
unloadScene( prevScene )
end


– Rebuild Group

rebuildGroup( “next” )


– Unload Scene

if prevScene ~= nextScene and currScene ~= nextScene then
unloadScene( nextScene )
end


– Finish

prevScreen = nil
nextScreen = nil
prevScene = “main”
nextScene = “main”

end

return director[/lua]

template.lua for director v1.4m[lua]–====================================================================–
– SCENE: [NAME]
–====================================================================–

–[[

  • Version: [1.0]
  • Made by: [name]
  • Website: [url]
  • Mail: [mail]

******************

  • INFORMATION
    ******************

  • [Your info here]

–]]

local template = {}
function template.new ()


– Groups

local localGroup = display.newGroup()


– Your code here

— CODE —


– MUST return a display.newGroup()

return localGroup

end

return template[/lua]

ui.lua by ANSCA Inc.[lua]-- ui.lua (currently includes Button class with labels, font selection and optional event model)

– Version 1.5 (works with multitouch, adds setText() method to buttons)

– Copyright © 2010 ANSCA Inc. All Rights Reserved.
– Made module-less by Edward Garcia @ 2011

– Permission is hereby granted, free of charge, to any person obtaining a copy of
– this software and associated documentation files (the “Software”), to deal in the
– Software without restriction, including without limitation the rights to use, copy,
– modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
– and to permit persons to whom the Software is furnished to do so, subject to the
– following conditions:

– The above copyright notice and this permission notice shall be included in all copies
– or substantial portions of the Software.

– THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
– INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
– PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
– FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
– OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
– DEALINGS IN THE SOFTWARE.

local ui = {}


– Helper function for newButton utility function below
local function newButtonHandler( self, event )

local result = true

local default = self[1]
local over = self[2]

– General “onEvent” function overrides onPress and onRelease, if present
local onEvent = self._onEvent

local onPress = self._onPress
local onRelease = self._onRelease

local buttonEvent = {}
if (self._id) then
buttonEvent.id = self._id
end

local phase = event.phase
if “began” == phase then
if over then
default.isVisible = false
over.isVisible = true
end

if onEvent then
buttonEvent.phase = “press”
result = onEvent( buttonEvent )
elseif onPress then
result = onPress( event )
end

– Subsequent touch events will target button even if they are outside the stageBounds of button
display.getCurrentStage():setFocus( self, event.id )
self.isFocus = true

elseif self.isFocus then
local bounds = self.stageBounds
local x,y = event.x,event.y
local isWithinBounds =
bounds.xMin <= x and bounds.xMax >= x and bounds.yMin <= y and bounds.yMax >= y

if “moved” == phase then
if over then
– The rollover image should only be visible while the finger is within button’s stageBounds
default.isVisible = not isWithinBounds
over.isVisible = isWithinBounds
end

elseif “ended” == phase or “cancelled” == phase then
if over then
default.isVisible = true
over.isVisible = false
end

if “ended” == phase then
– Only consider this a “click” if the user lifts their finger inside button’s stageBounds
if isWithinBounds then
if onEvent then
buttonEvent.phase = “release”
result = onEvent( buttonEvent )
elseif onRelease then
result = onRelease( event )
end
end
end

– Allow touch events to be sent normally to the objects they “hit”
display.getCurrentStage():setFocus( self, nil )
self.isFocus = false
end
end

return result
end

– Button class

function ui.newButton( params )
local button, default, over, size, font, textColor, offset

if params.default then
button = display.newGroup()
default = display.newImage( params.default )
button:insert( default, true )
end

if params.over then
over = display.newImage( params.over )
over.isVisible = false
button:insert( over, true )
end

– Public methods
function button:setText( newText )

local labelText = self.text
if ( labelText ) then
labelText:removeSelf()
self.text = nil
end

local labelShadow = self.shadow
if ( labelShadow ) then
labelShadow:removeSelf()
self.shadow = nil
end

local labelHighlight = self.highlight
if ( labelHighlight ) then
labelHighlight:removeSelf()
self.highlight = nil
end

if ( params.size and type(params.size) == “number” ) then size=params.size else size=20 end
if ( params.font ) then font=params.font else font=native.systemFontBold end
if ( params.textColor ) then textColor=params.textColor else textColor={ 255, 255, 255, 255 } end

– Optional vertical correction for fonts with unusual baselines (I’m looking at you, Zapfino)
if ( params.offset and type(params.offset) == “number” ) then offset=params.offset else offset = 0 end

if ( params.emboss ) then
– Make the label text look “embossed” (also adjusts effect for textColor brightness)
local textBrightness = ( textColor[1] + textColor[2] + textColor[3] ) / 3

labelHighlight = display.newText( newText, 0, 0, font, size )
if ( textBrightness > 127) then
labelHighlight:setTextColor( 255, 255, 255, 20 )
else
labelHighlight:setTextColor( 255, 255, 255, 140 )
end
button:insert( labelHighlight, true )
labelHighlight.x = labelHighlight.x + 1.5; labelHighlight.y = labelHighlight.y + 1.5 + offset
self.highlight = labelHighlight

labelShadow = display.newText( newText, 0, 0, font, size )
if ( textBrightness > 127) then
labelShadow:setTextColor( 0, 0, 0, 128 )
else
labelShadow:setTextColor( 0, 0, 0, 20 )
end
button:insert( labelShadow, true )
labelShadow.x = labelShadow.x - 1; labelShadow.y = labelShadow.y - 1 + offset
self.shadow = labelShadow
end

labelText = display.newText( newText, 0, 0, font, size )
labelText:setTextColor( textColor[1], textColor[2], textColor[3], textColor[4] )
button:insert( labelText, true )
labelText.y = labelText.y + offset
self.text = labelText
end

if params.text then
button:setText( params.text )
end

if ( params.onPress and ( type(params.onPress) == “function” ) ) then
button._onPress = params.onPress
end
if ( params.onRelease and ( type(params.onRelease) == “function” ) ) then
button._onRelease = params.onRelease
end

if (params.onEvent and ( type(params.onEvent) == “function” ) ) then
button._onEvent = params.onEvent
end

– Set button as a table listener by setting a table method and adding the button as its own table listener for “touch” events
button.touch = newButtonHandler
button:addEventListener( “touch”, button )

if params.x then
button.x = params.x
end

if params.y then
button.y = params.y
end

if params.id then
button._id = params.id
end

return button
end

– Label class

function ui.newLabel( params )
local labelText
local size, font, textColor, align
local t = display.newGroup()

if ( params.bounds ) then
local bounds = params.bounds
local left = bounds[1]
local top = bounds[2]
local width = bounds[3]
local height = bounds[4]

if ( params.size and type(params.size) == “number” ) then size=params.size else size=20 end
if ( params.font ) then font=params.font else font=native.systemFontBold end
if ( params.textColor ) then textColor=params.textColor else textColor={ 255, 255, 255, 255 } end
if ( params.offset and type(params.offset) == “number” ) then offset=params.offset else offset = 0 end
if ( params.align ) then align = params.align else align = “center” end

if ( params.text ) then
labelText = display.newText( params.text, 0, 0, font, size )
labelText:setTextColor( textColor[1], textColor[2], textColor[3], textColor[4] )
t:insert( labelText )
– TODO: handle no-initial-text case by creating a field with an empty string?

if ( align == “left” ) then
labelText.x = left + labelText.stageWidth * 0.5
elseif ( align == “right” ) then
labelText.x = (left + width) - labelText.stageWidth * 0.5
else
labelText.x = ((2 * left) + width) * 0.5
end
end

labelText.y = top + labelText.stageHeight * 0.5

– Public methods
function t:setText( newText )
if ( newText ) then
labelText.text = newText

if ( “left” == align ) then
labelText.x = left + labelText.stageWidth / 2
elseif ( “right” == align ) then
labelText.x = (left + width) - labelText.stageWidth / 2
else
labelText.x = ((2 * left) + width) / 2
end
end
end

function t:setTextColor( r, g, b, a )
local newR = 255
local newG = 255
local newB = 255
local newA = 255

if ( r and type® == “number” ) then newR = r end
if ( g and type(g) == “number” ) then newG = g end
if ( b and type(b) == “number” ) then newB = b end
if ( a and type(a) == “number” ) then newA = a end

labelText:setTextColor( r, g, b, a )
end
end

– Return instance (as display group)
return t

end

return ui[/lua] [import]uid: 7721 topic_id: 16262 reply_id: 61768[/import]

The right way ( I think) to use display object on metatables:

dog.lua

local dog = {}  
local dog\_mt = { \_\_index = dog }  
   
function dog.new( x, y, width, height )  
 local img = display.newRect( x, y, width, height )  
 local newDog = { image = img } -- newDog is a table now, fit to be use in setmetatable  
 return setmetatable( newDog, dog\_mt )  
end  
   
function dog:rollOver()  
 print( self.image.x, self.image.y )  
end  
   
return dog  

main.lua

local dog = require 'dog' local dog1 = dog.new(10,10,10,10) dog1:rollOver() -- prints 10, 10 [import]uid: 31508 topic_id: 16262 reply_id: 131199[/import]

The right way ( I think) to use display object on metatables:

dog.lua

local dog = {}  
local dog\_mt = { \_\_index = dog }  
   
function dog.new( x, y, width, height )  
 local img = display.newRect( x, y, width, height )  
 local newDog = { image = img } -- newDog is a table now, fit to be use in setmetatable  
 return setmetatable( newDog, dog\_mt )  
end  
   
function dog:rollOver()  
 print( self.image.x, self.image.y )  
end  
   
return dog  

main.lua

local dog = require 'dog' local dog1 = dog.new(10,10,10,10) dog1:rollOver() -- prints 10, 10 [import]uid: 31508 topic_id: 16262 reply_id: 131199[/import]