OOP like concepts - RhythmLunatic/stepmania GitHub Wiki
It's a work in progress
Lua doesn't behave exactly like other OOP languages you might know, but you can do similar things.
"Constructors" in lua files
LoadActor() takes a second argument which will be passed in as the variable ...
.
Usually this is the player, ex. LoadActor("lifebar",PLAYER_1)
You can also pass in a table and then expand the table. For example:
LoadActor("lifebar",{PLAYER_1, true, "AAA","BBB"})
Then in the "lifebar.lua":
local tbl = ...
local player = tbl[1]
local is_EXHARD = tbl[2]
local name = (you get the idea)
(For some reason ...[1] and ...[2] does not work, but table variables are just pointer references anyways so declaring local tbl
will not waste memory)
Declaring a "class"
Lua doesn't have classes. But you can copy tables and treat them as classes.
Example 1: instancing objects
This example shamelessly stolen from Simply Love and Rave It Out's code. In C# and C++ this is better known as a struct, not a class.
PlayerDefaults = {
JudgmentGraphic = "Season 2",
ScreenFilter = 0
}
We copy these tables into a another table using this function...
function table.shallowcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in pairs(orig) do
copy[orig_key] = orig_value
end
else -- number, string, boolean, etc
copy = orig
end
return copy
end
And the "class" to hold "instances" of the "struct" we created...
PlayerOptions = {
PLAYER_1 = table.shallowcopy(PlayerDefaults),
PLAYER_2 = table.shallowcopy(PlayerDefaults)
}
Which can then be accessed anywhere by using PlayerOptions[PLAYER_1] or PlayerOptions[PLAYER_1]["ScreenFilter"] to get the "ScreenFilter" value for the player.
Make sure to reset your table or overwrite it when the player joins in with their profile, if you're using this for a player option.
If you just want one static instance, PlayerOptions = table.shallowcopy(PlayerDefaults)
works.
Example 2: metatables
The actual way to create classes in lua. Usually copying a table is enough because you can assign functions to tables and use a Reset() function from the table, but if you want to do it like this anyways:
(P.S. This is taken from my MB theme, you should try it)
Let's declare our class 'MBAAClass':
local MBAAClass = {}
MBAAClass.__index = MBAAClass
Now let's add an init function (or constructor, whatever you want to call it)
MBAAClass.new = function()
--Magic function that turns a table into a class thing
local self = setmetatable({}, MBAAClass)
self.info = {{Character="None",Difficulty=1},{Character="None",Difficulty=1}};
Warn("MBSTATE initialized.")
return self;
end;
And a couple of functions...
MBAAClass.GetDifficulty = function(self, pn)
return self.info[PlayerNumber:Reverse()[pn]+1].Difficulty;
end;
MBAAClass.SetDifficulty = function(self, pn, diff)
self.info[PlayerNumber:Reverse()[pn]+1].Difficulty = diff;
end;
Now let's create an instance of the class:
MBSTATE = MBAAClass.new();
Example 3: More advanced metatables
This is an example of a Vector2 class, commonly used in game engines like Godot.
local V2mt = {}
V2mt.__index=V2mt
function V2mt:__add(b)
local res = Vector2(0,0)
if type(b) == "number" then
res.x = self.x + b
res.y = self.y + b
else
res.x = self.x + b.x
res.y = self.y + b.y
end
return res;
end
function V2mt:__tostring()
return ("Vec2(%.1f, %.1f)"):format(self.x, self.y)
end
--And this function creates an instance of a Vector2 when you use Vector2() like you'd expect in any other OOP language.
function Vector2(x,y)
return setmetatable( {x=x or 0, y=y or 0}, V2mt)
end
With the Vector2 class we can add two Vector2 objects.
local vec2_1 = Vector2(1,2);
local vec2_2 = Vector2(2,3);
local vec2_result = vec2_1 + vec2_2;
SCREENMAN:SystemMessage(tostring(vec2_result)); --Will print "Vec2(3,5)"
More help on metatables as classes is available here: http://lua-users.org/wiki/LuaClassesWithMetatable