Wednesday, August 25, 2010

DisplayObject inheritance in Corona

In Corona SDK, DisplayObjects are actually native C++ objects under the hood. Lua makes it relatively easy to expose native functionality in the Lua language, but then a Lua "userdata" loses the "object-oriented" capabilities of a Lua table.

Corona goes way beyond the simple solution with this problem; a Corona DisplayObject behaves partially as a normal Lua table, giving you the ability to add properties to it. This is a great convenience, but because of the way Lua is implemented, it cannot give you the full ability to "override" methods and properties that Lua can for generic tables.

Here's a more detailed description and example.

Corona Display Objects are actually C++ native objects internally. The way Lua implements binding native pointers is with "userdata", which sets a metatable, used for looking up methods and properties, giving you a very powerful kind of extensibility. However, 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. There are a number of examples in Corona of this. They may have properties and/or methods but are not extensible. The fact that Display Objects behave partly like tables is a convenience feature. The alternative would have been having a DisplayObject field for custom data, allowing you to access your own data associated with a DisplayObject, for example. Raising this functionality directly into the DisplayObject is conceptually simpler (although more work to implement). But the underlying native bridge is constrained by the way Lua implements it, and we view this as a feature, not a bug, since allowing an instance of a native object to selectively override parts of its interface could be profoundly destabilizing and possibly insecure.

Meanwhile, it is possible to "extend" Corona DisplayObjects as tables, by using Lua's built in features. This is not exactly like Lua's normal capabilities, because we can't replace the DisplayObject metatable in this case. However, it is very useful if you want to extend DisplayObjects.

So here's a code example of how to accomplish this. An image is loaded by makeProxy(), but the actual object returned is a table. Any properties/methods not defined by the proxy table are forwarded to the underlying DisplayObject.

The basic method is described in http://www.lua.org/pil/13.4.4.html.

function makeProxy(params)

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