Class 2 - Getting Started with LÖVE
Play homework
Topics
- Review: variables
- Review: functions
- Install Love
- Love framework vs Lua language
- Intro to graphics
- Shapes
- Delta time
-
Player input keypresses - Images
Description
This week we install Love and began to create video game graphics. We’ll understand the basics of a ‘game loop’, draw shapes on the screen and take player input.
Class notes
Variable Review
With programming we can do arithmetics.
What is 3 + 4?
It’s 7!
Okay well let’s test that. We can use print
to make the number appear in our output console.
print(3 + 4)
--Output: 7
Run your code (meaning press F6 and then close the window to show the output) and your console should say 7
.
Cool! Now what is a + b?
Uhm…
Well it could be anything. That’s because “a” and “b” don’t have a value. Let’s change that.
a = 5
b = 3
Let’s take another look, what is a + b? What we’re really asking is “What is the value of a + the value of b?”. In other words, what is 5 + 3? Which is 8.
To prove that a + b = 8, we’re going to print it.
a = 5
b = 3
print(a + b)
--Output: 8
Run your code again.
Here a
and b
are what we call variables. A variable is a word in which you can store a value. The number 3 is always 3, and 7 is always 7, but a variable can be anything you want it to be. Hence the name variable.
The word in which you store a value can be almost anything.
sheep = 3
test = 20
PANTS = 1040
asdfghjkl = 42
Variables are case-sensitive. That means that when you have the same word, but with different casing, it’s not treated as the same. For example
sheep = 3
SHEEP = 10
sHeEp = 200
are three different variables, with each their own value.
You can do more than just summing up numbers.
a = 20 - 10 --Substraction
b = 20 * 10 --Multiplication
c = 20 / 10 --Division
d = 20 ^ 10 --Exponentiation
For numbers with decimals we use a dot.
a = 10.4
b = 2.63
c = 0.1
pi = 3.141592
Take a look at the following code:
X = 5
Y = 3
Z = X + Y
First we say X = 5
. When we give a variable a value, we call that an assignment. We assign 5 to X
, and 3 to Y
. Next we assign X + Y
to Z
. So now Z
equals 8. Remember that you can always check the value of a variable with print
. If we were to change the value of X
or Y
after Z = X + Y
, it would not affect Z
. It would still be 8.
X = 5
Y = 3
Z = X + Y
X = 2
Y = 40
print(Z)
--Output: 8
This is because to the computer Z
is not X + Y
, it’s simply 8.
Strings
A variable can also store text.
text = "Hello World!"
This is what we call a string. Because it’s a string of characters.
We can connect strings by using two dots (..)
name = "Daniel"
age = "25"
text = "Hello, my name is " .. name .. ", and I'm " .. age .. " years old."
print(text)
--Output: "Hello, my name is Daniel, and I'm 25 years old."
Variable naming rules
There are a few rules when naming a variable. First of all, your variable may have a number in it, but not at the start.
test8 --Good
te8st --Good
8test --Bad, error!
Your variable name also can’t include any special characters like @#$%^&*.
And finally, your variable name can’t be a keyword. A keyword is a word that the programming language uses. Here’s a list of keywords:
and break do else elseif
end false for function if
in local nil not or
repeat return then true until while
Usage
Variables can be used to keep track of things. For example, we can have the variable coins
, and every time we pick up a coin we can do coins = coins + 1
.
Summary
Variables are words in which we can store a value like a number or text. You can name them whatever you want, with a few exceptions. Variables are case-sensitive.
Functions review
With functions, we can store pieces of code. This allows us to execute this code whenever we want. Functions are also known as methods
.
There are 2 ways to create a function:
example = function ()
print("Hello World!")
end
and the more common way:
function example()
print("Hello World!")
end
You start by writing the keyword function
, followed by the name of the function. A function is a type of variable, so the same rules apply as when you name a variable. This function’s name is example
. After the name we put parentheses ()
. Now we can start typing the code we want to put inside our function. In this case, we put in print("Hello World!")
When you’re done you close the function with an end
.
Note that when you run the code, you’ll see no “Hello World!” in your console, this is because we still have to execute the function. We execute a function like this:
example()
--Output: "Hello World!"
You simply type the function’s name, followed by parentheses. This is what we call a function-call.
Parameters
Take a look at this code:
function sayNumber(num)
print("I like the number " .. num)
end
sayNumber(15)
sayNumber(2)
sayNumber(44)
sayNumber(100)
print(num)
--Output:
--"I like the number 15"
--"I like the number 2"
--"I like the number 44"
--"I like the number 100"
--nil
Inside the parentheses of the function we can put what we call parameters. Parameters are temporary variables that only exist inside the function. In this case we place the parameter num
. And now we can use num
like any other variable.
We execute our function multiple times, each time with a different number. And thus each time we print the same sentence, but with a different number. The number we put inside the parentheses is what we call an argument. So in the first function-call, we pass the argument 15 to the parameter num
.
At the end of our code we print num
, outside of our function. This gives us nil
. This means that num has no value. It’s not a number, or string, or function. It’s nothing. Because like I said before, parameters are variables that are only available inside the function.
Return
Functions can return a value, which we can store inside a variable, for example. You can return a value by using the return
keyword.
function giveMeFive()
return 5
end
a = giveMeFive()
print(a)
--Output: 5
a
becomes the value that giveMeFive returns.
Another example:
-- Multiple parameters and arguments are separated by a comma
function sum(a, b)
return a + b
end
print(sum(200, 95))
--Output:
--295
Our function sum
returns the sum of a
and b
. We don’t necessarily need to put the value our function returns in a variable first. We can directly print the value.
Usage
Often you want to execute certain code in multiple locations. Instead of copying that code each time you want to use it, we can simply add a function call. And if we want to change the behaviour of this function, we only need to change it in one location, which is the function itself. This way we avoid repeating code. Don’t repeat yourself, it’s one of the most important programming principles.
Summary
Functions can store code that we can execute at any time. We call a function by writing its name followed by parentheses. We can put values inside these parentheses. These values are passed to the function’s parameters, which are temporary variables that only exist within the function. Functions can also return a value. Functions remove repetition and that is a good thing.
Installation
LÖVE
Go to love2d.org.
You should download either the 32-bit or the 64-bit installer. This depends on your system type. If you don’t know your system type, just go with 32-bit.
Open the installer. Click on Next. Click on I agree. Now you can decide where you install LÖVE. It doesn’t matter where you install LÖVE, but make sure you remember the location because we need it in a moment. This folder will be referred to as the Installation Folder.
My installation folder will be C:/Program Files/LOVE
.
Click on Next. Click on Install.
When LÖVE is done installing, click on Finish.
ZeroBrane Studio
Now we need to install a text editor. We’re going to use ZeroBrane Studio in this tutorial.
Go to studio.zerobrane.com, and click on “Download”.
Here you get the option to donate to ZeroBrane Studio. If you don’t want to donate click on “Take me to the download page this time”,
Open the installer, and install ZeroBrane Studio in your preferred folder.
When ZeroBrane Studio is done installing, open it.
Now we need to make a Project Folder. Open you file explorer and create a folder wherever you like, and name it whatever you want. In ZeroBrane Studio, click on the “Select Project Folder” icon, and select the folder you just created.
In ZeroBrane Studio, create a new file. File
-> New
, or use the shortcut Ctrl + N
.
Inside this file, write the following code:
function love.draw()
love.graphics.print("Hello World!", 100, 100)
end
Go to File
-> Save
, or use the shortcut Ctrl + S
. Save the file as main.lua
.
Go to Project
-> Lua Interpreter
and select LÖVE
.
Now when you press the F6, a window should open with the text “Hello World!”. Congratulations, you’re ready to start learning LÖVE. Whenever I tell you to run the game or run the code I’m telling you to press F6 to execute LÖVE.
In case nothing happens and the following text shows up: Can’t find love2d executable in any of the following folders, you installed LÖVE somewhere where ZeroBrane Studio can’t find it. Go to Edit
-> Preferrences
-> Settings: User
. Put in the following:
path.love2d = 'C:/path/to/love.exe'
And replace 'C:/path/to/love.exe'
with the path to where you installed LÖVE. Make sure to use frontslashes (/).
A few more things
Did you copy/paste the example code? I encourage you to type the code I show you yourself. That might seem like a lot of extra work, but by doing so it will help you memorize everything a lot better.
The one thing you don’t need to type yourself are comments.
-- This line is a comment. This is not code.
-- The next line is code:
print(123)
--Output: 123
Every line starting with 2 minus-signs (–) is a comment. The computer ignores it, meaning we can type anything we want without getting an error. I can use comments to explain certain code better. When typing over the code you don’t have to copy the comments.
With print
we can send information to our Output console. This is the box at the bottom of our editor. When you close the game, it should say the text “123”. I add the text --Output:
to show you the expected output. This is not to be confused with love.graphics.print
.
If you put the following code at the top of your main.lua
you will see the prints right away. How this works is not important, basically you turn off that the program should wait to be closed before showing the prints.
io.stdout:setvbuf("no")
Alternative text editors
What is LÖVE?
LÖVE is what we call a framework. Simply said: It’s a tool that makes programming games easier.
LÖVE is made with C++ and OpenGL, which are both considered to be very difficult. The source code of LÖVE is very complex. But all this complex code makes creating a game a lot easier for us.
For example, with love.graphics.ellipse()
, we can draw an ellipse. This is the source code behind it:
void Graphics::ellipse(DrawMode mode, float x, float y, float a, float b, int points)
{
float two_pi = static_cast<float>(LOVE_M_PI * 2);
if (points <= 0) points = 1;
float angle_shift = (two_pi / points);
float phi = .0f;
float *coords = new float[2 * (points + 1)];
for (int i = 0; i < points; ++i, phi += angle_shift)
{
coords[2*i+0] = x + a * cosf(phi);
coords[2*i+1] = y + b * sinf(phi);
}
coords[2*points+0] = coords[0];
coords[2*points+1] = coords[1];
polygon(mode, coords, (points + 1) * 2);
delete[] coords;
}
You might not understand this code above at all (I barely do so myself), and that’s exactly why we use LÖVE. It takes care of the hard parts of programming a game, and leaves the fun part for us.
Lua
Lua is the programming language that LÖVE uses. Lua is a programming language on its own, and is not made by or for LÖVE. The creators of LÖVE simply chose Lua as the language for their framework.
So what part of what we code is LÖVE, and what is Lua? Very simple: Everything starting with love.
is part of the LÖVE framework. Everything else is Lua.
These are functions part of the LÖVE framework:
love.graphics.circle("fill", 10, 10, 100, 25)
love.graphics.rectangle("line", 200, 30, 120, 100)
And this is simply Lua:
function test(a, b)
return a + b
end
print(test(10, 20))
--Output: 30
If it’s still confusing to you, that’s okay. It’s not the most important thing right now.
How does LÖVE work?
LÖVE calls 3 functions. First it calls love.load(). In here we create our variables.
After that it calls love.update() and love.draw(), repeatedly in that order.
So: love.load -> love.update -> love.draw -> love.update -> love.draw -> love.update, etc.
Behind the scenes, LÖVE calls these functions, and we to create them, and fill them with code. This is what we call a callback.
LÖVE is made out of modules, love.graphics, love.audio, love.filesystem. There are about 15 modules, and each module focuses on 1 thing. Everything that you draw is done with love.graphics. And anything with sound is done with love.audio.
For now, let’s focus on love.graphics.
LÖVE has a wiki with an explanation for every function. Let’s say we want to draw a rectangle. On the wiki we go to love.graphics
, and on the page we search for “rectangle”. There we find love.graphics.rectangle
.
This page tells us what this function does and what arguments it needs. The first argument is mode
, and needs to be of the type DrawMode
. We can click on DrawMode
to get more info about this type.
DrawMode is a string that is either “fill” or “line”, and controls how shapes are drawn.
All following arguments are numbers.
So if we want to draw a filled rectangle, we can do the following:
function love.draw()
love.graphics.rectangle("fill", 100, 200, 50, 80)
end
Now when you run the game, you’ll see a filled rectangle.
The functions that LÖVE provides is what we call the API. API stands for Application Programming Interface. You can read the Wikipedia article to learn what an API exactly is, but in this case it simply means the LÖVE functions you can use.
Summary
LÖVE is a tool that makes it easier for us to make games. LÖVE uses a programming language called Lua. Everything starting with love.
is part of the LÖVE framework. The wiki tells us everything we need to know about how to use LÖVE.
Getting started with LÖVE
What is LÖVE?
LÖVE is what we call a framework. Simply said: It’s a tool that makes programming games easier.
LÖVE is made with C++ and OpenGL, which are both considered to be very difficult. The source code of LÖVE is very complex. But all this complex code makes creating a game a lot easier for us.
For example, with love.graphics.ellipse()
, we can draw an ellipse. This is the source code behind it:
void Graphics::ellipse(DrawMode mode, float x, float y, float a, float b, int points)
{
float two_pi = static_cast<float>(LOVE_M_PI * 2);
if (points <= 0) points = 1;
float angle_shift = (two_pi / points);
float phi = .0f;
float *coords = new float[2 * (points + 1)];
for (int i = 0; i < points; ++i, phi += angle_shift)
{
coords[2*i+0] = x + a * cosf(phi);
coords[2*i+1] = y + b * sinf(phi);
}
coords[2*points+0] = coords[0];
coords[2*points+1] = coords[1];
polygon(mode, coords, (points + 1) * 2);
delete[] coords;
}
You might not understand this code above at all (I barely do so myself), and that’s exactly why we use LÖVE. It takes care of the hard parts of programming a game, and leaves the fun part for us.
Lua
Lua is the programming language that LÖVE uses. Lua is a programming language on its own, and is not made by or for LÖVE. The creators of LÖVE simply chose Lua as the language for their framework.
So what part of what we code is LÖVE, and what is Lua? Very simple: Everything starting with love.
is part of the LÖVE framework. Everything else is Lua.
These are functions part of the LÖVE framework:
love.graphics.circle("fill", 10, 10, 100, 25)
love.graphics.rectangle("line", 200, 30, 120, 100)
And this is simply Lua:
function test(a, b)
return a + b
end
print(test(10, 20))
--Output: 30
If it’s still confusing to you, that’s okay. It’s not the most important thing right now.
How does LÖVE work?
LÖVE calls 3 functions. First it calls love.load(). In here we create our variables.
After that it calls love.update() and love.draw(), repeatedly in that order.
So: love.load -> love.update -> love.draw -> love.update -> love.draw -> love.update, etc.
Behind the scenes, LÖVE calls these functions, and we to create them, and fill them with code. This is what we call a callback.
LÖVE is made out of modules, love.graphics, love.audio, love.filesystem. There are about 15 modules, and each module focuses on 1 thing. Everything that you draw is done with love.graphics. And anything with sound is done with love.audio.
For now, let’s focus on love.graphics.
LÖVE has a wiki with an explanation for every function. Let’s say we want to draw a rectangle. On the wiki we go to love.graphics
, and on the page we search for “rectangle”. There we find love.graphics.rectangle
.
This page tells us what this function does and what arguments it needs. The first argument is mode
, and needs to be of the type DrawMode
. We can click on DrawMode
to get more info about this type.
DrawMode is a string that is either “fill” or “line”, and controls how shapes are drawn.
All following arguments are numbers.
So if we want to draw a filled rectangle, we can do the following:
function love.draw()
love.graphics.rectangle("fill", 100, 200, 50, 80)
end
Now when you run the game, you’ll see a filled rectangle.
The functions that LÖVE provides is what we call the API. API stands for Application Programming Interface. You can read the Wikipedia article to learn what an API exactly is, but in this case it simply means the LÖVE functions you can use.
Summary
LÖVE is a tool that makes it easier for us to make games. LÖVE uses a programming language called Lua. Everything starting with love.
is part of the LÖVE framework. The wiki tells us everything we need to know about how to use LÖVE.
Hello, World!
There’s not much to see in the LÖVE engine. It's really a framework, meaning you can use whatever text editor you prefer. First thing to try is a "hello world" program, to make sure it launches, and to introduce you to the basic syntax of both the Lua language and the LÖVE framework. Open a text editor and enter this text:
cwide = 520
chigh = 333
love.window.setTitle(' Hello Wörld ')
love.window.setMode(cwide, chigh)
function love.load()
love.graphics.setBackgroundColor(177, 106, 248)
-- font = love.graphics.setNewFont("SkirtGirl.ttf", 72)
end
function love.draw()
love.graphics.setColor(255, 33, 78)
love.graphics.print("Hello World", cwide/4, chigh/3.33)
end
Save that as main.lua.
A distributable LÖVE package is just a standard zip file, with the .love extension. The main file, which must always be named main.lua, must be at the top level of the zip file. Create it like this (in the command line):
zip hello.love main.lua
And launch it:
love ./hello.love
Moving a rectangle
Now we can start with what I like to call “The fun part”. We’re going to make stuff move!
Let’s start with the 3 main callbacks.
function love.load()
end
function love.update()
end
function love.draw()
end
Next, we draw a rectangle.
function love.draw()
love.graphics.rectangle("line", 100, 50, 200, 150)
end
The second and third argument of this function are the x and y position.
x means “horizontal position on the screen”. 0 is the left side of the screen.
y means “vertical position on the screen”. 0 is the top side of the screen.
Now we want to make the rectangle move. It’s time to start thinking like a programmer. What exactly needs to happen in order for the rectangle to move to the right? The x-position needs to go up. 100, 101, 102, 103, 104, etc. But we can’t change 100 to 101. 100 is simply 100. We need to have something that can change in any number we want it to be. That’s right, a variable!
In love.load, create a new variable called x
, and replace the 100 in love.graphics.rectangle
with x
.
function love.load()
x = 100
end
function love.draw()
love.graphics.rectangle("line", x, 50, 200, 150)
end
So now the x-position of our rectangle is the value of x
.
Note that the variable name x
is just a name. We could’ve named it icecream
or unicorn
or whatever. Functions don’t care about variable names, it only cares about its value.
Now we want to make the rectangle move. We do this in love.update. Every update we want to increase x
by 5. In other words, x
needs to be value of x
+ 5. And that’s exactly how we write it.
function love.update()
x = x + 5
end
So now when x
equals 100, it will change x
into 100 + 5. Then next update x
is 105 and x
will change into 105 + 5, etc.
Run the game. The rectangle should now be moving.
___
Delta time
We got a moving rectangle, but there’s one small problem. If you were to run the game on a different computer, the rectangle might move with a different speed. This is because not all computers update at the same rate, and that can cause problems.
For example, let’s say that Computer A runs with 100 fps (frames per second), and Computer B runs with 200 fps.
100 x 5 = 500
200 x 5 = 1000
So in 1 second, x has increased with 500 on computer A, while on computer B x has increased with 1000.
Luckily, there’s a solution for this: delta time.
When LÖVE calls love.update, it passes an argument. Add the parameter dt (short for delta time) in the love.update, and let’s print it.
function love.update(dt)
print(dt)
x = x + 5
end
Delta time is the time that has passed between the previous and the current update. So on computer A, which runs with 100 fps, delta time on average would be 1 / 100, which is 0.01.
On computer B, delta time would be 1 / 200, which is 0.005.
So in 1 second, computer A updates 100 times, and increases x
by 5 x 0.01, and computer B updates 200 times and increases x
by 5 x 0.005.
100 x 5 * 0.01 = 5
200 x 5 * 0.005 = 5
By using delta time our rectangle will move with the same speed on all computers.
function love.update(dt)
x = x + 5 * dt
Now our rectangle moves 5 pixels per second, on all computers. Change 5 to 100 to make it go faster.
Summary
We use a variable that we increase on each update to make the rectangle move. When increasing we multiply the added value by delta time. Delta time is the time between the previous and current update. Using delta time makes sure that our rectangle moves with the same speed on all computers.
Player Input with if statements and keypresses
With if-statements, we can allow pieces of code to only be executed when a condition is met.
You create an if-statement like this:
if condition then
-- code
end
A condition, or statement, is something that’s either true or false.
For example: 5 > 9
The >
means, higher than. So the statement is that 5 is higher than 9, which is false.
Put an if-statement around the code of x increasing.
function love.update(dt)
if 5 > 9 then
x = x + 100 * dt
end
end
When you run the game you’ll notice that our rectangle isn’t moving. This is because the statement is false. If we were to change the statement to 5 < 9
(5 is lower than 9), then the statement is true, and the code inside the if-statement will execute.
With this, we can for example make x
go up to 600, and then make it stop moving, with x < 600
.
function love.update(dt)
if x < 600 then
x = x + 100 * dt
end
end
If we want to check if a value is equal to another value, we need to use 2 equal-signs (==).
For example: 4 == 7
1 equal-sign is for assigning, 2 equal-signs is for comparing.
x = 10 --Assign 10 to x
x == 10 --Compare 10 to x
We can also use >=
and <=
to check if values are higher or equal to each other or if the values are lower and equal to each other.
10 <= 10 --true, 10 equals to 10
15 >= 4 --true, 15 is higher than 4
So the code above is a shorthand for
10 == 10 or 10 < 10
15 == 4 or 15 > 4
Boolean
A variable can also be true
or false
. This type of variable is what we call a boolean.
Let’s make a new variable called move
, with the value true
, and check if move
is true
in our if-statement.
function love.load()
x = 100
move = true
end
function love.update(dt)
-- Remember, 2 equal signs!
if move == true then
x = x + 100 * dt
end
end
move
is true
, so our rectangle moves. But move == true
is actually redundant. We’re checking if it’s true that the value of move
is true
. Simply using move
as a statement is good enough.
if move then
x = x + 100 * dt
end
If we want to check if move
is false
, we can put a not
in front of it.
if not move then
x = x + 100 * dt
end
If we want to check if a number is NOT equal to another number, we use a tilde (~).
if 4 ~= 5 then
x = x + 100 * dt
end
We can also assign true
or false
to a variable with a statement.
For example: move = 6 > 3
.
If we check if move is true, and then change move to false inside the if-statement, it’s not as if we jump out of the if-statement. All the code below will still be executed.
if move then
move = false
print("This will still be executed!")
x = x + 100 * dt
end
Arrow keys
Let’s make the rectangle move based on if we hold down the right arrowkey. For this we use the function love.keyboard.isDown
(wiki).
Notice how the D of Down is uppercase. This is type of casing is what we call camelCasing. We start the first word in lowercase, and then every following word’s first character we type in uppercase. This type of casing is also what we will be using for our variables throughout this tutorial.
We pass the string “right” as first argument to check if the right arrowkey is down.
if love.keyboard.isDown("right") then
x = x + 100 * dt
end
So now only when we hold down the right arrowkey does our rectangle move.
We can also use else
to tell our game what to do when the condition is false
. Let’s make our rectangle move to the left, when we don’t press right.
if love.keyboard.isDown("right") then
x = x + 100 * dt
else
x = x - 100 * dt --We decrease the value of x
end
We can also check if another statement is true, after the first is false, with elseif
. Let’s make it so that after checking if the right arrowkey is down, and it’s not, we’ll check if the left arrowkey is down.
if love.keyboard.isDown("right") then
x = x + 100 * dt
elseif love.keyboard.isDown("left") then
x = x - 100 * dt
end
Try to make the rectangle move up and down as well.
and & or
With and
we can check if multiple statements are true.
if 5 < 9 and 14 > 7 then
print("Both statements are true")
end
With or
, the if-statement will execute if any of the statements is true.
if 20 < 9 or 14 > 7 or 5 == 10 then
print("One of these statements is true")
end
One more thing
To be more precise, if-statements check if the statement is NOT false
or nil
.
if true then print(1) end --Not false or nil, executes.
if false then print(2) end --False, doesn't execute.
if nil then print(3) end --Nil, doesn't execute
if 5 then print(4) end --Not false or nil, executes
if "hello" then print(5) end --Not false or nil, executes
--Output: 1, 4, 5
Summary
With if-statements, we can allow pieces of code to only be executed when a condition is met. We can check if a number is higher, lower or equal to another number/value. A variable can be true or false. This type of variable is what we call a boolean
. We can use else
to tell our game what to execute when the statement was false, or elseif
to do another check.
Working with images
Creating and using images is a very easy task in LÖVE. First we need an image. I’m going to use this image:
Of course, you can use any image you like, as long as it’s of the type .png. Make sure the image is in the same folder as your main.lua
.
First we need to load the image, and store it in a variable. For this we will use love.graphics.newImage(path)
. Pass the name of the image as a string as first argument. So if you have a
function love.load()
myImage = love.graphics.newImage("sheep.png")
end
You can also put your image in a subdirectory, but in that case make sure to include the whole path.
myImage = love.graphics.newImage("path/to/sheep.png")
Now our image is stored inside myImage
. We can use love.graphics.draw
to draw our image.
function love.draw()
love.graphics.draw(myImage, 100, 100)
end
And that is how you draw an image.
.draw() arguments
Let’s take a look at all the arguments of love.graphics.draw()
. All arguments besides the image are optional.
image
The image you want to draw.
x and y
The horizontal and vertical position of where you want to draw the image.
r
The rotation (or angle). All angles in LÖVE are radians. I’ll explain more about radians later.
sx and sy
The x-scale and y-scale. If you want to make your image twice as big you do
love.graphics.draw(myImage, 100, 100, 0, 2, 2)
You can also use this to mirror an image with
love.graphics.draw(myImage, 100, 100, 0, -1, 1)
ox and oy
The x-origin and y-origin of the image.
By default, all the scaling and rotating is based on the top-left of the image.
This is based on the origin of the image. If we want to scale the image from the center, we’ll have to put the origin in the center of the image.
love.graphics.draw(myImage, 100, 100, 0, 2, 2, 39, 50)
kx and ky
These are for shearing (which doesn’t have a k at all so I’m not sure what to make of it).
With it you can skew images.
love.graphics.print
, which we used before to draw text, has these same arguments.
x, y, r, sx, sy, ox, oy, kx, ky
Again, all these arguments except image can be left out. We call these optional arguments.
You can learn about LÖVE functions by reading the API documentation.
Image object
The image that love.graphics.newImage
returns, is actually an object. An Image object. It has functions that we can use to edit our image, or get data about it.
For example, we can use :getWidth()
and :getHeight()
to get the width and height of the image. We can use this to put the origin in the center of our image.
function love.load()
myImage = love.graphics.newImage("sheep.png")
width = myImage:getWidth()
height = myImage:getHeight()
end
function love.draw()
love.graphics.draw(myImage, 100, 100, 0, 2, 2, width/2, height/2)
end
Color
You can change in what color the image is drawn with love.graphics.setColor(r, g, b)
. It sets the color for everything you draw, so not only images but rectangles, shapes and lines as well. It uses the RGB-system. Although this officially ranges from 0 to 255, with LÖVE it ranges from 0 to 1. So instead of (255, 200, 40) you would use (1, 0.78, 0.15). If you only know the color using the 0-255 range, you can calculate the number you want with number/255
. There is also the fourth argument a
which stands for alpha and decides the transparency of everything you draw. Don’t forget to set the color back to white if you don’t want the color for any other draw calls. You can set the background color with love.graphics.setBackgroundColor(r, g, b)
. Since we only want to call it once, we can call it in love.load
.
function love.load()
myImage = love.graphics.newImage("sheep.png")
love.graphics.setBackgroundColor(1, 1, 1)
end
function love.draw()
love.graphics.setColor(255/255, 200/255, 40/255, 127/255)
love.graphics.setColor(1, 0.78, 0.15, 0.5)
-- Or ...
love.graphics.draw(myImage, 100, 100)
-- Not passing an argument for alpha automatically sets it to 1 again.
love.graphics.setColor(1, 1, 1)
love.graphics.draw(myImage, 200, 100)
end
Summary
We load an image with myImage = love.graphics.newImage("path/to/image.png")
, which returns an Image object that we can store in a variable. We can pass this variable to love.graphics.draw(myImage)
to draw the image. This function has optional arguments for the position, angle and scale of the image. An Image object has functions that you can use to get data about it. We can use love.graphics.setColor(r, g, b)
to change in what color the image and everything else is drawn.
Code homework
For homework, you will be creating an experimental video game character and making it interactive.
- Start out in a notebook with pen on physical paper. Come up with a name, and a short description. If it helps, consider this a character in a RPG or a platformer.
- Create the character through sketching in a notebook. Begin with pencil or pen, very rough. Make an iteration or two, then make a high quality ‘final sketch’ with art tools such as pencil, Paint, crayons, markers, etc.
- You will need to scan in your drawing and crop it to size. Remove the background and save the image as a png file with transparent background.
- Import your character into your game
- Add interactivity so a player can manipulate the character. Use WASD keys to control the movement.
- OPTIONAL: Add environmental features like terrain to the game
- OPTIONAL: Add items in the game
Credits
- Oh My Giraffe © Nico Prins, 2015
- Image of Tennis for Two (1958)
- Copyright (c) 2022 Sheepolution
- How to program games with the LÖVE gaming engine on the Raspberry Pi by Seth Kenlon CC BY-SA