Help with love2d/lua classes!

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
DaKillerBear1
Prole
Posts: 7
Joined: Thu Jul 07, 2016 10:52 am

Help with love2d/lua classes!

Post by DaKillerBear1 »

So I'm planning on making a very simple tank game. Now I'm going to have multiple different tanks in the game, and I want to use a class system. I have a basic under standing of classes in lua and in general. But what I'm wondering is, most tanks will have the same attributes and such (Health, armour, speed etc.) so I was wondering, should I make just one class maybe called "Tank" and have many subclasses, or make a class for each tank?

What I want to know is:
* What should I use and why
* How would I do it
* How to handle special cases (Different tank variants T34, T34-85 etc)
* Examples!
User avatar
Chef_Panic
Prole
Posts: 5
Joined: Sun Jan 05, 2014 9:05 pm

Re: Help with love2d/lua classes!

Post by Chef_Panic »

As someone who comes from Java land, I'm always interested in OOP implementations for Lua. I've used Middleclass in the past and it's worked beautifully! I recommend Middleclass because it offers basic OOP like constructor chaining, class inheritance, etc. but also some more advanced features like mixins and typeof/instanceof functionality.

But I will also say that sometimes you can restructure your code into a more component-entity type of system that doesn't rely on OOP as much. But I think either approach is dependent on what you're making :-)
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: Help with love2d/lua classes!

Post by kikito »

hi there!

One thing that took me a lot of time to understand about programming is this: there's almost never a "objective best" way to do things. This is because *you* will be the person doing it, and that's a very important factor.

I will tell you what *I* would do but you need a "subjective best": the best option for *you*, considering your current knowledge, state of mind, previous experiences and personal preference.

If I were doing your game, I would use middleclass, because it's my library, so it's the one I know best :). I would use a two-level hierarchy: A base "Tank" class and one subtank class per "tank model"- ShermanTank, MerkavaTank, etc. The tanks would be instances of these classes. So when you do MerkavaTank:new(x,y) you create a new tank in position x,y. I would probably use one extra class for each "variation" too - a class would represent "how a tank is right when it's fresh out of the factory".

How the rest would be done depends a bit on the kind of game you want to do: namely, how much complexity you want to add to your tanks. If you are ok with all the tanks "behaving the same way" (i.e. all of them can move, all of them can fire from their main turret, etc) so that some "common attributes" (like speed, armor, etc) govern all of them, then I would make the Tank class set them to default values on the constructor. It would also implement all the "common" methods (move, fire, etc).

Code: Select all

 -- tank.lua
local class = require 'middleclass'
local Tank = class('Tank')

function Tank:initialize(x,y)
  self.x, self.y = x,y
  self.turretAngle = 0
  self.bodyAngle = 0
  self.health = 100
  self.firePower = 25
  self.speed = 100
  ...
end

function Tank:fire()
  ... -- create a projectile at self.x,self.y with self.firePower going towards the angle self.bodyAngle + self.turretAngle
      -- maybe play self.fireSound ?
end

function Tank:draw()
  ... -- draw the body and turret using self.bodyImage and self.turretImage
end

... -- (other methods like rotating the body, advancing, rotating the turret, etc)

return Tank
And then each tank model would just override the default ones on its constructor - and maybe do visual stuff like setting the images and sounds for each model, if you are considering doing that

Code: Select all

-- sherman_tank.lua
local class = require 'middleclass'
local Tank = require 'tank'

local ShermanTank = class('ShermanTank', Tank)

function ShermanTank:initialize(x,y)
  Tank.initialize(self, x,y)
  self.firePower = 40 -- overrides default firePower
  self.turretImage = ...
  self.bodyImage = ...
  self.fireSound = ...
end

return ShermanTank
Since ShermanTank is a subclass of Tank, it would inherit all the tank methods. So it would be able to fire, move, etc.

If you wanted to model more complex things than an abstract hp option (like "this tank's roadwheels have been destroyed" or "the turret has been impacted and its accuracy won't be optimal") then I would use a different approach: each tank would have an "inventory of parts", each one with its own capacities (hp, armor, capacity to fire, capacity to move) so they would be modelled independently. But this would be quite complex, and require lots of settings. You probably want to start with the version I told you above.
When I write def I mean function.
DaKillerBear1
Prole
Posts: 7
Joined: Thu Jul 07, 2016 10:52 am

Re: Help with love2d/lua classes!

Post by DaKillerBear1 »

kikito wrote:hi there!

One thing that took me a lot of time to understand about programming is this: there's almost never a "objective best" way to do things. This is because *you* will be the person doing it, and that's a very important factor.

I will tell you what *I* would do but you need a "subjective best": the best option for *you*, considering your current knowledge, state of mind, previous experiences and personal preference.

If I were doing your game, I would use middleclass, because it's my library, so it's the one I know best :). I would use a two-level hierarchy: A base "Tank" class and one subtank class per "tank model"- ShermanTank, MerkavaTank, etc. The tanks would be instances of these classes. So when you do MerkavaTank:new(x,y) you create a new tank in position x,y. I would probably use one extra class for each "variation" too - a class would represent "how a tank is right when it's fresh out of the factory".

How the rest would be done depends a bit on the kind of game you want to do: namely, how much complexity you want to add to your tanks. If you are ok with all the tanks "behaving the same way" (i.e. all of them can move, all of them can fire from their main turret, etc) so that some "common attributes" (like speed, armor, etc) govern all of them, then I would make the Tank class set them to default values on the constructor. It would also implement all the "common" methods (move, fire, etc).

Code: Select all

 -- tank.lua
local class = require 'middleclass'
local Tank = class('Tank')

function Tank:initialize(x,y)
  self.x, self.y = x,y
  self.turretAngle = 0
  self.bodyAngle = 0
  self.health = 100
  self.firePower = 25
  self.speed = 100
  ...
end

function Tank:fire()
  ... -- create a projectile at self.x,self.y with self.firePower going towards the angle self.bodyAngle + self.turretAngle
      -- maybe play self.fireSound ?
end

function Tank:draw()
  ... -- draw the body and turret using self.bodyImage and self.turretImage
end

... -- (other methods like rotating the body, advancing, rotating the turret, etc)

return Tank
And then each tank model would just override the default ones on its constructor - and maybe do visual stuff like setting the images and sounds for each model, if you are considering doing that

Code: Select all

-- sherman_tank.lua
local class = require 'middleclass'
local Tank = require 'tank'

local ShermanTank = class('ShermanTank', Tank)

function ShermanTank:initialize(x,y)
  Tank.initialize(self, x,y)
  self.firePower = 40 -- overrides default firePower
  self.turretImage = ...
  self.bodyImage = ...
  self.fireSound = ...
end

return ShermanTank
Since ShermanTank is a subclass of Tank, it would inherit all the tank methods. So it would be able to fire, move, etc.

If you wanted to model more complex things than an abstract hp option (like "this tank's roadwheels have been destroyed" or "the turret has been impacted and its accuracy won't be optimal") then I would use a different approach: each tank would have an "inventory of parts", each one with its own capacities (hp, armor, capacity to fire, capacity to move) so they would be modelled independently. But this would be quite complex, and require lots of settings. You probably want to start with the version I told you above.

I quite like this idea! I'll do my best, but I'm starting to realize I might not be as experianced as I thought at first xD But If I understand it correctly I create a "main" class, probabably called "Tank", and create all of the needed functions, basic variables in there, and then later on I just create multiple subclasses (Which, due to innheritance) need not have all the functions that the base class have, as they already do? Also where do you suggest I store all my instances of tanks? Should I make a table and store it in the main Tank class file? And to update maybe do:

Code: Select all

tanks = {}

for k, v in ipairs(tanks) do
	v:updt(dt)
end
or something along those lines?

P.S. Also, what differs "Tank:initialize" from "tank:new" ?
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: Help with love2d/lua classes!

Post by kikito »

DaKillerBear1 wrote:If I understand it correctly I create a "main" class, probabably called "Tank", and create all of the needed functions, basic variables in there, and then later on I just create multiple subclasses (Which, due to innheritance) need not have all the functions that the base class have, as they already do?
That's the implementation I was proposing for this particular example. It is not the only possible way of doing things in general.

DaKillerBear1 wrote:Also where do you suggest I store all my instances of tanks? Should I make a table and store it in the main Tank class file?
You can do that, in the sense that nothing will physically prevent you from doing it. But I would not recommend it.

The reason is that the Tank file / class already has a clear responsibility: it is in charge of specifying how a generic tank behaves. A class with a single responsibility is a Good Thing. There is a programming principle named after this. If you add that "list of tanks" there too, then it will have two responsibilities on the class/file: Defining how the tanks behave AND keeping a list of tanks.

The place were you should put the tanks depends a bit on what your game should do. Will it have projectiles? Explosions? Maybe buildings? Without knowing these things, it's difficult to answer precisely, but I can give you a generic answer: the entity responsible for storing references to the "game entities" of a game is usually another entity called "game". It could be an instance of a Game class. It could also be a plain Lua table. If you used a library, like bump.lua, for the collisions, you could store the references to the tanks, bullets, explosions etc inside the "bump world" (which would probably be stored inside the game entity).
DaKillerBear1 wrote:

Code: Select all

for k, v in ipairs(tanks) do
	v:updt(dt)
end
I strongly recommend that you use complete words when naming methods: v:update(dt). Being able to easily read out loud the names of your variables is very useful when communicating with others (our your future self).
DaKillerBear1 wrote: P.S. Also, what differs "Tank:initialize" from "tank:new" ?
When you call Tank:new(100,200), the last step it does is invoking Tank:initialize with the parameters 100 & 200. They are not equal because Tank:new does more stuff in addition to calling Tank:initialize. Here's its implementation: https://github.com/kikito/middleclass/b ... #L123-L128
When I write def I mean function.
DaKillerBear1
Prole
Posts: 7
Joined: Thu Jul 07, 2016 10:52 am

Re: Help with love2d/lua classes!

Post by DaKillerBear1 »

You can not belive how glad I am to have someone like yourself helping me! :D Do you know of any library that can handle collisions with rotated rectangles?
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: Help with love2d/lua classes!

Post by kikito »

DaKillerBear1 wrote:You can not belive how glad I am to have someone like yourself helping me! :D Do you know of any library that can handle collisions with rotated rectangles?
Both HC and [wiki]love.physics[/wiki] can handle those.
When I write def I mean function.
User avatar
Ulydev
Party member
Posts: 445
Joined: Mon Nov 10, 2014 10:46 pm
Location: Paris
Contact:

Re: Help with love2d/lua classes!

Post by Ulydev »

That was an interesting read, thank you!
As someone not very familiar with OOP it helped me understand some concepts and best practices, especially for the main Game class.
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 232 guests