-
Notifications
You must be signed in to change notification settings - Fork 17
Possible Implementation of Classes in Lua
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.
Feedback is crucial to improve the tutorial!
Let me know if you have any questions, critique, suggestions or just any other ideas.
Chapter 1: Prototype
- The Ball, The Brick, The Platform
- Game Objects as Lua Tables
- Bricks and Walls
- Detecting Collisions
- Resolving Collisions
- Levels
Appendix A: Storing Levels as Strings
Appendix B: Optimized Collision Detection (draft)
Chapter 2: General Code Structure
- Splitting Code into Several Files
- Loading Levels from Files
- Straightforward Gamestates
- Advanced Gamestates
- Basic Tiles
- Different Brick Types
- Basic Sound
- 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
- Improved Ball Rebounds
- Ball Launch From Platform (Two Objects Moving Together)
- Mouse Controls
- Spawning Bonuses
- Bonus Effects
- Glue Bonus
- Add New Ball Bonus
- Life and Next Level Bonuses
- Random Bonuses
- Menu Buttons
- Wall Tiles
- Side Panel
- Score
- Fonts
- More Sounds
- Final Screen
- Packaging
Appendix D: GUI Layouts
Appendix E: Love-release and Love.js
Beyond Programming: