This guide won't help you avoid bugs, but it will tell you how to locate and fix them.
Let's begin by having a look at the things we can use to find the source of bugs.
error traceback - This is a pointer as to where the problem occured. (even if it can point you in the wrong direction)
print - This will print one or more values and/or notes to the console
logic - The most important tool there is
let's create an example with bugs and have a look at how to fix it.
Code: Select all
x, y = 400,300
function love.update(dt)
if love.keyboard.isDown("w") then
new_y = y - dt * 40
elseif love.keyboard.isDown("s") then
new_y = y + dt * 40
elseif love.keyboard.isDown("a") then
new_x = x - dt * 40
elseif love.keyboard.isDown("d") then
new_x = x + dt * 40
end
x = new_x
y = new_y
end
function love.draw()
love.graphics.circle("line",x,y,40)
end
Let's run it and see the error traceback.
Code: Select all
Error: main.lua:17: bad argument #2 to 'circle' (number expected, got nil)
stack traceback:
[C]: in function 'circle'
main.lua:17: in function 'draw'
[string "boot.lua"]:438: in function <[string "boot.lua"]:399>
[C]: in function 'xpcall'
Let's see what we can figure out if we print it to the console.
First make sure the console is enabled in [wiki]conf.lua[/wiki].
That file have alot of fun things to play around with, the one you should enable for now is the t.console.
Now that we can print values to the console, let's add a print statement right before the error.
Code: Select all
x, y = 400,300
function love.update(dt)
if love.keyboard.isDown("w") then
new_y = y - dt * 40
elseif love.keyboard.isDown("s") then
new_y = y + dt * 40
elseif love.keyboard.isDown("a") then
new_x = x - dt * 40
elseif love.keyboard.isDown("d") then
new_x = x + dt * 40
end
x = new_x
y = new_y
end
function love.draw()
print(x, y) -- New line
love.graphics.circle("line", x, y, 40)
end
Code: Select all
nil nil
Error: main.lua:19: bad argument #2 to 'circle' (number expected, got nil)
stack traceback:
[C]: in function 'circle'
main.lua:19: in function 'draw'
[string "boot.lua"]:438: in function <[string "boot.lua"]:399>
[C]: in function 'xpcall'
So let's add some more print statements
Code: Select all
x, y = 400,300
function love.update(dt)
if love.keyboard.isDown("w") then
new_y = y - dt * 40
elseif love.keyboard.isDown("s") then
new_y = y + dt * 40
elseif love.keyboard.isDown("a") then
new_x = x - dt * 40
elseif love.keyboard.isDown("d") then
new_x = x + dt * 40
end
print("update", x, y, new_x, new_y) -- We print all 4 values and "update" so we know it was the print in love.update
x = new_x
y = new_y
end
function love.draw()
print("draw", x, y) -- We add the line "draw" before the values to ensure that this is the print in love.draw
love.graphics.circle("line", x, y, 40)
end
Code: Select all
update 400 300 nil nil
draw nil nil
Error: main.lua:20: bad argument #2 to 'circle' (number expected, got nil)
stack traceback:
[C]: in function 'circle'
main.lua:20: in function 'draw'
[string "boot.lua"]:438: in function <[string "boot.lua"]:399>
[C]: in function 'xpcall'
Now that we have located the source of the problem, it should be quite easy to solve.
Code: Select all
x, y = 400,300
function love.update(dt)
if love.keyboard.isDown("w") then
new_y = y - dt * 40
elseif love.keyboard.isDown("s") then
new_y = y + dt * 40
elseif love.keyboard.isDown("a") then
new_x = x - dt * 40
elseif love.keyboard.isDown("d") then
new_x = x + dt * 40
end
print("update", x, y, new_x, new_y) -- We print all 4 values and "update" so we know it was the print in love.update
x = new_x or x -- With the 'or' statement we ensure that x is unchanged in the cases that new_x is nil
y = new_y or y
end
function love.draw()
print("draw", x, y) -- We add the line "draw" before the values to ensure that this is the print in love.draw
love.graphics.circle("line", x, y, 40)
end
Let's run the example again just to be sure.
Looks like you have a circle moving around with the WASD keys now, a nice sign that it's working.
If you look at the console now you will see it filled with information that is no longer useful, just comment the print statements out if you think you may need them later or just remove them.
Code: Select all
x, y = 400,300
function love.update(dt)
if love.keyboard.isDown("w") then
new_y = y - dt * 40
elseif love.keyboard.isDown("s") then
new_y = y + dt * 40
elseif love.keyboard.isDown("a") then
new_x = x - dt * 40
elseif love.keyboard.isDown("d") then
new_x = x + dt * 40
end
--print("update", x, y, new_x, new_y) -- We print all 4 values and "update" so we know it was the print in love.update
x = new_x or x -- With the 'or' statement we ensure that x is unchanged in the cases that new_x is nil
y = new_y or y
end
function love.draw()
--print("draw", x, y) -- We add the line "draw" before the values to ensure that this is the print in love.draw
love.graphics.circle("line", x, y, 40)
end
Start the example again and see if you can move up and left at the same time... That's just as I suspected, you can't.
Now we use the final tool, logic, we place the print statements where we think the source of the bug is.
Now let's place some new print statements for this bug.
Code: Select all
x, y = 400,300
function love.update(dt)
if love.keyboard.isDown("w") then
print("up") -- New
new_y = y - dt * 40
elseif love.keyboard.isDown("s") then
print("down") -- New
new_y = y + dt * 40
elseif love.keyboard.isDown("a") then
print("left") -- New
new_x = x - dt * 40
elseif love.keyboard.isDown("d") then
print("right") -- New
new_x = x + dt * 40
end
--print("update", x, y, new_x, new_y)
x = new_x or x
y = new_y or y
end
function love.draw()
--print("draw", x, y)
love.graphics.circle("line", x, y, 40)
end
Let's pretend we press both W and A but not S and D, and suddenly THIS,
Code: Select all
if love.keyboard.isDown("w") then
print("up")
new_y = y - dt * 40
elseif love.keyboard.isDown("s") then
print("down")
new_y = y + dt * 40
elseif love.keyboard.isDown("a") then
print("left")
new_x = x - dt * 40
elseif love.keyboard.isDown("d") then
print("right")
new_x = x + dt * 40
end
Code: Select all
if true then
print("up")
new_y = y - dt * 40
elseif false then
print("down")
new_y = y + dt * 40
elseif true then
print("left")
new_x = x - dt * 40
elseif false then
print("right")
new_x = x + dt * 40
end
Now we see that it stops at the if and the elseif statement is never even checked.
Let's rewrite it a little bit so everything can happen at once.
Code: Select all
if love.keyboard.isDown("w") then
--print("up")
new_y = y - dt * 40
end
if love.keyboard.isDown("s") then
--print("down")
new_y = y + dt * 40
end
if love.keyboard.isDown("a") then
--print("left")
new_x = x - dt * 40
end
if love.keyboard.isDown("d") then
--print("right")
new_x = x + dt * 40
end
Now we have another bug, they just keep coming don't they?
Try holding both W and S at the same time, it moves down, it really feels like it should stand still though, but how do we do this?
If we move at different speeds up and down it may be better to add a not statement checking to make sure it does not try to walk in the opposite direction at the same time.
But since we move at the same speed there is another solutions that I will use this time.
This is by modifying new_x and new_y by new_x and new_y instead of x and y, we do this by declaring new_x and new_y prior to checking for keyboard input.
Code: Select all
x, y = 400,300
function love.update(dt)
local new_x, new_y = x, y -- Declaring new_x, new_y in a local scope, read about scopes if you don't know what I am talking about it's probably the reason for most bugs.
if love.keyboard.isDown("w") then
--print("up")
new_y = new_y - dt * 40 -- Replaced the y with the now already declared new_y
end
if love.keyboard.isDown("s") then
--print("down")
new_y = new_y + dt * 40
end
if love.keyboard.isDown("a") then
--print("left")
new_x = new_x - dt * 40
end
if love.keyboard.isDown("d") then
--print("right")
new_x = new_x + dt * 40
end
--print("update", x, y, new_x, new_y) -- We print all 4 values and "update" so we know it was the print in love.update
x = new_x -- We no longer need the nil check because new_x and new_y is always declared this time.
y = new_y
end
function love.draw()
--print("draw", x, y) -- We add the line "draw" before the values to ensure that this is the print in love.draw
love.graphics.circle("line", x, y, 40)
end
If you managed to follow this wihtout any problems, you should be able to apply the same concept to your own project and most likely be able to find and eleminate the source of your bugs.
If you find any gramatical errors or if it's something you're wondering about just post a reply.