-
Notifications
You must be signed in to change notification settings - Fork 20
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 )
.....
endIn 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
endsetmetatable(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: