Skip to content

Possible Implementation of Classes in Lua

noooway edited this page Sep 7, 2017 · 2 revisions

From a technical point, a class in Lua is a table, containing class functions (a.k.a. methods) and class variables. Each instance (a.k.a. object) of the class is also a table, configured such that any missing field is looked at the class-table. This allows objects to have customized properties stored in their own table, while sharing common properties using the class table. In Lua, such arrangement is achieved by configuring so-called __index metamethod in each instance of the class. This __index metametod is either a function or another table that is used when an inquired field is missing in the quered table.

Here is an example:

> class_A = {}
> class_A.name = 'The Class'
> class_A.say_hello_method = function( z ) print( "hello from " .. z.name ) end
> class_A.name 
The Class --(*1)
> class_A.say_hello_method( class_A )
hello from The class   --(*2)
> object_of_class_A = {}
> object_of_class_A.name
nil --(*3)
> object_of_class_A.__index = class_A
> setmetatable( object_of_class_A, object_of_class_A )
> object_of_class_A.name 
The Class --(*4)
> object_of_class_A.say_hello_method( object_of_class_A )
hello from The Class
> object_of_class_A.name = "An object"
> object_of_class_A.say_hello_method( object_of_class_A )
hello from An Object --(*5)

(*1): Field name in the class_A table is set to "The Class".
(*2): We call a function, stored in the field say_hello_method of the class_A table.
It expects a single argument z. We pass class_A itself as such argument.
(*3): We define a table object_of_class_A. An attempt to index it's name field returns nil.
(*4): After __index and setmetatable magic, an attempt to index object_of_class_A.name field doesn't return nil any longer. However, at this point the field name still isn't set at the object_of_class_A, so the attempt to index it results in an invocation of the __index method. In this case, it points to the class_A, where it redirects the query for the name. The value of this field is 'The Class', which is printed in the output.
(*5): We set field name in the object_of_class_A. Now it can be successfully indexed without any redirections. Still, the field say_hello_method does not present in the object_of_class_A and it's value is searched for in the class_A (where __index points to).

There is a syntactic trick in Lua, which simplifies writing and calling methods — the colon syntax. When a method is prefixed by the colon instead of the dot, a table before ':' is substituted as the first formal parameter of the method:

some_table:function_inside_table() 
-- is equivalent to
some_table.function_inside_table( some_table )

That way it is possible to write:

> class_A:say_hello_method()
hello from The Class
> object_of_class_A:say_hello_method()
hello from An Object 

It is also possible to define methods using colon syntax. In that case, a first formal parameter with name self is implied.

function some_table:function_inside_table( arg1, arg2, etc )
  ..... 
end
-- is equivalent to
function some_table.function_inside_table( self, arg1, arg2, etc )
  .....
end

In our case:

> function class_A:self_say_hello_method( hello_ending )
>> print( "self-hello from " .. self.name .. hello_ending )
>> end
> class_A:self_say_hello_method( "!!!" )
self-hello from The Class!!!
> object_of_class_A:self_say_hello_method( "???" )
self-hello from An Object???

The dot and the colon forms can be mixed freely:

> object_of_class_A.self_say_hello_method( object_of_class_A, "!!!" )
self-hello from An Object!!!

A common Lua idiom for class constructor looks like this:

function Class:new( o )
   o = o or {}
   setmetatable(o, self)
   self.__index = self
   o.name = o.name or "Class"
   o.property_1 = o.property_1 or 10
   o.property_2 = o.property_2 or 20
   .....
   return o
end

setmetatable(o, self) sets Class as the metatable for an each new Class object. self.__index = self sets __index metamethod in the Class to point to Class itself.

To implement inheritance, it is necessary to configure __index metamethod of the child class to point to the table representing the parent class. In this case it can be done simply by SubClass = Class:new(). Then it is possible to proceed defining SubClass methods. I won't need this, since I'm not going to use inheritance in this project.

One last thing: in Lua syntax some_function{} is identical to some_function( {} ). Such shorthand is commonly found in calls to class constructors, so don't be surprised.

.....

It is possible to pack all these low-level details into a set of functions that can be used to define classes.

    Home
    Acknowledgements
    Todo

Chapter 1: Prototype

  1. The Ball, The Brick, The Platform
  2. Game Objects as Lua Tables
  3. Bricks and Walls
  4. Detecting Collisions
  5. Resolving Collisions
  6. Levels

    Appendix A: Storing Levels as Strings
    Appendix B: Optimized Collision Detection (draft)

Chapter 2: General Code Structure

  1. Splitting Code into Several Files
  2. Loading Levels from Files
  3. Straightforward Gamestates
  4. Advanced Gamestates
  5. Basic Tiles
  6. Different Brick Types
  7. Basic Sound
  8. Game Over

    Appendix C: Stricter Modules (draft)
    Appendix D-1: Intro to Classes (draft)
    Appendix D-2: Chapter 2 Using Classes.

Chapter 3 (deprecated): Details

  1. Improved Ball Rebounds
  2. Ball Launch From Platform (Two Objects Moving Together)
  3. Mouse Controls
  4. Spawning Bonuses
  5. Bonus Effects
  6. Glue Bonus
  7. Add New Ball Bonus
  8. Life and Next Level Bonuses
  9. Random Bonuses
  10. Menu Buttons
  11. Wall Tiles
  12. Side Panel
  13. Score
  14. Fonts
  15. More Sounds
  16. Final Screen
  17. Packaging

    Appendix D: GUI Layouts
    Appendix E: Love-release and Love.js

Beyond Programming:

  1. Game Design
  2. Minimal Marketing (draft)
  3. Finding a Team (draft)

Archive

Clone this wiki locally