Is this OOP?

General discussion about LÖVE, Lua, game development, puns, and unicorns.
User avatar
giantofbabil
Prole
Posts: 32
Joined: Tue Dec 15, 2015 6:07 pm

Is this OOP?

Post by giantofbabil »

I have always struggled with understanding what OOP is, which probably sounds silly to a lot of veteran programmers. I first started coding trying to learn C++ many years ago and I never got to fully understanding the language, then(still a long time ago) I took a class in Visual Basic but only learned how to make simple apps like a calculator for instance. Doing these things I never really understood OOP even though I used objects in my code. I've watched videos trying to explain it as well and I feel like most programmers make it hard to explain because they explain it in a way that makes sense to a programmer but is confusing to someone new to coding...

On to my question! Flash forward to today and I'm neck deep into programming a platformer in Love2D, I just got back into programming about a month ago and I'm having a lot of fun this time thanks to Lua and Love2D. But when you do this with tables:

Code: Select all

  local isDown = love.keyboard.isDown
  if isDown('f') and playerStats.weaponEquip == 0 and sprite.zapperGun.canShoot == true then
    --create zapper shot
    local body    = love.physics.newBody(world, sprite.body:getX(), sprite.body:getY(), 'dynamic')
    local shape   = love.physics.newRectangleShape(7, 7, 13, 13, 0)
    local fixture = love.physics.newFixture(body, shape)
    table.insert(sprite.zapperGun.zapBullet, {body = body, shape = shape, fixture = fixture:setSensor(true), body:setBullet(true), speedSet = false})
    sprite.zapperGun.zapSound:play()
    sprite.zapperGun.canShoot = false
    sprite.zapperGun.canShootTimer = sprite.zapperGun.canShootTimerMax
  end
Is that OOP? To me it looks like what I think OOP is... but like I said the definition of OOP is confusing to me. I mean here you are creating an object of the type 'zapBullet' and you can create as many of them as you'd like. Is that right?

I've also heard that Lua is a 'prototype' language and that's different than what C++ is, but don't really understand what that means either.

Code: Select all

if enemy == lowerClassSaiyan and powerLevel > 9000 then
    love.graphics.print("What?! 9000?! There's no way that could be right!", 10, 200)
else
    love.graphics.print("You fool!", 10, 200)
end
User avatar
rmcode
Party member
Posts: 454
Joined: Tue Jul 15, 2014 12:04 pm
Location: Germany
Contact:

Re: Is this OOP?

Post by rmcode »

Well you are using objects in that function, but OOP can be a lot more :D

Basically the cool thing about OOP is that you can write classes which can create tons of objects who have all the same code (so to speak), but are independent from each other.

Here's a simple class:

Code: Select all

local Ball = {}

function Ball.new()
    local self = {}

    self.x, self.y = love.math.random(  0, 100 ), love.math.random( 0, 100 )
    self.speed     = love.math.random( 10, 80  )

    function self:update( dt )
        self.x = self.x + dt * self.speed
        self.y = self.y + dt * self.speed
    end

    return self
end

return Ball
You can create a bunch of balls and each of them will have its own x and y location:

Code: Select all


function love.load()
    -- Create a bunch of balls.
    balls = {
        Ball.new(),
        Ball.new(),
        Ball.new(),
        Ball.new(),
        Ball.new()
    }
end

function love.update( dt )
    for i, v in ipairs( balls ) do
        v:update( dt )
    end
end

function love.draw()
    for i, v in ipairs( balls ) do
        love.graphics.circle('line', v.x, v.y, 5, 8)
    end
end
The benefits should be clear: We only had to write the code for once and all the balls can use it. But that's just scratching the surface. One of the other really cool things is inheritance. It basically means that you can hand down functionality from a parent to its kid. Let's extend the example from above. We now want to have squares and balls. Obviously squares also need to know their location in the world and move around BUT they certainly need to be drawn differently. Let's move the relevant to a "parent" class:

Code: Select all

local GeometryObject = {}
function GeometryObject.new()
    local self = {}

    self.x, self.y = love.math.random(  0, 100 ), love.math.random( 0, 100 )
    self.speed     = love.math.random( 10, 80  )

    function self:update( dt )
        self.x = self.x + dt * self.speed
        self.y = self.y + dt * self.speed
    end 

    return self
end
return GeometryObject
We can now inherit from that parent and use its functionality without having to write a single line of code:

Code: Select all

local Ball = {}
function Ball.new()
    local self = GeometryObject.new() -- Inherit from parent.

    function self:draw()
        love.graphics.circle('line', self.x, self.y, 5, 8)
    end

    return self
end
return Ball

Code: Select all

local Square = {}
function Square.new()
    local self = GeometryObject.new() -- Inherit from parent.

    function self:draw()
        love.graphics.rectangle('line', self.x, self.y, 20, 20)
    end

    return self
end
return Square

Code: Select all


function love.load()
    -- Create a bunch of balls.
    objects = {
        Ball.new(),
        Ball.new(),
        Ball.new(),
        Ball.new(),
        Ball.new(),
        Square.new(),
        Square.new(),
        Square.new(),
        Square.new(),
        Square.new()
    }
end

function love.update( dt )
    for i, v in ipairs( objects ) do
        v:update( dt )
    end
end

function love.draw()
    for i, v in ipairs( objects ) do
        v:draw()
    end
end
The cool thing is we can now use squares just like we used balls, but didn't have to rewrite the code. When you want to change the movement code for your objects you won't have to go through every object (squares, rectangles, circles and so on), but instead you just have to change the code in the parent class. Also note how each object now knows how to draw itself ( we simply call its draw() function ). Without OOP you'd probably have something like this in your love.draw:

Code: Select all

function self:draw()
    love.graphics.rectangle('line', rectangle1x, rectangle1y, 20, 20)
    love.graphics.rectangle('line', rectangle2x, rectangle2y, 20, 20)
    love.graphics.rectangle('line', rectangle3x, rectangle3y, 20, 20)
    love.graphics.rectangle('line', rectangle4x, rectangle4y, 20, 20)
    love.graphics.rectangle('line', rectangle5x, rectangle5y, 20, 20)
    love.graphics.rectangle('line', rectangle6x, rectangle6y, 20, 20)
end
There is a lot more to the subject and I didn't even explain the code above, but I hope it gives you an idea of what OOP CAN be (then there is also polymorphism and a lot of other cool stuff) and how it could be achieved in Lua. There also is a beginner tutorial on OOP in Lua here.
I've also heard that Lua is a 'prototype' language and that's different than what C++ is, but don't really understand what that means either.
It basically means that instead of having classes, interfaces and all the other cool features of an object oriented language (like Java) you are basically getting inheritance by cloning and moving around other objects (in the case of Lua this means tables). So Lua isn't an object oriented language by definition, but can be used in an object oriented way nonetheless.
User avatar
giantofbabil
Prole
Posts: 32
Joined: Tue Dec 15, 2015 6:07 pm

Re: Is this OOP?

Post by giantofbabil »

Wow thanks rmcode! So basically in your example because you say that "self = GeometryObject.new()" it applies the values of anything under "self" in that function(GeometryObject.new) to the function you are creating(Ball.new). That's pretty cool.

I could probably use this to streamline some of my code, but even if I don't end up using it yet it's great to understand it better. Your example was really good.

Code: Select all

if enemy == lowerClassSaiyan and powerLevel > 9000 then
    love.graphics.print("What?! 9000?! There's no way that could be right!", 10, 200)
else
    love.graphics.print("You fool!", 10, 200)
end
User avatar
Calandriel
Prole
Posts: 39
Joined: Wed Apr 22, 2015 9:00 am

Re: Is this OOP?

Post by Calandriel »

But there is no such thing as private Classes or Encapsulation in Lua, right?
If violence is not solving all your problems, You're simply not using enough of it.
Twitter: https://twitter.com/Calandriell Some news about wtf I'm doing.
Tumblr:https://www.tumblr.com/blog/calandriell I use to draw some random stuff.
User avatar
Linkpy
Party member
Posts: 102
Joined: Fri Aug 29, 2014 6:05 pm
Location: France
Contact:

Re: Is this OOP?

Post by Linkpy »

The good point with the OOP with Lua is that you do it like you want. If you want a very simple class system, with only basic inheritance, then you can make it.

But, if you want a very heavy class system (like in C++, Java) with virtual/abstract functions, interface, etc you can do it too (it will be long to implement it, maybe, but it will be your OOP). The only thing required is... To understand metatable. As soon you have understand those... thing you can do what you want !

For my part, my class system is only a function : "class" which create a... class ! You right Billy ! (I guess ?) And it use a simple inheritance with a Python-like "super" syntax :

Code: Select all

TextSprite = class():extends (AbstractSprite)

function TextSprite:init (font, text, width, align)

   -- You do the same thing in Python for inheritance.
	AbstractSprite.init (self)


	align = align or "left"

	self.font = font or lg.getFont ()
	self.text = lg.newText (self.font)
	self.text:setf (text, width, align)

	self.applyTransforms = false

end
I saw some OOP system really complex, with virtual, multiple inheritance, type checking, and many other feature. But, for myself, I use Lua and Löve because it's light and easy. So I don't see the point of making a big and complex OOP system. :emo:
Founder of NeoShadow Studio. Currently working on the project "Sirami".
github / linkpy
User avatar
rmcode
Party member
Posts: 454
Joined: Tue Jul 15, 2014 12:04 pm
Location: Germany
Contact:

Re: Is this OOP?

Post by rmcode »

Calandriel wrote:But there is no such thing as private Classes or Encapsulation in Lua, right?
There is - At least with closure based oop. It's what I use in all of my projects.

Here's an example for a class with public and private methods and attributes.
https://github.com/rm-code/logivi/blob/ ... h/File.lua

(No metatables needed)
User avatar
Karai17
Party member
Posts: 930
Joined: Sun Sep 02, 2012 10:46 pm

Re: Is this OOP?

Post by Karai17 »

It is worth noting that in game development, strict OOP is falling out of favour. Most of the big engines these days (Unreal, Unity) use a hybrid OOP/ECS setup. ECS is definitely the way to go for modern game development as it does away with the inheritance nightmare that OOP puts on you in favour for a more modular approach.

The main difference between OOP and ECS is that in OOP, each object's functionality is defined within the object and to create any new functionality, you need to create a new class that inherits some other class. This can be a problem because let's say you have a Shape superclass and then you have Circle and a Rectangle subclasses. The Shape superclass defines some basic things that all shapes should have, such as a position. The circle class defines a radius, and the rectangle class defines a width and height. So far this is simple. But what if we want to add a sphere? Now we add a Z axis to the position as defined in Shape, which is forced into circle and rectangle even if we don't want them to have a Z axis (for whatever reason). We also have the problem that a circle and a sphere are basically the same thing except that the sphere's radius can be calculated in any direction around the centre position. So do we inherit Shape, or do we inherit Circle? If circle, what else do we add to sphere to make it different? if shape, we need to re-implement the radius, duplicating code.

And that's just properties, we need to also talk about functionality. Can a circle bounce? Can a sphere? How about a cube? Do we want to break our inheritance tree up so we have BouncingShape inherit shape, and then have the sphere and cube inherit that? Do we implement bounce several times in only the shapes we want? What a nightmare!

But along comes ECS to save the day. an ECS entity, unlike an OOP object, has no functionality built in. It only has properties, or components, which are jsut key-value pairs of data. So if the entity has no functionality, how does it do anything? Well, you write independent systems that read in entities based on the components they have, and the system then processes that entity as needed.

So to use our above example, we want a few shapes, and we want some of them to bounce. Well, in ECS, we just create a blank entity, and give it a few properties to define it.

Code: Select all

local entity = {
   name = "Sphere.0001",
   shape = "Sphere",
   position = { x=0, y=0, z=0 },
   radius = 20,
   bounce = true
}
So we have some sphere with a position, radius, and a flag to say it is bouncy. Well that's neat, but how do we actually get it to bounce? We write a system that detects if an entity is bouncy, and runs calculations on it. It ignores all non-bouncy entities.

Code: Select all

local system = {
   filter = { "bounce" },
   update = function(entities, dt)
      -- do stuff
   end
}
Okay, so we are just moving functionality outside of an object and into a system. What's the big deal? Well, what if we only want some spheres to bounce? Well in OOP we would have to create a non-bouncing sphere class and re-implement the whole sphere from a different parent in the inheritance tree. With ECS, we just... turn it off. Bam. Done. We can turn it on or off any entity at all just by adding or removing the component, and it just works. The other day I was joking around and I wanted to test my player controller that I wrote so I simply added "player = true" to my terrain entity and lo and behold, my terrain could run around and jump!

ECS allows you to separate your data from your functionality, much like MVC structures. It is also modular so you can add or remove any data you want and your game will never break because of it, it will work exactly as you expect. You also have the benefit of completely avoiding nested hierarchies for inheritance and other such nonsense. It is a very fast, clean way to write game code and I highly suggest trying it out. I personally use tiny-ecs but there are others out there in the love community.
STI - An awesome Tiled library
LÖVE3D - A 3D library for LÖVE 0.10+

Dev Blog | GitHub | excessive ❤ moé
User avatar
rmcode
Party member
Posts: 454
Joined: Tue Jul 15, 2014 12:04 pm
Location: Germany
Contact:

Re: Is this OOP?

Post by rmcode »

Karai17 wrote:It is worth noting that in game development, strict OOP is falling out of favour. Most of the big engines these days (Unreal, Unity) use a hybrid OOP/ECS setup. ECS is definitely the way to go for modern game development as it does away with the inheritance nightmare that OOP puts on you in favour for a more modular approach.
Haven't used an entity system yet, but it looks nice.

The problems you describe are problems created by the programmer not by OOP itself. A lot of people seem to think that "OOP == inheritance" and that leads to the "monolithic" parent classes you often see.

Personally I am trying to find a nice way to use composition in Lua. The end result isn't much different from the ECS as far as I can tell.
User avatar
Karai17
Party member
Posts: 930
Joined: Sun Sep 02, 2012 10:46 pm

Re: Is this OOP?

Post by Karai17 »

inheritance hell isn't a requirement of OOP, but it is highly suggested in its architecture. It is commonly taught that you should not duplicate code, which is a good thing to teach. But mixing that mentality in its strictest form (you should NEVER duplicate a single line of code) with objects and suddenly you have pressure to create deeply nested inheritance trees. In the effort to never duplicate, you always write code as far up the tree as possible, often branching in weird directions to meet that goal.

The nice thing about ECS is you don't even need to worry about that at all. You can write clean, unique code without worrying about having to duplicate it. You can create as many systems as you need that do as little or as much as you want. You can also create prefab entities that you can quickly generate (and modify afterwards if necessary). I really like the concept of data-driven programming and ECS provides a very good place to learn it.

tiny-ecs has some optimizations in place such as caching so that each system doesn't need to sift through every entity. You just add an entity to the tiny world and it quickly runs it through the system filters and caches them where they need to be. Removing the entity from the tiny world also removes it from the system caches, and everyone is happy. Lua tables are great, eh?
STI - An awesome Tiled library
LÖVE3D - A 3D library for LÖVE 0.10+

Dev Blog | GitHub | excessive ❤ moé
User avatar
rmcode
Party member
Posts: 454
Joined: Tue Jul 15, 2014 12:04 pm
Location: Germany
Contact:

Re: Is this OOP?

Post by rmcode »

Karai17 wrote:inheritance hell isn't a requirement of OOP, but it is highly suggested in its architecture. It is commonly taught that you should not duplicate code, which is a good thing to teach. But mixing that mentality in its strictest form (you should NEVER duplicate a single line of code) with objects and suddenly you have pressure to create deeply nested inheritance trees. In the effort to never duplicate, you always write code as far up the tree as possible, often branching in weird directions to meet that goal.
I disagree. There is nothing about OOP itself that suggests creating crazy inheritance trees. After all it is recommended to favor composition over inheritance (https://en.wikipedia.org/wiki/Compositi ... nheritance). If a programmer decides to use inheritance only or overuse it then it's a "problem in chair not in computer" in my opinion.

But I'll leave it at that. In the end it comes down to personal preference. I just think it's absurd to say OOP produces bad code.
Post Reply

Who is online

Users browsing this forum: No registered users and 3 guests