Lua class objects? - lua

I'm new to Lua and I'm wondering if there's a way to make many class objects to make different items in my case like in a OOP language like C# or Java. An example of what I'm saying is a class like this in Lua...
weapon = {}
function weapon.load()
{
weapon.name = "CHASE'S BUG"
weapon.damage = 1
weapon.rare = "Diet Valley Cheez"
weapon.hottexture = love.graphics.newImage("/ledata/invalid.png")
weapong.playtexture = love.graphics.newImage("/ledata/invalid.png")
weapon.dura = 1
weapon.type = "swing"
}
But in a main class you could make new objects of that class which would be done like so in something like C#
weapon Dagger = new weapon();
Dagger.name = "Dagger of Some Mountain"
...
Is there a way to do that in Lua?

There're many ways. This is a simple one. Not really much OOP, you don't have inheritance and some other stuff. But I think this will work in your case.
function weaponFire ()
print "BANG BANG"
end
function newWeapon (opts)
local weaponInstance = {}
weaponInstance.name = opts.name
weaponInstance.damage = opts.damage
weapon.fire = weaponFire
return weaponInstance
end

Another way to go about it is to use a table like so (using the example of a car):
Car = {}
Car.new = function(miles,gas,health)
local self = {}
self.miles = miles or 0
self.gas = gas or 0
self.health = health or 100
self.repair = function(amt)
self.health = self.health + amt
if self.health > 100 then self.health = 100 end
end
self.damage = function(amt)
self.health = self.health - amt
if self.health < 0 then self.health = 0 end
end
return self
end
It creates a table called 'Car' which would be the equivalent of a class, not an instance, then it defines a method "new" in the Car class which returns an instance of a car with variables and functions. An example to using this implementation:
local myCar = Car.new()
print(myCar.health)
myCar.damage(148)
print(myCar.health)
myCar.repair(42)
print(myCar.health)

Lua is object oriented, but it's not like Java/C++/C#/Ruby, etc, there's no native class, the only way to create new object is to clone an existing object. That's why it's called prototype language(like JavaScript).
Read Programming in Lua Chapter 16. You can mock normal OOP using metatable.

since you tagged with love2d, you may have a look at middleclass. It have docs there. And more it have addon like stateful which is mainly for game and love2d.

Related

Lua, local variables in a table

Sorry in advance if this is an incorrect question. I'm fairly new to Lua and I'm not sure how to go about this. I want to access a variable stored in a table from a function variable.
As far as I know there is no self-referencing tables before constructed.
An example would be this:
local bigTable = {
a = {
foo = 0,
bar = function(y)
print(foo) --Incorrect
end
}
}
What would be the best approach for this situation?
What you want to do is to create a table first, and append the keys to it:
local a = {}
a.foo = 0
a.bar = function()
print(a.foo)
end
local bigTable = {
a = a
}
bigTable.a.bar() -- prints 0
local bigTable = {
a = {
foo = 0,
bar = function(self, ...)
print(self.foo)
end,
}
}
-- Somewhere else in the code...
bigTable.a.bar(bigTable.a) --> 0
-- or the shorter but (almost) equivalent form:
bigTable.a:bar() --> prints 0
I anticipate your next question will be "What does the : do?", and for that there's lots of answers on SO already :)
Note that there's potential for a performance improvement here: if the above code gets called a lot, the bar method will be created again and again, so it could make sense to cache it; but that's pointless unless the surrounding code is already fast enough that this one allocation would have a noticeable impact on its runtime.

Trying to implement object-oriented programming in Lua, but it's not quite working

Ok, so I'm trying to follow the instructions found here: https://www.lua.org/pil/16.1.html to do something resembling OO programming in Lua (and the LOVE game framework), but it's not working. Here's the core of my code. I have a generic Object class:
local Object = {}
function Object:load(arg)
end
function Object:update(dt)
end
function Object:draw()
end
function Object:new(arg)
o = {}
setmetatable(o, self)
self.__index = self
o:load(arg)
return o
end
return Object
and a Ship class that inherits from it:
Object = require('objects.object')
local Ship = Object:new()
Ship.sprite = love.graphics.newImage('assets/sprites/ship.png')
Ship.sprite:setFilter('nearest', 'nearest', 0)
Ship.px = Ship.sprite:getWidth()/2
Ship.py = Ship.sprite:getHeight()/2
function Ship:load(arg)
self.x = arg.x or 0
self.y = arg.y or 0
self.sx = arg.sx or arg.s or 1
self.sy = arg.sy or arg.s or 1
self.rot = arg.rot or 0
self.tint = arg.tint or {255, 255, 255}
end
function Ship:draw()
love.graphics.setColor(self.tint)
love.graphics.draw(self.sprite, self.x, self.y, self.rot,
self.sx, self.sy, self.px, self.py)
love.graphics.setColor({255, 255, 255})
end
return Ship
Now the problem is, I create two of these Ships as members of another object with this code:
self.ship1 = Ship:new({x=50, y=self.y1, s=2, tint={0, 0.5, 1}})
self.ship2 = Ship:new({x=750, y=self.y2, s=-2, tint={1, 0.5, 0}})
But when I draw them, I only see one - the second. As it turns out, it's like the code above doesn't assign the new instances of Ship to ship1 and ship2, but directly to self, for reasons that I can't understand. Did I do something wrong or is this a weird bug of the interpreter?
It is not bug of the interpreter, it is designed behavior of the language. o={} creates global variable, which is not expected by the programmer here. "Why it is so?" is a frequent question to the language creator. There are many efforts to take control over that behavior simpler .
o = {} without local creates global variable global variable is accessible and shared between all the functions in the program, unless you use fancy environment scoping techniques. Using global variable inside a function opens up doors for various side effects, you should be careful with side effects.
I've removed some of the syntactic sugar and added code above can be equivalently written as follows:
Object.new = function(table_before_colon,arg)
highlander = {} --global variable, there can be only one
setmetatable(highlander,table_before_colon);
table_before_colon.__index = table_before_colon;
highlander:load(arg) -- table_before_colon.__index.load(highlander,arg)
return highlander
end
local Ship = Object:new()
--global highlander == Ship
--Ship.new points to Object.new
function Ship:load(arg)--equivalent to: Ship.load=function(self,arg)
--code that sets fields of the `self` object and is called from within new
self.x = arg.x or 0 -- equivalently highlander.x=arg.x or 0
end
Now, the presence of the global variable would not matter, if nothing happened to it in the period from the start of the new till the new returns. But, apparently, your other code is similar to this:
local OtherObject = Object:new()
--otherObject == highlander
OtherObject.load = function(new_other_obj,arg)
--highlander == new_other_obj
new_other_obj.ship1 = Ship:new({x=50, y=self.y1, s=2, tint={0, 0.5, 1}})
--highlander == new_other_obj.ship1
new_other_obj.ship2 = Ship:new({x=750, y=self.y2, s=-2, tint={1, 0.5, 0}})
--highlander == new_other_obj.ship2
end
So, OtherObject.load calls other functions, and those functions also access and modify the same global variable.
local some_object = OtherObject:new()
returns the global variable as it is at the end of the call, which is last set to the ship2 inside the call to Ship:new inside the call to OtherObject.load inside call to OtherObject:new.
Solved! Apparently, what was needed was this little snippet:
function Object:new(arg)
local o = {}
setmetatable(o, self)
self.__index = self
o:load(arg)
return o
end
Making o local in all my new methods solved everything. I don't know how it worked exactly but I assume having it in the global variable space broke something when the meta tables are set.

Scoping classes created in Lua using Luabind

I am aware that Lua classes can be created using the OO system that Luabind exposes to Lua:
http://www.rasterbar.com/products/luabind/docs.html#defining-classes-in-lua
class 'lua_testclass'
function lua_testclass:__init(name)
self.name = name
end
function lua_testclass:print()
print(self.name)
end
a = lua_testclass('example')
a:print()
However I am unable to figure out how to scope the class within another namespace so I can do the following:
a = MyScope.lua_testclass('example')
a:print()
Anyone has a idea. I do not want my classes to pollute the global namespace in Lua.
Luabind's class function will always pollute the global table. However, you can clean up after it:
function newclass(name)
oldglobal = _G[name]
class(name)
cls = _G[name]
_G[name] = oldglobal
return cls
end
Then you would use it like this:
MyScope.lua_testclass = newclass 'lua_testclass'
Analogous to local mod = require 'mod' you have to spell the name of the class twice, but you could easily build another function on top of this that could be used like setclass(MyScope, 'lua_testclass'), automatically putting the class into MyScope:
function setclass(scope, name) scope[name] = newclass(name) end
Disclaimer: All this code is entirely untested.
I did mine a little differently, but it's generally the same concept. Mine doesn't create the class, but rather just moves it. I also implemented it on the C++ side.
To implement what I did in Lua, you would write:
function moveClass(name)
oldGlobal = _G[name]
_G[name] = nil
return oldGlobal
end
To implement it in C++, you would write:
luabind::module(lua) [
luabind::def("moveClass", +[](lua_State * lua, std::string name) {
// In the case the class does not exist, this will just
// remove nil and return nil. That essentially does nothing.
luabind::object oldGlobal = luabind::globals(lua)[name];
luabind::globals(lua)[name] = luabind::nil;
return oldGlobal;
})
];
So now if you were to use that to move a class you created, you would do this:
class 'MyClass'
myTable = {}
myTable.MyClass = moveClass 'MyClass'
As an extra note, if you want the moveClass function to give an error in the case that the class you are trying to move does not exist, use luabind::type(oldGlobal) == LUA_TNIL to determine if the class existed or not.
Example:
luabind::module(lua) [
luabind::def("moveClass", +[](lua_State * lua, std::string name) {
luabind::object oldGlobal = luabind::globals(lua)[name];
if (luabind::type(oldGlobal) == LUA_TNIL) {
throw std::runtime_error("Class does not exist.");
}
luabind::globals(lua)[name] = luabind::nil;
return oldGlobal;
})
];

Classes in Lua, how does it work

I'm new to Lua, and I'm trying to understand its OO part, for example :
lkw = {}
lkw.la= 0
function lkw:func(ge)
self.la = self.la + ge
end
function lkw:new()
local res = {}
setmetatable(res, self)
self.__index = self
return res
end
mylkw = lkw:new()
in this example the "class" lkw can create object using new, but what do self and index mean ?
should consider self as this in java/C++ and what is the index ?
This style of OOP is frequent in Lua. I do not like it because it is not explicit enough for me, but let me try to explain.
There are two confusing things: the use of the : sugar in function definitions and the use of the "class" as the metatable for its instances.
First, function a:b(...) is the same as a.b = function(self, ...), so let us remove all sugar:
lkw = {}
lkw.la = 0
lkw.func = function(self, ge)
self.la = self.la + ge
end
lkw.new = function(self)
local res = {}
setmetatable(res, self)
self.__index = self
return res
end
mylkw = lkw.new(lkw)
Now, this is "prototypal inheritance". lkw is the "prototype" for instances like mylkw. This is similar but slightly different from a "class".
When the new constructor is called, lkw is passed as the self argument.
The second and third lines of the constructor are weird. This is probably easier to understand:
lkw.new = function(self)
local res = {}
setmetatable(res, {__index = lkw})
return res
end
i.e.: if we do not find something in the instance we go look for it inside the prototype.
This explains how func works. The first time it is called, the instance will not contain a la key so lkw.la will be used.
The reason the code is not written this way is that the weird construction allows "prototypal inheritance": you could call "new" on mylkw and get an "instance of the instance" (i.e. in prototypal inheritance an instance and a child class are the same thing).
I think this is a very confusing feature. For reference this is about how I would write code that does about the same thing, with no inheritance:
local methods = {
func = function(self, ge)
self.la = self.la + ge
end
}
local lkw = {
new = function()
return setmetatable({la = 0}, {__index = methods})
end
}
local mylkw = lkw.new()

When instantiating multiple instances of a lua object when to use require vs new?

I'm trying to wrap my head around the different ways to define and instantiate objects in Lua.
For the current problem, I'm imagining objects as simple as a C-struct (no methods necessary).
Between these two methods below, does it matter which way we choose? Is there a practical difference? I'd like to go for "least code" because I like "less code" best. It seems like the first method has a problem somewhere. Maybe it's just me. What happens if we instantiate 10,000 mary's? Does it matter?
First Way:
mary.lua
return {
name = "mary"
}
main.lua
local r = require("mary")
local s = require("mary")
local t = require("mary")
local u = require("mary")
Second Way:
mary.lua
local _O = {}
function _O:new()
o = {}
setmetatable(o, self)
self.__index = self
o:_create()
return o
end
function _O:_create()
self.name = "mary"
end
return _O
main.lua
local o = require("mary")
local r = o:new()
local s = o:new()
local t = o:new()
local u = o:new()
require is a function for loading modules, not instantiating classes.
require("mary") only loads mary.lua once, then stores the return value inside of package.loaded["mary"]. All subsequent results of require("mary") return the object at package.loaded["mary"], and do not create new instances. This makes require unsuitable for class instantiating.
local r = require("mary")
local s = require("mary")
print(rawequal(r, s)) -- Prints true
r.name = "samantha"
print(s.name) -- Prints samantha
Your second way actually creates new class instances. However, instead of a new class method, it's more familiar to call the class table itself, using the __call metamethod. I've written a one-file OOP framework that handles that and single inheritance; feel free to use it directly or as a reference.

Resources