Create a class without external libraries

General discussion about LÖVE, Lua, game development, puns, and unicorns.
User avatar
Kibita
Prole
Posts: 31
Joined: Tue Dec 29, 2015 7:46 pm

Create a class without external libraries

Post by Kibita »

Hello!
I wonder how you guys make your own class in Lua without using external libraries like middleClass. I'm a little stuck in this part because I don't wan't to make something wrong that could imply a bad coding. So, what's is the most formal way to make classes in lua?

Thank you!
bobbyjones
Party member
Posts: 730
Joined: Sat Apr 26, 2014 7:46 pm

Re: Create a class without external libraries

Post by bobbyjones »

There isn't. You can do it whatever way you like. Some use closures and some use metatables. It depends on what you want or need.

With closures - http://theindiestone.com/forums/index.p ... etatables/

With metatables - http://lua-users.org/wiki/ObjectOrientationTutorial
User avatar
Kibita
Prole
Posts: 31
Joined: Tue Dec 29, 2015 7:46 pm

Re: Create a class without external libraries

Post by Kibita »

bobbyjones wrote:There isn't. You can do it whatever way you like. Some use closures and some use metatables. It depends on what you want or need.

With closures - http://theindiestone.com/forums/index.p ... etatables/

With metatables - http://lua-users.org/wiki/ObjectOrientationTutorial
So it is right what I am doing?

Code: Select all

local Game = {}
Game.__index = Game

function Game:new(meter, gravity)
    setmetatable({}, Game)

    -- Set physics parameters
    love.physics.setMeter(meter)
    self.world = love.physics.newWorld(0, gravity*meter, true)

    -- Load background
    self.background = love.graphics.newImage("img/background.png")

    -- Create the Balls
    self.balls = {}
    self.balls.left  = Ball:new(self.world, 75, 5, 150) -- Insert the left ball
    self.balls.right = Ball:new(self.world, 75, 570, 150) -- Insert the right ball

    -- Create the player
    self.player = nil

    -- Attatch the balls to the player

    return self
end

...
bobbyjones
Party member
Posts: 730
Joined: Sat Apr 26, 2014 7:46 pm

Re: Create a class without external libraries

Post by bobbyjones »

Actually if you are just starting out I recommend the closure approach it's easier to understand. But anyways if your code works and does what your expect it to then yes it is right. If it doesn't then we'll you are doing it wrong. If you tested the code properly you would notice an error. Instead of returning self return the setmetatable({},Game).
User avatar
Roland_Yonaba
Inner party member
Posts: 1563
Joined: Tue Jun 21, 2011 6:08 pm
Location: Ouagadougou (Burkina Faso)
Contact:

Re: Create a class without external libraries

Post by Roland_Yonaba »

I would just suggest to go along with PiL's approach, chapter 16.
Also, even though I do not consider this to be anywhere formal, here is the bare-bones light class implementation I am mostly using when I need one.
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: Create a class without external libraries

Post by airstruck »

Code: Select all

Game.__index = Game
...
setmetatable({}, Game)
That pattern has always felt wrong to me. I'm not sure how it got so popular. The __index property does not really belong in your Game class, and none of Game's properties really belong in the metatable. It works alright, it just seems messy and semantically non-obvious.

I'd probably use a dedicated metatable instead:

Code: Select all

local meta = { __index = Game }
...
setmetatable({}, meta)
Or just create them on the fly if you don't care about a few extra tables.

Code: Select all

setmetatable({}, { __index = Game })
Also, in your Game:new method you create a new instance with setmetatable({}, Game), but you don't assign the result to anything, and then you use self (which refers to the Game class) as if it were the instance you created. This won't work the way you probably intend.

Once you fix that, there's another problem with having the "instantiator" and constructor both in one function (Game:new). If you want to use inheritance, you have no way of applying the parent constructor to an instance of a subclass, because there are no dedicated constructors, only the "instantiator."

Here is the base class I use when I need classical OOP, if you're interested. When I don't feel like including that, I sometimes use a snippet like this instead:

Code: Select all

local Foo = setmetatable({}, { 
    __call = function (self, ...)
        local object = setmetatable({}, { __index = self })
        return object, self.constructor(object, ...)
    end
})
Last edited by airstruck on Fri Feb 12, 2016 3:47 pm, edited 2 times in total.
User avatar
rmcode
Party member
Posts: 454
Joined: Tue Jul 15, 2014 12:04 pm
Location: Germany
Contact:

Re: Create a class without external libraries

Post by rmcode »

I prefer the closure based approach:

Code: Select all

local ClassName = {};

function ClassName.new()
    local self = {};

    local baz = 'Foo is Löve, All is Löve' -- Private attribute
    self.bazooka = true -- Public attribute
    
    -- Private function
    local function bar()
        -- Do stuff
    end
    
    -- Public function
    function self:foo()
        -- Do stuff
    end

    return self;
end

return ClassName;
It makes for clean and readable code and makes it easy to use inheritance.
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: Create a class without external libraries

Post by airstruck »

That's nice and readable, no metatables, allows private members.

Each object has its own copy of all methods and private functions, which could be a performance consideration. When using metatables, all instances of a class share the same methods. You could get that without metatables by doing this kind of thing:

Code: Select all

-- classname.lua

-- Private function
local function bar (self)
    -- Do stuff
end

-- Public function
local function foo (self)
    -- Do stuff
end

-- Constructor
return function (x, y)
    return {
        -- Constructor args
        x = x, y = y,
        -- Default properties
        bazooka = true,
        -- Public methods
        foo = foo,
    }
end
It doesn't look as nice, but you get reusable methods. You'll still have private functions, and it will be easy to expose them as public functions by adding them to the returned object. You won't have private instance variables, but you can have "static" privates.

If your module returns a table of constructors for similar things, this will give you nice mixin-like reuse of methods and you won't need inheritance.
User avatar
Kibita
Prole
Posts: 31
Joined: Tue Dec 29, 2015 7:46 pm

Re: Create a class without external libraries

Post by Kibita »

bobbyjones wrote:Actually if you are just starting out I recommend the closure approach it's easier to understand. But anyways if your code works and does what your expect it to then yes it is right. If it doesn't then we'll you are doing it wrong. If you tested the code properly you would notice an error. Instead of returning self return the setmetatable({},Game).
The unique trouble I'm facing right now is when I create two objects from the same class:

Code: Select all

-- Create the Balls
self.balls = {}
self.balls.left  = Ball:new(self.world, ballRadius, 0, gameHeight / 2) -- Insert the left ball
self.balls.right = Ball:new(self.world, ballRadius, gameWidht - ballRadius, gameHeight / 2) -- Insert the right ball
It just show one (actually I think both are created, but they share the same information like position and size, so one of the balls are covering the other, making it not visible)
bobbyjones
Party member
Posts: 730
Joined: Sat Apr 26, 2014 7:46 pm

Re: Create a class without external libraries

Post by bobbyjones »

Yup its because you returned self and not the setmetatable({},Game)
Post Reply

Who is online

Users browsing this forum: No registered users and 95 guests