Need Critique on Tutorial Series on Making Arkanoid-type Game.

Show off your games, demos and other (playable) creations.
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: Need Critique on Tutorial Series on Making Arkanoid-type Game.

Post by airstruck »

ivan wrote:That would be the fastest way to evoke functions, but clunky and slower to allocate (looks a little bit like mixins). In short, have to manually move data around when you don't use metatables.
If you were really creating a ton of these things and not calling many methods on them, metatables would probably edge them out performance-wise. I just recommended them as a nice way to avoid the "teach-a-library" vs. "teach metatables" decision. You can get your OO from basic language constructs (functions, tables) and teach metatables later on.

Of course, you could also start with a procedural API as you suggested, and then if you want OO down the road you can probably re-purpose it as an __index for objects. I'm not sure there's really any point to that as far as learning curve is concerned, though. OO is easy enough to understand, especially if you don't bother people with notions like classes.

Code: Select all

local function Guy (name)
    return {
        name = name,
        say = say,
    }
end
It's not a class. It's just a function; maybe a "factory" function.

Code: Select all

local function Dog (t)
    t = t or {}
    t.name = t.name or "a dog"
    t.say = bark
    
    return t
end
Now it's a "decorator" that doubles as a factory.

Code: Select all

local function Guard (t) t.patrol = patrol; return t end
local function Guide (t) t.lead = lead; return t end

local function GuardDog (t) return Guard(Dog(t)) end
local function GuideDog (t) return Guide(Dog(t)) end
local function GuardGuy (t) return Guard(Guy(t)) end
local function GuideGuy (t) return Guide(Guy(t)) end
Composition is simple, no need to bother with long, brittle inheritance trees / multiple inheritance / mixins / redundancy / etc.
User avatar
Positive07
Party member
Posts: 1014
Joined: Sun Aug 12, 2012 4:34 pm
Location: Argentina

Re: Need Critique on Tutorial Series on Making Arkanoid-type Game.

Post by Positive07 »

As airstruck and I previously said, if you do MANY simple assignments then metatable is better, but if you need a few (should benchmark) then what you lost by assignment you gain it by the fact than indexing a metatable is slower.

When you have many more then indexing is not that bothersome and assignment consumes more time time than the time you get by the faster indexing. Again since we don't have real numbers there is no way to test this out.

I commonly use metatables because they are easier to write but each methods has it's merits... Performance wise I would say not to bother, it won't probably be a bottleneck, and preamature optimization is the root of all evil. I recommend to use whatever you feel confortable with, and understand, then you can move to more complex stuff, but if you can then learn new things! that's the best you can do!
for i, person in ipairs(everybody) do
[tab]if not person.obey then person:setObey(true) end
end
love.system.openURL(github.com/pablomayobre)
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Need Critique on Tutorial Series on Making Arkanoid-type Game.

Post by ivan »

Positive07 wrote:As airstruck and I previously said, if you do MANY simple assignments then metatable is better, but if you need a few (should benchmark) then what you lost by assignment you gain it by the fact than indexing a metatable is slower.
Don't forget that without metatables you are basically making a copy/reference of every function so the memory usage would be higher too. So yea, metatables is usually the most efficient approach for OO in Lua.
User avatar
Positive07
Party member
Posts: 1014
Joined: Sun Aug 12, 2012 4:34 pm
Location: Argentina

Re: Need Critique on Tutorial Series on Making Arkanoid-type Game.

Post by Positive07 »

Yes, I agree, but is not the simpler to grasp for beginners. It's easy to go from one to the other anyway... Except for clojures with private data, those are harder
for i, person in ipairs(everybody) do
[tab]if not person.obey then person:setObey(true) end
end
love.system.openURL(github.com/pablomayobre)
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: Need Critique on Tutorial Series on Making Arkanoid-type Game.

Post by airstruck »

ivan wrote:Don't forget that without metatables you are basically making a copy/reference of every function so the memory usage would be higher too.
Now that's an interesting point. Here's a little thought experiment. In the context of an Arkanoid game, you're talking about maybe ~500 objects max (one for each brick, a paddle, a few balls, powerups, bullets). Let's just say they have 4 methods each and 4 numeric properties each. That's 4 extra function references per object for the ad-hoc solution. Under LuaJit, this means about 115K total memory for the ad-hoc solution and 68K total for the metatable solution.

Looking at it one way, that's about two thirds more memory, which sounds significant. Looking at it another way, unless we're going to run this on a Commodore 128, it's pretty much irrelevant.

It's good to think about performance. Beyond that, some things to consider here might be flexibility, simplicity, "learn-ability," and whether it's possible to take advantage of a language-agnostic style.
User avatar
Positive07
Party member
Posts: 1014
Joined: Sun Aug 12, 2012 4:34 pm
Location: Argentina

Re: Need Critique on Tutorial Series on Making Arkanoid-type Game.

Post by Positive07 »

BENCHMARKSSSSS Please!! Otherwise this discussion about optimization is pretty much in the air.

The benchmark should:
  • Check how much time it takes to create the functions, done once, around 10 functions should be pretty similar to a real use case
  • Check how much time it takes to allocate N objects where N is at least 300
  • Check how much time it takes to call all methods, in a loop M times, where M is at least 1000
  • Check how much memory each method consumes
Anyone that wants to do this I would appreciate it.

To me, starting at the second point because first one is reference only, the second is a win for the metatable, the third is a win for the simple assignment, and the fourth is a win to the metatables. Proving that each one has their own benefits. If speed is the concern then simple assignment wins, if memory/allocation is a concern then metatable wins
for i, person in ipairs(everybody) do
[tab]if not person.obey then person:setObey(true) end
end
love.system.openURL(github.com/pablomayobre)
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Need Critique on Tutorial Series on Making Arkanoid-type Game.

Post by ivan »

Sorry for the topic hijack, noway!
Going back to the original discussion,
you really want to make sure your tutorial code is so simple
that everybody reading it will say "yes, this is clearly the easiest way to make breakout".
That's why I recommend avoiding OOP altogether.
Aside from that I noticed a few things in the code that can be cleaned up.
These are not mistakes, but it just looks like you're coming from a language other than Lua:

Code: Select all

function Ball:draw()
   local segments_in_circle = 16
   love.graphics.circle( 'line', self.position.x, self.position.y, self.radius, segments_in_circle )
end
Can become:

Code: Select all

local segments_in_circle = 16
function Ball:draw()
   love.graphics.circle( 'line', self.position.x, self.position.y, self.radius, segments_in_circle)
end
Or better yet:

Code: Select all

function Ball:draw()
   love.graphics.circle( 'line', self.position.x, self.position.y, self.radius, 16)
end

Code: Select all

local Gamestate = require "gamestate"
menu = {} -- you can safely delete this
game = {} -- and this line
local menu = require "menu"
local game = require "game"
Note that setfenv is not commonly used (at least I don't see it that often)
except for peculiar things like loading Lua code from untrusted locations.
I understand it can be used to make modules too...
IMO it's just not necessary to use 'setfenv' in this case.

If you decide to stick with Vrld's libraries (which are very good) then...

Code: Select all

vector( platform_starting_x_pos + o.separation_from_platform_center.x,
          platform_starting_y_pos + o.separation_from_platform_center.y )
...you might as well go crazy with the vector operators:

Code: Select all

local platform_starting_pos = vector(500, 500)
....
o.position = o.position or (platform_starting_pos + o.separation_from_platform_center)
These are just minor gripes,
good job with the tutorial overall,
looks like a lot of effort went into it.
noway
Prole
Posts: 43
Joined: Mon Mar 21, 2011 7:58 am

Re: Need Critique on Tutorial Series on Making Arkanoid-type Game.

Post by noway »

Sorry for the topic hijack, noway!
No problem. It's interesting to follow.
Besides, it keeps the topic up :megagrin:
These are not mistakes, but it just looks like you're coming from a language other than Lua:
Indeed, I don't have much experience with Lua, and so I'm unaware of some of the common practices and most likely I do some things not the "Lua way". It seems to me, that Lua favors make-it-from-scratch-for-this-particular-case approach, whereas I tend to use more universal ready-to-go solutions. This sometimes overcomplicates things...
you really want to make sure your tutorial code is so simple
that everybody reading it will say "yes, this is clearly the easiest way to make breakout".
That's why I recommend avoiding OOP altogether.
Probably you are right that OOP should not be used unless absolutely needed.
On the other hand, I don't think that declaring a class should be such a big deal.
Once you understand, how __index works, it's not a problem any longer.
Note that setfenv is not commonly used (at least I don't see it that often)
except for peculiar things like loading Lua code from untrusted locations.
I understand it can be used to make modules too...
IMO it's just not necessary to use 'setfenv' in this case.
I've picked it from Programming in Lua ( 3rd ed. , sec. 15.3; relevant excerpt )
In short, this allows to avoid declaring all functions local, or prefixing them with the module name (in the case of classes, the latter is unavoidable though) .

Code: Select all

local Gamestate = require "gamestate"
menu = {} -- you can safely delete this
game = {} -- and this line
local menu = require "menu"
local game = require "game"
In part 9, yes.
But in general, gamestates can circularly require one another and it becomes necessary to declare them in advance.
Or better yet:

Code: Select all

function Ball:draw()
   love.graphics.circle( 'line', self.position.x, self.position.y, self.radius, 16)
end
I use a simple test in cases like this: "Six months from now, would I be able to tell, what this '16' means" ?
I had to add the local variable.

These are not mistakes, but it just looks like you're coming from a language other than Lua:

Code: Select all

function Ball:draw()
   local segments_in_circle = 16
   love.graphics.circle( 'line', self.position.x, self.position.y, self.radius, segments_in_circle )
end
Can become:

Code: Select all

local segments_in_circle = 16
function Ball:draw()
   love.graphics.circle( 'line', self.position.x, self.position.y, self.radius, segments_in_circle)
end
If this is for perfomance/memory reasons, I would prefer to avoid such optimization tweaks, and leave them
to more advanced tutorial.

If you decide to stick with Vrld's libraries (which are very good) then...

Code: Select all

vector( platform_starting_x_pos + o.separation_from_platform_center.x,
          platform_starting_y_pos + o.separation_from_platform_c
enter.y )

...you might as well go crazy with the vector operators:

Code: Select all

local platform_starting_pos = vector(500, 500)
....
o.position = o.position or (platform_starting_pos + o.separation_from_platform_center)
Yes, this probably should be corrected.
It first appears in the part 16. I'm surprised, that someone got this far :)
I'll try to fix it.
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Need Critique on Tutorial Series on Making Arkanoid-type Game.

Post by ivan »

noway wrote:I've picked it from Programming in Lua ( 3rd ed. , sec. 15.3; relevant excerpt )
In short, this allows to avoid declaring all functions local, or prefixing them with the module name (in the case of classes, the latter is unavoidable though) .
I understand, all I'm saying is that setfenv/_ENV is not the 'typical' way that most Lua modules are written. The majority of the lib/modules you'll find in the wild are composed of a table with some functions. Take a look the HC code, for example. The Lua manual changes and seems to be more about exploring different techniques. So there is certainly a discrepancy between common practice and the examples from the manual.
noway wrote:If this is for perfomance/memory reasons, I would prefer to avoid such optimization tweaks, and leave them
to more advanced tutorial.
No, it's not a question of performance. It just looks strange to declare a local variable that doesn't change so you can pass it as a parameter.
A comment would be more suitable:

Code: Select all

love.graphics.circle( 'line', self.position.x, self.position.y, self.radius, 16) -- where 16 is the number of vertices in the circle
It first appears in the part 16. I'm surprised, that someone got this far :)
To be honest I just follow the code examples, and refer to the descriptions/text only if something is confusing or unclear.
noway
Prole
Posts: 43
Joined: Mon Mar 21, 2011 7:58 am

Re: Need Critique on Tutorial Series on Making Arkanoid-type Game.

Post by noway »

I understand, all I'm saying is that setfenv/_ENV is not the 'typical' way that most Lua modules are written. The majority of the lib/modules you'll find in the wild are composed of a table with some functions. Take a look the HC code, for example. The Lua manual changes and seems to be more about exploring different techniques. So there is certainly a discrepancy between common practice and the examples from the manual.
Probably you are right about that after all.
I'll consider to remove `setfenv`s from the code.
Of course, I've seen several other libraries.
Still, I think that `setfenv` is a cleaner way to define modules.
I'll probably include that approach as an optional material.
The majority of the lib/modules you'll find in the wild are composed of a table with some functions.
It seems so, unfortunately.
`setfenv` is just an additional guard to avoid accidentally polluting the global namespace.
You still have to return a table from the file with the module definitions.
If you want to make some functions private, it would be necessary to explicitly return a table with public functions only, instead of returning the whole module table. I don't do that in my code - and that is just my mistake.
Post Reply

Who is online

Users browsing this forum: No registered users and 132 guests