Help with OOP

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
LeNitrous
Prole
Posts: 29
Joined: Tue Sep 08, 2015 3:25 am

Help with OOP

Post by LeNitrous »

Hello, this is related with my current project but I feel this needs a topic of its own as it would get off-topic. I'm fairly new to OOP and don't know much about it. I'm making a system that when an object is created, it adds itself to another table. Some pseudo code to get it:

Code: Select all

list = {}

item = {
	new = function(__self, id)
		self.id = id
		table.insert(list, __self.id())
	end;
	spawn = function()
		-- spawn code
	end
}
So when for example heart = item.new() is called, the object "heart" is added to the table "list" and can run functions like list["heart"]:spawn()
Is this possible? I want to make my system as flexible as I can.
User avatar
pgimeno
Party member
Posts: 3640
Joined: Sun Oct 18, 2015 2:58 pm

Re: Help with OOP

Post by pgimeno »

Yes it is. Note that list["heart"] is exactly equivalent to list.heart.

Also, you don't need table.insert at all. Just list[id] = instance will do.

That said, I don't understand the logic in your code above (especially self vs. __self, what function id() is, and what part is supposed to create the instance).
nyenye
Citizen
Posts: 62
Joined: Fri Dec 02, 2016 1:44 pm

Re: Help with OOP

Post by nyenye »

First of all, Lua is not a Object Oriented Programming language, but it gives you the tools to do so, and these are called metatables.

item.lua

Code: Select all

local Item = {}
local item_metatable = { __index = Item}

Item.new = function(data)
  local instance = data
  return setmetatable(instance, item_metatable)
end

Item:use = function()
  self.count = self.count - 1
end
With this you could do something like:

Code: Select all

local Item = require 'item'
local heart = Item.new({count = 3})
heat:use()
Note the difference between '.' and ':', when using ':' you pass as first paramteter, self, and when using '.', you won't get acces to self.

Later, you should organize your code, so you don't mix the creation of an item, with it being added to a list. Maybe you could mount yourself a kind of Factory which given a string would handle it all without mixing thinks. Here is some adapted code from my project given that right now I don't have time to explain any further:

factory.lua

Code: Select all

local Factory = {
    ['switch'] = {
        ['item'] = function( data )
            local instance = Item.new(data)
            return instance
        end
    }
}

function Factory.new( entity, data )
    if Factory.switch[entity] then
        return Factory.switch[entity]( data )
    end
end

return Factory
main.lua

Code: Select all

Factory = require 'factory.lua'
list = {}
table.insert(list, Factory.new('item', {id = 'heart'})
Keep in mind that this 'table.insert()', will put the new item at the end of the list, ordered numerically. If you want the key to be the id, you have to make sure that it doesn't exists on the list (otherwise will be overwritten), and do it normally:

Code: Select all

list[id] = item
Last edited by nyenye on Sat Dec 03, 2016 4:05 pm, edited 3 times in total.
User avatar
Sir_Silver
Party member
Posts: 286
Joined: Mon Aug 22, 2016 2:25 pm
Contact:

Re: Help with OOP

Post by Sir_Silver »

Yes, OOP is possible in Lua. There isn't a typical class structure in Lua like there is in Python for example, instead we can use metatables and metamethods.

Code: Select all

list = {}

item = {}
item.__index = item  --The index metamethod is defined for our item class. This means that any table whose metatable is the item class will now be able to "inherit" it's methods.

function item:new(id)
	local item_object = setmetatable({}, item)  --This creates a table and sets up the item class as the parent to our newly instanced item_object.
	
	item_object.id = id
	table.insert(list, item_object)
	
	return item_object
end

function item:spawn()
	--spawn code
end

heart = item:new(1)
I cleaned up your example a little bit, because it was just psuedo code, into an example that you can play around with if you want to.

Here are all of the metamethods if you're curious about them. You can use them to make any sort of class structure that you want, from vectors, to items and beyond!

http://lua-users.org/wiki/MetatableEvents
Firehawk
Prole
Posts: 2
Joined: Sat Dec 03, 2016 2:00 pm

Re: Help with OOP

Post by Firehawk »

I think this is what you were looking to do

Code: Select all

item_mt = {}
--This sets up item_mt as a lookup.
--__index is a metafunction which is only invoked if an index does NOT exist in the main table.
item_mt.__index = item_mt 

--We can define a function on the base.
function item_mt:base_cool_function()
    print("That was awesome!")
end

--Think of this as a sort of "item manager"
item = {
    --We'll use this table to hold registered items for later instancing.
	registered = {},
	
	new = function(id)
		assert(not item.registered[id], "This item has already been registered!")
	    
	    --Create a new table supplied with the item id and point __index to itself.
		local obj = setmetatable({id = id}, item_mt)
		obj.__index = obj
		
		item.registered[id] = obj
		return obj
	end,
	
	spawn = function(id)
		local item_class_mt = assert(item.registered[id], "This item does not exist!")
		local obj = setmetatable({}, item_class_mt)
		
		return obj
	end
}

--Register a basic item.
test_item = item.new("basic_item")

--Give it some base functionality. This works because of the use of __index.
function test_item:cool_function()
    print("Hey! That's pretty good!")
end

--We can now spawn a new item instance and call that function we defined.
local my_test_item = item.spawn("basic_item")
my_test_item:cool_function()

--We can call a function further up the chain of inheritance..
my_test_item:base_cool_function()

local their_test_item = item.spawn("basic_item")

--These are not the same instances.
print(their_test_item == my_test_item)
User avatar
LeNitrous
Prole
Posts: 29
Joined: Tue Sep 08, 2015 3:25 am

Re: Help with OOP

Post by LeNitrous »

I kinda gotten confused already. My goal is to use a table to contain all objects and drawing them all without calling each object individually while still being able to access their functions hence an item manager.

Code: Select all

-- items.txt
heart
circle
square
triangle
star

-- main.lua
items = {}
function love.load()
	file = "items.txt"
	if love.filesystem.isFile(file) then
		for line in love.filesystem.lines(file) do
			local object = item(line)
			table.insert(items, object)
		end
	end
end
EDIT: I can use a separate text file to append all items inside it. Now I need to know how to call a specific item. I can do it via items[1]:use() yet I need it to be like items["heart"]:use(). Any way how?
User avatar
pgimeno
Party member
Posts: 3640
Joined: Sun Oct 18, 2015 2:58 pm

Re: Help with OOP

Post by pgimeno »

See [manual]pairs[/manual].
User avatar
Sir_Silver
Party member
Posts: 286
Joined: Mon Aug 22, 2016 2:25 pm
Contact:

Re: Help with OOP

Post by Sir_Silver »

Firehawks example sounds exactly like what you wanted, it's basically an item manager. Though it probably uses concepts like metatables that you are unfamiliar with. Learning metatables is probably where you want to start if you want to look into the use of objects.
User avatar
ivan
Party member
Posts: 1915
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Help with OOP

Post by ivan »

Just a small note:

Code: Select all

item = {}
item.__index = item

function item:new(id)
	local item_object = setmetatable({}, item)
	[...]
	return item_object
end
Seems that "item.__index = item" doesn't do anything at all.
I believe you mean:

Code: Select all

item = {} -- interface
itemMT = { __index = item } -- metatable (falls back to the interface)

function item:new(id)
	local item_instance = setmetatable({}, itemMT) -- this is where the magic happens
	[...]
	return item_instance 
end
It's clearer to separate the "interface" from the "metatable",
especially when inheritance is involved.
nyenye
Citizen
Posts: 62
Joined: Fri Dec 02, 2016 1:44 pm

Re: Help with OOP

Post by nyenye »

LeNitrous wrote: EDIT: I can use a separate text file to append all items inside it. Now I need to know how to call a specific item. I can do it via items[1]:use() yet I need it to be like items["heart"]:use(). Any way how?
Given your code, you only need to change 2 lines to do what you want:

Code: Select all

-- main.lua
items = {}
function love.load()
   file = "items.txt"
   if love.filesystem.isFile(file) then
      for line in love.filesystem.lines(file) do
         local object = item(line) -- object is the ID for the item
         items[object] = object -- this will put on the table, a key 'heart', and a value 'heart'
      end
   end
end
Having said that I recommend that you read this wiki: lua-users.org/wiki/TablesTutorial (maybe the very last section is what you are looking for... dunno). Also keep in mind that items.heart, is a string!!, so you won't be able to get any OOP functionalities (like calling methods, or adding properties like counters or whatever). Maybe do:

Code: Select all

items[object] = { count = 5 }
-- so you can do 
if items.heart.counter > 0 then
    ---use
end
And if you make yourself an Item class (see earlier responses) you can access methods right away.

EDIT: Just to clarify something about the metatable, you can think of it as a second list of properties for a table. So if you call some property that is not known to the 'primary' table, then it will look up on the metatable (or 'secondary' table). This is mostly useful for methods, which shouldn't have to change depending on the instance. (See earlier responses to see examples)
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 2 guests