[HOWTO]Debug code
Posted: Thu Jul 24, 2014 3:04 pm
We all had bugs, but as you get better the less you will create.
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.
This is one bug-filled code, but may seem perfectly fine to people with less experience.
Let's run it and see the error traceback.
The error was on line 17 according to the error traceback, and that is not entirely true.
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.
Let's look at the console output again
We now know that both values are nil and we should look at places where we set the values.
So let's add some more print statements
Let's look at the new console output.
It looks like 'x' and 'y' is not nil in love.update but it looks like 'new_x' and 'new_y' is.
Now that we have located the source of the problem, it should be quite easy to solve.
This would make sure 'x' and 'y' is never set to nil in the update function.
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.
But let's have a look at another type of bug now, one that does not give you an error message.
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.
If you run this example you may notice some things, and if you have some idea of how programming works you should be a step ahead and see what the problem is, but it may not always be this easy.
Let's pretend we press both W and A but not S and D, and suddenly THIS,
Turn into THIS,
You should know how if and elseif statements work, if not look at some tutorials on lua and re-read this guide once you have a better understanding of the language in general.
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.
We can now move it freely both up and to the left at the same time, elseif has alot of uses but this was a case where end followed by a new if was a better solution.
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.
Now if you try to run this it should work just as expected, and you are done with debugging it.
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.
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.