Showing all entries for: April 2012

Object-Oriented Programming in Lua

We all know that Lua isn’t an object-oriented language per se, but it can be made to incorporate OOP features because of its inherent flexibility. Because of that fact, there have been several class implementations for Lua, and all of them are worthy solutions.

However, there doesn’t seem to be a single one (that I’ve found anyway) that is tailor-made for Corona SDK developers with support for classes that produce instances of display objects as well as non-display objects—all while remaining compatible with non-Corona Lua scripts (in case you would like to use the same system for those projects as well).

Long story short: I created (yet another) class implementation for Lua. It’s lightweight, requires only a single module, has a ton of useful features, and supports display objects when used with Corona (and also supports non-display object table classes as well).

It’s called OWL (Objects with Lua), and is distributed under the MIT License so feel free to use it as you wish. To get it, visit the OWL GitHub page. Be sure to take a look at the reference file and all the samples included in the archive (which can all be run in the Corona Simulator).

The main features include:

  • Constructor/initialization (can be overridden on a per-instance basis, or avoided entirely).
  • Multiple inheritance (all the way to top-level super class).
  • Separation of classes and instances, as with most OOP languages.
  • Common OOP methods: is_a(), kind_of(), and instance_of().
  • Private class table (inherited by sub-classes, but not by instance objects).
  • Easily implement basic metamethods via add_property_callback() method (yes, even with instances that are Corona display objects).
  • Several samples and documentation included.
  • Compatible with Corona SDK (provides display object classes), as well as non-Corona projects.

Quick note on the “property callbacks” feature

One particularly cool feature that’s included, which isn’t actually directly related to OOP, is the ability to add “property callbacks” to object instances, which opens up basic metamethod functionality (yes, even to Corona display object instances) so you can easily intercept property changes for specific objects.

It’s like saying: “Call [this function] whenever [this property] is changed on [this object].”

Or better yet: “Call health_changed() whenever the [health] property is changed on the [player] object.”

Of course, all the other features are great too, and should come in very handy for your projects, but I thought that feature was pretty unique and should be very useful for many developers. For instance, it’s much more efficient to use these property callbacks than to use, say, an enterFrame listener and “poll” property changes on several objects at set intervals (or even worse, on every frame).

Examples

The examples below are meant to give you a “feel” for how things work when using OWL. If you need more in-depth usage information, see the samples included in the download.

Classes and Sub-Classes

local owl = require "owl"

-- create a class and two sub-classes
local AnimalClass = owl.class{ name="Animal" }
local CatClass = owl.class{ name="Cat", from="Animal", image="cat.png" }
local DogClass = owl.class{ name="Dog", from=AnimalClass, image="dog.png" }
local PuppyClass = owl.class{ name="Puppy", from="Dog", image="puppy.png" }

-- instances from the above classes (actual display objects)
local cat = owl.instance{ from=CatClass }
cat:translate( 100, 100

local dog1 = owl.instance{ from="Dog" }
dog1:translate( 200, 200 )

local dog2 = owl.instance{ from="Dog" }
dog2:translate( 300, 300 )

local puppy = owl.instance{ from="Puppy" }
puppy:translate( 400, 400 )

print( puppy.class_name )  -- Puppy
print( puppy.super_class.class_name )  -- Dog
print( puppy:is_a( "Animal" ) ) -- true

Constructor/Initialization

local owl = require "owl"

-- create Animal class
local AnimalClass = owl.class{ name="Animal" }

-- create Dog class (sub-classed from Animal)
local DogClass = owl.class{ name="Dog", from="Animal" }

-- constructor for all instances of the "Dog" class
function DogClass:init()
    local id = self.id or "Unnamed"  -- self is object instance

    print( "New instance of DogClass created. ID: " .. id )
end

local dog = owl.instance{ from="Dog", id="Scruffy" }

-- OUTPUT - New instance of DogClass created. ID: Scruffy

Want more? See the sample code…

The samples included in the repo go over just about all the different features, to include separating classes into different modules, defining class methods, using constructors, working with private class data, and more. Combine that with the documentation, there’s no shortage of information on how to use OWL in a real project.

On a final note, I’m very much looking forward to hearing about your experiences with OWL. Feel free to shoot over quick tweet if you end up using it in any of your projects.

Links: OWL on GitHub Documentation