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

Show off your games, demos and other (playable) creations.
User avatar
ivan
Party member
Posts: 1915
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:Still, I think that `setfenv` is a cleaner way to define modules.
Back in the day, we had:

Code: Select all

module("mymodule")
but then they dropped it from the recent versions of Lua.
At the time, I wasn't too happy about it either.
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.
Sure, you can use locals:

Code: Select all

local _private_var = 123
local function _private_func(a,b,c)
  .....
end

local your_module = {}
function your_module.public_function(a,b,c)
  ....
end
return your_module
Locals won't be accessible outside of the module file (unless you "return" them).
`setfenv` is just an additional guard to avoid accidentally polluting the global namespace.
There's strict.lua which can help with that although it has its own caveats.

Also, note that you don't need lines like:

Code: Select all

local love = love
local setmetatable = setmetatable
local math = math
local print = print
LuaJIT probably optimizes those anyways.
Most people tend to write stuff like:

Code: Select all

local draw = love.graphics.draw
It's fine if you want to use these, but it just doesn't look "clean".
Germanunkol
Party member
Posts: 712
Joined: Fri Jun 22, 2012 4:54 pm
Contact:

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

Post by Germanunkol »

Hi,
I tried the newest version in your old thread and it works with Löve 0.10.1, but the ball gets ridiculously fast very quickly.
I can reproduce this if I try to hit the ball against the wall at a very low angle (i.e. almost aligned with the wall) which I always try because I want to get the ball up and behind the brick (quickest way to win arkanoid, I believe). So I go up to the side wall and try to get the ball past the bricks, which makes the ball hit the wall at the low angle and it speeds up very fast. It looks like a calculation error with the reflecting of the ball? A reflection shouldn't change the velocity, only the direction...?
trAInsported - Write AI to control your trains
Bandana (Dev blog) - Platformer featuring an awesome little ninja by Micha and me
GridCars - Our jam entry for LD31
Germanunkol.de
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 »

but the ball gets ridiculously fast very quickly.
I can reproduce this if I try to hit the ball against the wall at a very low angle (i.e. almost aligned with the wall) which I always try because I want to get the ball up and behind the brick (quickest way to win arkanoid, I believe). So I go up to the side wall and try to get the ball past the bricks, which makes the ball hit the wall at the low angle and it speeds up very fast.
I've made the speed growth slower.
The updated love-file is attached.

Part of the problem was in Ball:increase_speed_after_collision() function.
The speed was increasing each 5 collisions and the growth rate was exponential:

Code: Select all

function Ball:increase_speed_after_collision()
   if self.collision_counter % 5 == 0 then
      self.speed = self.speed * self.collision_speed_multiplier
   end
end
I've updated this function so that the speed increases each 10 collisions and changed the growth rate to linear:

Code: Select all

function Ball:increase_speed_after_collision()
   local speed_increase = 20
   if self.collision_counter % 10 == 0 then
      self.speed = self.speed + self.speed:normalized() * speed_increase
   end
end
So I go up to the side wall and try to get the ball past the bricks, which makes the ball hit the wall at the low angle and it speeds up very fast. It looks like a calculation error with the reflecting of the ball? A reflection shouldn't change the velocity, only the direction...?
Sometimes, due to some error in collision resolution, the ball hits the wall several times repeatedly.
This causes rapid growth of the ball's speed.
I'm not sure what exactly the problem is, so I haven't fixed it yet.
As a quick fix I can suggest to comment out the lines
self:increase_collision_counter() and self:increase_speed_after_collision() in the
Ball:react_on_wall_collision function inside the Ball.lua:

Code: Select all

function Ball:react_on_wall_collision( another_shape, separating_vector )
   self.position = self.position + separating_vector
   self.collider_shape:moveTo( self.position:unpack() )
   self:normal_rebound( separating_vector )
   self:min_angle_rebound()
   --self:increase_collision_counter()
   --self:increase_speed_after_collision()
   ball_wall_sound:play()
end
This way, the ball will not get any speed increase after collisions with the walls.
Attachments
love2d_arkanoid_tutorial.love
(2.71 MiB) Downloaded 79 times
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 »

Hey! How's the New Year going?

The tutorial has been split in 3 chapters. The idea is following:
Chapter 1 describes how to build a prototype for an Arkanoid-type game in the most straightforward way, without relying too much on any external libraries or advanced language features.
Chapter 2 shows how to make the code maintainable. It introduces basic classes, collision detection and gamestate libraries.
Chapter 3 proceeds to add details to achieve a full-featured game. I'm deliberately going to try to make articles in this chapter short, only sketching a general idea of what is going on.

The old material has been split between chapters 2 and 3.
In Chapter 2, I've updated the parts on gamestates and basic usage of tiles and sound.
9. Basic Gamestates: Game and Menu
10. More Gamestates: Gamepaused and Gamefinished
11. Basic Tiles
12. Different Brick Types
13. Basic Sound

I've made a draft version of Chapter 1.
It has 5 parts: Intro,Bricks and Wall, Collision Detection, Collision Resolution and Levels.
For this chapter, I've kept all the code in a single main.lua file and haven't used any external libraries.
I've used no-OOP approach, suggested by ivan.
The code is mostly ready; the text is in the process of preparation.

The current goals are following:
1. Finish Chapter 1.
2. Make logical transition from Chapter 1 to Chapter 2.
3. In Chapter 2, change modules to use locals instead of setfenv.
4. I'm still thinking to use classes based on metatables in Chapter 2. Most likely, thought, I'll change the class definition code.
5. In Chapter 2, replace HC with Bump.
User avatar
ivan
Party member
Posts: 1915
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

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

Post by ivan »

Hi again noway, looks good so far. Good job for the amount of effort going behind this tutorial.
I think the code has become simpler and much easier to read than before.
I see you've removed HC, which is a good move - you don't need it anyways for a tutorial like this.
Looking over the collisions code, I have a few suggestions.
The functions "collisions.check_rectangles_overlap", "collisions.ball_platform_collision", etc look practically identical.
I feel that these can be consolidated into a single function.
The "big_enough_overlap" variable, sometimes called epsilon is a good idea but I recommend avoiding it in this case.
Epsilon is useful when slopes are involved (and numeric precision is a factor),
but for axis-aligned rectangles the code should be robust enough to work without epsilon.
Check out my collision tutorial at: http://2dengine.com/doc/gs_collision.html
One simplification that you can take advantage of is keeping your objects in lists,
one for moving/dynamic objects (paddle/ball) and one for static objects (the blocks).

Code: Select all

  for i = 1, #dynamics do
    local d = dynamics[i]
    -- move based on velocity
    move(d, d.xv*dt, d.yv*dt)
    -- check and resolve collisions against static objects
    for j = 1, #statics do
      local s = statics[j]
      collision(d, s)
    end
    -- check and resolve collisions against dynamic objects
    for j = i + 1, #dynamics do
      local d2 = dynamics[j]
      collision(d, d2)
    end
  end
...
function collision(d, d2)
  if there is a collision between d and d2 then
    -- move d so that it doesn't overlap with d2
    -- adjust the velocity of d
  end
end
Hope this helps.
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 »

The functions "collisions.check_rectangles_overlap", "collisions.ball_platform_collision", etc look practically identical.
I feel that these can be consolidated into a single function.
...
Check out my collision tutorial at: http://2dengine.com/doc/gs_collision.html
One simplification that you can take advantage of is keeping your objects in lists,
one for moving/dynamic objects (paddle/ball) and one for static objects (the blocks).
Yeah, there is some code copy-paste, but I think that can be tolerated in this simple initial implementation.
I'll postpone any modifications of collision detection to later chapters.
The "big_enough_overlap" variable, sometimes called epsilon is a good idea but I recommend avoiding it in this case.
Epsilon is useful when slopes are involved (and numeric precision is a factor),
but for axis-aligned rectangles the code should be robust enough to work without epsilon.
Yes, it seems to work good enough without the check for the overlap magnitude.

I've updated the text for the first five parts (Chapter 1: Building The Prototype) and I hope this chapter won't change much:
1. The Ball, The Brick, The Platform
2. Bricks and Walls
3. Detecting Collisions
4. Resolving Collisions
5. Levels


My initial plan to switch to classes in the Chapter 2 has failed.
Such move didn't seem logical and I had to rewrite the whole chapter with no-OOP style of Chapter 1.
The code is available in the separate branch on github:
2-1: splitting code into several files
2-2: loading levels from files
2-3: straightforward gamestates
2-4: basic tiles
2-5: different brick types
2-6: basic sound
2-7: game over
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've finished rewriting the text for the chapter 2.
It introduces simple gamestates, basic graphics and sound.

Chapter 2: Common Features
1. Splitting Code into Several Files
2. Loading Levels from Files
3. Straightforward Gamestates
4. Basic Tiles
5. Different Brick Types
6. Basic Sound
7. Game Over

I think, the tutorial has come to a point where I can start to promote it a bit more actively.
So far I've only posted announcements on this forum (this thread, and the older one) and on reddit/love2d( newer, older ).
According to github traffic, this has resulted in roughly 100 unique visitiors each 2 weeks.
I think, this can and should be improved.
I've started a separate thread regarding this issue.
(besides, I plan to include an article on packaging and promoting the game in the tutorial.)

Image
User avatar
ivan
Party member
Posts: 1915
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

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

Post by ivan »

Good work, noway.
I like how it's going and the code is simple enough so that beginners can understand it.
One thing that can be improved is the "Straightforward Gamestates chapter".
I think there are better techniques for handling game states - if/else statements is just not a good direction.

States and events go hand in hand, so I would propose something like:

Code: Select all

local state = nil

function set_state(s, ...)
  -- optional: trigger enter/exit events when switching between states
  state_event('exit')
  state = s
  state_event('enter', ...)
end

function state_event(func, ...)
  if state and type(state[func]) == 'function' then
    state[func](...)
  end
end
This way each state is a Lua table encapsulating all of the code relevant to that state.
For example, the menu state would look like:

Code: Select all

local menu_state = {}

function menu_state.draw()
  love.graphics.print("Menu gamestate. Press Enter to continue.", 280, 250)
end

function menu_state.keyreleased(key, code)
  if key == "return" then
    set_state(game_state)
  elseif key == 'escape' then
    love.event.quit()
  end    
end
Next, all you have to do is re-route the Love2D callbacks through to the current state:

Code: Select all

function love.draw()
  state_event("draw")
end

function love.keyreleased( key, code )
  state_event("keyreleased", key, code)
end
The basic idea is to store the "current state" as a reference to a table - then you call functions in that table.

This is all done for the sake of encapsulation -
with the "each state is a table" approach all of the code for a given state is contained in a single file
with "if/else" statements the code related to a single state can creep up in multiple files
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 »

The basic idea is to store the "current state" as a reference to a table - then you call functions in that table.
Yes, I have been thinking to use such an approach (after all, initially I've used hump.gamestates, which does something similar).
But in the end, I've decided to postpone it to chapter 3 and write a separate part on it (something like "Improved Gamestates").
Probably, it wasn't an optimal decision, and it would have been better if I had used it instead of if-else.
In any case, I need some time to implement it.
After that, it should be easier to decide, whether to use it from the start or not.
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've rewritten the game states using tables instead of if-else'es
I agree that this topic is important and should be covered.
In the end, I've included it into the chapter 2, right after the "straightforward gamestates" part.
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest