OOP help

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
Zilarrezko
Party member
Posts: 345
Joined: Mon Dec 10, 2012 5:50 am
Location: Oregon

OOP help

Post by Zilarrezko »

So I've pretty much got a ton of LUA down, but I think one of the last things I need to learn is Object Orientated Programming in LUA. I've seen about 6 videos that use it in some way shape or form (tutorial or application), and 3 written tutorials or manuals (one from a book), yet I can't wrap my head around it enough to apply any of it to a game or even in a simple console program. If anyone has a reference that explains it VERY well please let me know so I can stop hitting my head against a brick wall. :cry:
User avatar
Kingdaro
Party member
Posts: 395
Joined: Sun Jul 18, 2010 3:08 am

Re: OOP help

Post by Kingdaro »

For the record, it's Lua, not LUA.

Also for the record, if you're not comfortable with OOP, you don't really need to learn it. There are plenty of different non-oop programming style and you can choose to make your game however you want, as long as it works.

On topic, OOP in Lua is usually based on tables, the "colon syntax", and using the __index metamethod for inheritance. Inheritance is the ability to create an instance of another object, or another table in this case, completely separate from the main table, which uses the functions of its parents.

Here's a simple example:

Code: Select all

Person = {}
Person.__index = Person -- this makes it so that instances access this table

-- same as
-- function Person.talk(self)
function Person:talk()
   print("Hello, my name is " .. self.name)
end

function Person.new(name)
   local instance = {}                     -- create a new instance
   setmetatable(instance, Person)  -- derive it from the person class
   instance.name = name               -- set the name of the instance
   return instance                          -- return the new instance
end

-- making a new instance of Person
jason = Person.new("Jason") 

-- same as
-- jason.talk(jason)
jason:talk() --prints: Hello, my name is Jason
You can learn more about it on the lua-users wiki: http://lua-users.org/wiki/ObjectOrientationTutorial
User avatar
Zilarrezko
Party member
Posts: 345
Joined: Mon Dec 10, 2012 5:50 am
Location: Oregon

Re: OOP help

Post by Zilarrezko »

I can understand that code just fine, it's just I can't write my own for an application because everything is pointing everywhere and it literally gives me a headache trying to understand it, let alone beginning to think about what code to write to do what I want as an application.

I've read that tutorial twice before and it just doesn't help me. As for a different approach to doing a task other than OOP, I can't think of any other way to do so.
User avatar
Kingdaro
Party member
Posts: 395
Joined: Sun Jul 18, 2010 3:08 am

Re: OOP help

Post by Kingdaro »

What is it about OOP that confuses you? You're being a little vague. Also, hat is it that you're trying to do? Are you sure you absolutely need to learn OOP to accomplish it?

There are plenty of alternatives to OOP. An alternative to the example code I've provided:

Code: Select all

function newPerson(name)
   return {
      name = name,
      talk = function(self)
         print("Hello, my name is " .. self.name)
      end,
   }
end

jason = newPerson("Jason")
jason:talk()
A suggestion I'd make is to look at the code in some others' games, to get a good idea of the kind of design patterns you can implement in a game. You'll find OOP 7 out of 10 times, but if you look deep enough, you'll also find a lot of neat coding techniques for yourself.
User avatar
Zilarrezko
Party member
Posts: 345
Joined: Mon Dec 10, 2012 5:50 am
Location: Oregon

Re: OOP help

Post by Zilarrezko »

I'm not sure what i'm having trouble with, and it's severely frustrating. As with your suggestion, trust me, I've seen a lot of videos

I've used Goature's entity system, and I can trace every bit of code, but it makes me hit my head against the wall that I can't create my own from scratch, of which I want to do and he never explains anything here:
http://www.youtube.com/watch?v=L9G0WyG9vaY

I've looked at this video about 4 times, but I can't figure out from 17:10 and on (you can see a couple of my comments in the video of me being distressed that I don't understand both the code and what I'm not understanding):
http://www.youtube.com/watch?v=SDLujCr0c5Y

And I've watched the following video 3 times trying to make my GUI system even starting but I seem to have trouble even making a function return two values which makes me :cry:. I don't even know where to begin in what is confusing for me there:
http://www.youtube.com/watch?v=ppZMCAF7cWw

And I've seen this one once, but he's too confusing to follow his words.
http://www.youtube.com/watch?v=GcKCAdc0WZ8

It might be because nobody explains their code, they just say "well here it is, this goes through here creating these values, and this is an OOP system". Yet no one really says this is how you create a set of base code, this is how an instance is create. This is how you can tell this instance from this other instance and here's how you access this specific instance's properties. I might have no clue what I'm talking about in which case no one can help me and I've wasted your time, of which I'm sorry if that's the case. I may take the day and tomorrow just to see what i'm not getting wrong. Just spent the past 4 months looking at code and OOP systems along with AP Physics, might not be bad to take a step back for a couple days too.
User avatar
Kadoba
Party member
Posts: 399
Joined: Mon Jan 10, 2011 8:25 am
Location: Oklahoma

Re: OOP help

Post by Kadoba »

You're making this harder than it actually is. OOP is really just about extending a set of functions and data. I mean, this is essentially OOP in Lua:

Code: Select all

-- OOP Library. Open source license. Send donations to Kadoba. Do not steal.
function inherit( parent, child )
   child = child or {}
   for k,v in pairs( parent ) do child[k] = v end
   return child
end
new = inherit
If any of that confuses you let me know.
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: OOP help

Post by kikito »

Ok. Brace yourself, this is going to be long.

---

The thing is that OOP is an overused, "bag term" that people use with many different meanings. Let me explain the ones I see more often.

Pure OOP: Objects

On its simplest form, OOP means "Programming using objects". On its purest form, an object is just "A group of attributes and some functions that act on those attributes". These "functions that act on attributes" are usually called methods.

Given that definition, you could create an object in Lua very easily:

Code: Select all

local peter = {name = 'Peter', speak = function(self) print("Hi, I'm " .. self.name) end }
peter:speak() -- Hi, I'm Peter
On that example, peter is an object with 1 attribute (name) and 1 method (speak).

Classes (as instance creators)

"Class" is another of those "bag concepts" that people use with several different meanings. The one they tend to use the most is "something that, given some properties, can create an object". Objects created by a class are usually called instances of that class. The instances of a class usually "work similarly", since their methods do the same things (only their attributes are different). This is very useful in games, where you very often have similar things that behave the same way (enemy ships in a shooter game, the bullets, powerups ...)

With that definition of class, a simple Lua function could be a class:

Code: Select all

local Person = function(name)
  return {name = name, speak = function(self) print("Hi, I'm " .. self.name) end }
end

local john = Person('John')
local mary = Person('Mary')
john:speak() -- Hi, I'm John
mary:speak() -- Hi, I'm Mary
So john and mary are now instances of Person. They both have an attribute called name and a method called speak.

Classes (as method repositories)

On the previous example, each instance of the class had its own copy of the speak method. While this works in certain scenarios, it is usually not very efficient in terms of speed (Lua creates functions very fast, but it still takes some time) and memory (there can be many copies of the same function). In other languages they have the same issue: it would be very desirable to put the methods in 1 place, and share them among all the instances. This task usually falls upon the classes too: they act as a "repository of methods". When an instance needs to invoke a method, it "requests it to the class", instead of having its own copy.

In Lua, this behavior is very easily accomplished with metatables & metamethods:

Code: Select all

local Person = {}
Person.new = function(name) return setmetatable({name=name}, {__index=Person}) end
Person.speak = function(self) print("Hi, I'm " .. self.name) end

local jason = Person.new('Jason')
local angelo = Person.new('Angelo')
jason:speak() -- Hi, I'm Jason
angelo:speak() -- Hi, I'm Angelo
On this example, Person is able to create instances by using a function called new. jason and angelo are instances of Person, but they only have the name attribute. The speak method is on the Person class, and there's only one copy of it. This saves memory and instantiation speed, in exchange of making every invokation of the speak method a bit slower ("asking the class for the method" takes a bit of time, too).

Composition

One important thing that people tend to overlook is the fact that attributes are not limited to basic objects, like strings or numbers. An attribute can be an object, too. When an object X has another object Y as attribute, we say that X it is composed of Y, or that X has a Y.

Code: Select all

local Person = {}
Person.new = function(name, parent) return setmetatable({name=name, parent=parent}, {__index=Person}) end
Person.speak = function(self)
  if self.parent then
    print("Hi, I'm " .. self.name .. ", son of " .. self.parent.name)
  else
    print("Hi, I'm " .. self.name)
  end
end

local mogh = Person.new('Mogh')
local worf = Person.new('Worf', mogh)

mogh:speak() -- Hi, I'm Mogh
worf:speak() -- Hi, I'm Worf, son of Mogh
Image

On the previous example, Person instances have two attributes: name & parent (which can be nil). mogh has its parent set to nil, while worf has its parent set to morgh. The speak method, shared by both instances in the Person class, prints a different message depending on whether the instance invoking it has a parent attribute or not.

Notice that you can do composition with instances of different classes, too:

Code: Select all

local Gun = {}
Gun.new = function(name, sound) return setmetatable({name=name, sound=sound}, {__index=Gun}) end
Gun.fire = function(self, target) print('<The ' .. self.name .. ' fires upon ' .. target.name .. '. ' .. self.sound .. '!>') end

local Person = {}
Person.new = function(name, gun) return setmetatable({name=name, gun=gun}, {__index=Person}) end
Person.attack = function(self, target)
  print('Take this, ' .. target.name .. '!')
  self.gun:soot(target)
end

local revolver = Gun.new('Revolver', 'Bang')
local phaser = Gun.new('Phaser', 'Zap')

local flash = Person('Flash', revolver)
local ming = Person('Ming', phaser)

flash:attack(ming) -- Take this, Ming! <The Revolver fires upon Ming. Bang!>
ming:attack(flash) -- Take this, Flash! <The Phaser fires upon Flash. Zap!>
Image

On this example there are two classes, Gun and Person.
Gun has two attributes, name & sound, and one method, fire. It has two instances, revolver & phaser.
Person has two attributes, name & gun, and one method, attack. It has two instances, flash (who uses a revolver) & ming (who uses a phaser).

Inheritance

Now this is something that trips people a lot. The relationship between a class and its instances is clear: we say "an instance IS A class": "peter IS A person".

There are some people that like to extend this to classes as well. They want to express things like, "A Mammal is an Animal", where both Mammal and Animal are classes with instances. And have Mammal "inherit" some properties from Animal, so that instances of Mammal "borrow" the methods of Animal in addition of those of Mammal. And they even want to extend this hierarchy with lots of levels: Bat is a Mammal is an Animal is a Vertebrate etc.

Defining relationships between classes with "is A" is difficult because a class can "be" many other things in the real world. A Mammal is a vertebrate, but it is also Solid. And is is also Visible. And needs food for living. The relationship between instances and their class is clear (the class creates the instances) but when you have 2 classes it's much more complex.

In my opinion, composition can usually express the same concepts as inheritance with more exactitude: a Mammal has a SolidBody, has a VisibleShape and has DietaryNeeds. For this reason, I am not going to show you how to do inheritance in Lua, which is possible, but is not really needed 90% of the time.

Conclusion

People mean very different things when they say "OOP". Most often it's enough to have classes that act as instance creators and method repositories. That can be done very easily with metamethods. It is very useful in games, where very often you need things that behave the same way but have slightly different properties.

I hope this explanation helps you get started. Good luck!
Last edited by kikito on Tue Feb 11, 2014 11:33 am, edited 2 times in total.
When I write def I mean function.
User avatar
Zilarrezko
Party member
Posts: 345
Joined: Mon Dec 10, 2012 5:50 am
Location: Oregon

Re: OOP help

Post by Zilarrezko »

uh... k... I don't see what setting a index equal to the table of which the index is in does, and I'm guessing in making the first argument in setmetatable with {name = name, parent=parent} is creating a table with those values. To be honest I might be more confused now than when I started.Why would I create a metatable for each time I create a new instance?

So let's say I want to create an entity system (not the only that goature did, because I copied that crap) I have a table and I want to create a prototype, or rather a foundation for each instance.

Code: Select all

t = {
}
prototype = {
   __index = {
         x = 0,
         y = 0,
         id = 0
      }
}
setmetatable(t, prototype)
t.james = {
}
so then in that code, would t.james have t.james.id = 0?

I just don't see how I would use speak or even how to use classes. It's like giving a child a crayon. You draw a line on a piece of paper and then you give the child the crayon and he sticks it up his nose. I can copy someones code, and I can run through it and explain it... a little. But If I was to ask to write my own code, I'm pretty much just going to be the child sticking a crayon up my ass.
User avatar
Kadoba
Party member
Posts: 399
Joined: Mon Jan 10, 2011 8:25 am
Location: Oklahoma

Re: OOP help

Post by Kadoba »

Now we're getting somewhere.
Zilarrezko wrote:uh... k... I don't see what setting a index equal to the table of which the index is in does,
Let's take a step back here. You probably understand now that __index will be checked if the values aren't found in the original table. You shouldn't have any problems understanding this:

Code: Select all

local meta = {
   __index = {  key = "value"  }
} 

local t = {}
setmetatable(t, meta)

-- now t.key  results in "value"
We actually used three tables here. One for the normal table, one for the metatable, and one for the __index. Well, there's nothing stopping us from using one table for both the metatable AND the __index.

Code: Select all

local meta = { key = "value" }
meta.__index = meta

local t = {}
setmetatable(t, meta)

-- t.key still results in "value"
There's really nothing wrong with either way but using a single table for both is typically cleaner and more often used.
I'm guessing in making the first argument in setmetatable with {name = name, parent=parent} is creating a table with those values. To be honest I might be more confused now than when I started. Why would I create a metatable for each time I create a new instance?
The first argument in setmetatable is not the metatable. It's the table you want to assign the metatable to. In otherwords, THAT is your new "instance". The function setmetatable also returns the table as well as assigning it. So both of the following peices of code do the same thing:

Code: Select all

local instance = {}
setmetatable(instance, meta)

Code: Select all

local instance = setmetatable({}, meta)
So let's say I want to create an entity system (not the only that goature did, because I copied that crap) I have a table and I want to create a prototype, or rather a foundation for each instance.

Code: Select all

t = {
}
prototype = {
   __index = {
         x = 0,
         y = 0,
         id = 0
      }
}
setmetatable(t, prototype)
t.james = {
}
so then in that code, would t.james have t.james.id = 0?

I just don't see how I would use speak or even how to use classes. It's like giving a child a crayon. You draw a line on a piece of paper and then you give the child the crayon and he sticks it up his nose. I can copy someones code, and I can run through it and explain it... a little. But If I was to ask to write my own code, I'm pretty much just going to be the child sticking a crayon up my ass.
You've almost got it here. The problem is that you are assigning the metatable to t and not t.james.

Code: Select all

t = {
}
prototype = {
   __index = {
         x = 0,
         y = 0,
         id = 0
      }
}
t.james = setmetatable({}, prototype)
User avatar
Zilarrezko
Party member
Posts: 345
Joined: Mon Dec 10, 2012 5:50 am
Location: Oregon

Re: OOP help

Post by Zilarrezko »

So now I need to create some type of function that when I call it it creates a new instance in the table "t".

Code: Select all

t = {
}
prototype = {
   __index = {
         x = 0,
         y = 0,
         text = "banana"
      }
}
id = 0
t.new = function()
   t[1 + id] = setmetatable({}, prototype)
   id = id + 1
end
but I believe if I try and do...

Code: Select all

local var = 10
for k,v in pairs(t) do
   love.graphics.print(v.text,v.x,v.y + var)
   var = var + 16
end
i'll probably get some type of error for some reason, probably because t.new is a function and the function doesn't very well have a v.text. So I should make a whole new table for the objects to store in? So...

Code: Select all

t = {
}
objects = {
}
prototype = {
   __index = {
         x = 0,
         y = 0,
         text = "banana"
      }
}
id = 0
t.new = function()
   objects[1 + id] = setmetatable({}, prototype)
   id = id + 1
end
So now I have a place where I can go through a for loop and not get any problems?
Post Reply

Who is online

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