Basic concepts of 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.
coffee
Party member
Posts: 1206
Joined: Wed Nov 02, 2011 9:07 pm

Basic concepts of OOP

Post by coffee »

Hello, after learned how to manipulate well enough tables with inserts/remove/ipairs I decided upgrade functions with some minimal OO and understand the basic principles. So I used this and all worked well.
-- main.lua

Code: Select all

function love.load()
	require ("enemy")
	enemy:init()

	enemy:add("elf",20)
	enemy:add("human")
	enemy:add("dwarf",30)
	enemy:add()	
end

function love.update(dt)
	enemy:update(dt)
end

function love.draw()
	enemy:draw()
end

function love.quit()
end
-- enemy.lua

Code: Select all

enemy, enemies = {},{}

function enemy:init()
  local instance = {}
  setmetatable(instance, {__index = enemy})
end

function enemy:add(race,hp)
	local enemy = {}
	enemy.race = race or "human"
	enemy.hp = hp or 15
	table.insert (enemies,enemy)
end

function enemy:update(dt)
	for i,v in pairs (enemies) do
	   	if v.race == "elf" and v.hp < 100 then v.hp = v.hp + 2 end
    end
end

function enemy:draw()
	for i,v in pairs (enemies) do
		love.graphics.print (i.." "..v.race.." "..v.hp,0,15*i)
	end
end
Then I tried to simplify and understand if was possible merge table "enemies" and class "enemy:" in the same table and so I failled when using enemy:update. Do I must always reserve "enemy" as function variable or I'm not doing the right thing to achieve a function/table mix (if possible)?

Code: Select all

enemy = {}

function enemy:init()
  local instance = {}
  setmetatable(instance, {__index = v})
end

function enemy:add(race,hp)
	local v = {}
	v.race = race or "human"
	v.hp = hp or 15
	table.insert (enemy,v)
end

function enemy:update(dt)
	for i,v in pairs (enemy) do
	   	if v.race == "elf" and v.hp < 100 then v.hp = v.hp + 2 end
    end
end

function enemy:draw()
	for i,v in pairs (enemy) do
		love.graphics.print (i.." "..v.race.." "..v.hp,0,15*i)
	end
end
Also I'd like to know if it's possible use something like enemy:add({ race = "half-human", weapon = "Hammer" }) and then skip in-between function args like define HP? Thank you in advance! :)
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: Basic concepts of OOP

Post by kikito »

I've got a some comments.

First, if you make "enemies" a local variable, and return it at the end of enemies.lua, you will have less trouble in the future. Right now "enemies" is a global variable, and it's too easy to modify it by mistake. You can achieve the same functionality while making enemies local this way:

--main.lua

Code: Select all

local enemies = require 'enemies' -- this "enemies" is only visible inside main.lua

function love.load()
  enemies.init()
  enemies.add("elf", 20)
  ... -- leave the rest as it was
--enemies.lua

Code: Select all

local enemies = {} -- this variable is visible only inside enemies.lua
...
return enemies -- ... but the files that require this file will get a reference to it
Concerning the "do I use one variable to hold the functions, and another one to hold the proper enemies, or just one for both": I would use two separate variables. I would also make the second variable local inside enemies.lua, so no one can access it directly (only via functions in the enemies table)
--enemies.lua

Code: Select all

local enemies = {}
local enemyList = {}
...

function enemies:add(race, hp)
  table.insert(enemyList, { race = race or "human", hp = hp or 15 })
end

...
return enemies -- ... but the files that require this file will get a reference to it
The reason I prefer this approach is that occasionally you will want to save your individual enemies using something different than a number to index them; for example, their name. Which will mean that you will need to use pairs to iterate over the list of enemies. And when you do that, you will get the enemies mixed up with the functions, which is not good. Adding an extra table is completely negligible in speed/memory usage.

Also, if you use the tables this way you neatly separate the two concerns: "you store the functions of the enemies", and "you store the enemies themselves". Separating different kinds of information into different places is always a good idea.
coffee wrote:Also I'd like to know if it's possible use something like enemy:add({ race = "half-human", weapon = "Hammer" }) and then skip in-between function args like define HP?
I'm sorry, I don't quite understand what you mean.
When I write def I mean function.
coffee
Party member
Posts: 1206
Joined: Wed Nov 02, 2011 9:07 pm

Re: Basic concepts of OOP

Post by coffee »

Thank you for your valuable input kikito. It will put me in the right direction.

1 - Keeping separate tables: Good points, nothing to argue about it, I totally agree. So my first draft was the best approach. Since I not familiar yet with classes was not sure if I wasn't getting "all" the benefits of it or if I was trying to get too much. :)

2 - Making "enemies" local. I think I understand your protection concerning but I believe if I do it so, it could make my life a living hell. That's because enemies data is/will be so needed everywhere for "checking/compare/consulting/printing stats that I would need create/call too much "enemies:xxxx" return functions and not instead simple directly checking like for example if "enemies.hp > 20 then". I try do it but don't know if for me that increased security will compensate the sacrifice and extra code.

3 -
kikito wrote:
coffee wrote:Also I'd like to know if it's possible use something like enemy:add({ race = "half-human", weapon = "Hammer" }) and then skip in-between function args like define HP?
I'm sorry, I don't quite understand what you mean.
Sorry. Imagine that I want to add a enemy but "enemy:add" entry args is already so long like enemy:add(race,hp,x,y,gender, age, ... , armor,weapon,shield, ...). So I want define that the new enemy race is "half-human", his weapon "Hammer" and the enemy:race function decides all the rest based in default values. To don't have to define all between values in "enemy:add("half-human","default hp", "default x", "default y", "default gender", "default/random age"... "Hammer")" can I get a way of direct define the values I really want and don't define it all the rest/between? I would need some kind of enemy:add(...) "variable/value" parser instead of direct lua variable assign?

EDITED: About the 3rd point I think is perhaps more flexible make an enemy:add ("race = half-human","weapon = Hammer","variable = value") oriented function! So it's what I'm working on! :)
User avatar
bartbes
Sex machine
Posts: 4946
Joined: Fri Aug 29, 2008 10:35 am
Location: The Netherlands
Contact:

Re: Basic concepts of OOP

Post by bartbes »

coffee wrote: EDITED: About the 3rd point I think is perhaps more flexible make an enemy:add ("race = half-human","weapon = Hammer","variable = value") oriented function! So it's what I'm working on! :)
Worst. API. Ever.
I'd most definitely prefer enemy:add{race = "half-human", weapon = "Hammer", variable = "value"}.
waraiotoko
Prole
Posts: 33
Joined: Thu Oct 06, 2011 6:08 pm

Re: Basic concepts of OOP

Post by waraiotoko »

coffee wrote:...can I get a way of direct define the values I really want and don't define it all the rest/between? I would need some kind of enemy:add(...) "variable/value" parser instead of direct lua variable assign?
I think what you're looking for is a function with the format: function foo(...) where "..." is read as a table called "args". http://www.lua.org/pil/5.2.html
Basically you would put the required variables up front, and the non-required variables in the back.
If that doesn't work for you, or you already knew about that, then you could just create a table with your default attributes for an enemy and write a copy function for it, then edit the values you want and send it to your function. Would look something like:

Code: Select all

myenemy = new(defaultenemy)
myenemy.somevalue = "non-default value"
enemy:add(myenemy)
Where new(table) is a deep or shallow copy function.
User avatar
bartbes
Sex machine
Posts: 4946
Joined: Fri Aug 29, 2008 10:35 am
Location: The Netherlands
Contact:

Re: Basic concepts of OOP

Post by bartbes »

You shouldn't be using the arg table anymore, it's been deprecated back in lua 5.1, use ... now.
waraiotoko
Prole
Posts: 33
Joined: Thu Oct 06, 2011 6:08 pm

Re: Basic concepts of OOP

Post by waraiotoko »

bartbes wrote:You shouldn't be using the arg table anymore, it's been deprecated back in lua 5.1, use ... now.
Sorry, didn't realize my manual was that old.
coffee
Party member
Posts: 1206
Joined: Wed Nov 02, 2011 9:07 pm

Re: Basic concepts of OOP

Post by coffee »

bartbes wrote:Worst. API. Ever.
I realized that. Have parsing in that way is slowing down things and even worst than enemy:add("race","half-human", "weapon","Hammer", "variable","value"}
bartbes wrote:I'd most definitely prefer enemy:add{race = "half-human", weapon = "Hammer", variable = "value").
Hmm, but I'm afraid don't know how to collect after the values in that way. That's new for me.
User avatar
thelinx
The Strongest
Posts: 857
Joined: Fri Sep 26, 2008 3:56 pm
Location: Sweden

Re: Basic concepts of OOP

Post by thelinx »

You just define enemy.add as

Code: Select all

function enemy.add(self, args)
  print(args.race, args.weapons)
end
This works because

Code: Select all

func{foo = "bar"}
-- is syntactic sugar for
func({foo = "bar"})
waraiotoko
Prole
Posts: 33
Joined: Thu Oct 06, 2011 6:08 pm

Re: Basic concepts of OOP

Post by waraiotoko »

thelinx wrote:You just define enemy.add as

Code: Select all

function enemy.add(self, args)
  print(args.race, args.weapons)
end
Agreed!
Post Reply

Who is online

Users browsing this forum: Amazon [Bot], Bing [Bot] and 12 guests