Welcome to part 5 of my Love2d tutorial. In this part we are going to add tiles to the game.
Adding tiles
The first thing we need to do is to create three tile pieces. For this i used GIMP and made three simple squares.
Next create a directory called images and place the three tile pieces there.
Tile 1Tile 2Tile 3
The next thing to do is to create a file Tiles.lua.
Inside this file, the first thing to do is to create a tile map.
This is a 2d array using numbers to represent location of the tiles on the map
0 represents empty space
1 represents tile 1
2 is tile 2
3 is tile 3
Tile Map
For the next part, the steps are:
Create an Icon object to hold icons. it should store:
icon for the tile created with love.graphics.newImage()
width of icon using getWidth()
height of icon. Use the function getHeight()
Create Icon list.This is a list to hold the Icon objects.
Create a function which populates the icon list with the three icons tile1, tile2, and tile3.
A function to loop through the tile map and print each tile.
We use a nested for loop to loop through the tile map.
If the tile isn't zero then we print the corresponding icon from icon_list using the number in Tile_Map.
Tile objects and Tile list
Modifying main.lua
We now need to make a few changes to main. We need to call our new code and we clean up a few things we previously added.
New stuff to add
Icon_List:populate we call the populate function in our love.load() function.
drawTileMap() We add this function to love.draw() in order to draw the tiles on each update.
Cleaning up older stuff
We now clean up some of our older code to improve our main.lua file.
we change our player construct function to use an image icon using love.graphics.newImage().
we change our enemy construct function to use an image icon directly.
we change the values for WIDTH and HEIGHT to accommodate our new tile map.
updated main.lua
Updating Characters.lua
We now make a few changes to our Characters.lua file.
o.icon = icon we set the objects icon to the icon we pass to it. No more calling love.graphics.newImage()
o.y = y - o.oy we adjust the y position to account for the y offset.
Updated Characters.lua
Final remarks
Game with tiles
If all went well you should now have tiles printed to the map when you start the game.You can try adjusting the numbers in tile_map to change the map.
In the next part we will further develop the game to include collision with the tiles.
As always, you can get the complete code from github.
This is my quick and basic introduction to lua.
In this guide i will quickly walk you through the basics of lua.
What this guide is not
This guide is not a step by step introduction to programming.
Nor will this guide cover a bunch of computer science concepts.
What this guide is
This guide assume you have some understating of programming and computer science.
This guide is simply a quick introduction to the basics of lua.
This guide assumes you are familiar with basic programming and want to cover the basics of lua.
Install basics
This guide wont go into the details of installation and running a lua program.
That information is pretty well conveyed by the lua website.
once you have lua set up you can continue the rest of this guide.
The Basics of variables
The very first thing to learn is how lua handles variables.
With lua there is no type declarations or even a var keyword.
To use a variable, simple use the variable name and assign it a value, as seen in example below
You can find all the example code used in this guide over on github.
Example of using variables in lua
Variables can be assigned to any type of data, int, floats, strings, etc.
Variables can also be reassigned to a different type.
For example, a variable holding a string can then be assigned to hold an int.
By default all variables are global. To make a variable local, use the local keyword.
local variables.
Functions
Functions are declared by using the function keyword.
Return types are not declared as they are in languages such as C.
Functions are closed with the end keyword.
functions in lua.
Functions declared local have their scope limited the same as variables.
Local functions can only be called below them in the file.
Local functions can be quicker than non local function.
Functions can return no data, a single variable, or many variables.
Functions return multiple items as separate variables.
If you try to assign more variables to a function than it returns, the excess variables are assigned the value nil
tables
Tables are lua's storage device.
Understanding tables is a very big key to being good at lua.
Tables can be used as dynamic arrays are used in other languages.
Tables can also be used like associative arrays or dictionaries.
You can store almost anything you want in a table,and aren't limited to a single data type per table.
One thing to keep in mind: Tables start at 1 not 0.
Tables in lua.
Inserting data into tables
Inserting elements into a table.
There are two main ways to insert data into an array: using an index and using table.insert()
There are two forms of table.insert():
table.insert(mytable,element)
This will place the element at the end of the given table.
table.insert(mytable,index,element)
this one inserts the element at the given index into the table.
You can also assign the return data from a function to be elements in a table.
When assigning from a function returning multiple items, each item will be a sequential element in the table.
Retrieving data from table
Retrieving data from a table.
Retrieving data from a table used as an array is simple: just use the index like you would in most other language.
For tables used as associative array/dictionaries, you have two options:
use the key in mytable[key] like using an index.
Use mytable.key.
Removing elements from a table
Removing items from a table is easy, just use table.remove(mytable,index).
For an associative array/dictionary table, set the key to nil to remove the key,value pair.
Removing elements from a table.
sorting tables
Sorting tables is pretty easy. lua includes a built in quicksort for this.
sorting tables.
table.sort(mytable)
This sorts the elements in the given table numerically.
table.sort(mytable,compr)
this version takes in a function to do the comparison.
Using a custom compare function lets you sort elements in a table however you like.
That covers the basics of using tables. But to get really good, you should read up on metatables.
Metatables cover the inner workings and properties of a table.
You can get tables to do some interesting things by changing its metatable.
Printing
Printing to stdout is fairly simple
The standard way to do so is to use io.write()
Lua does have a print() function, but io.write() is the standard one to use.
Print to stdout.
Control flow
If you are familiar with any other modern programming language, then this section should be familiar.
Only things to note are lua syntax specific things such as:
if statements need an end just like functions.
Lua uses and for logic AND.
Lua uses or for logic OR.
Lua uses ~= for not equal to.
Lua uses not for logical NOT.
elseif is one word.
control flow in lua.
Loops
Loops in lua should be easy to grasp for anyone with programming experience.
Syntax for a standard for loop is var=start,stop,increment do.
repeat until("condition") loops until condition in until() is true
So you can think of it like a do while() loop but the condtion is reversed
ipairs() iterates over a table giving you an index and element on each iteration.
pairs() iterates over a table giving you a key and value on each iteration.
loops need an end just like functions and if statements.
Loops in lua.
File I/O
Basic file I/O is fairly simple.
The example code pretty much sums up the basics.
There are a few functions such as fseek() and flush() which are not covered here.
I felt that those functions were beyond the scope of my very basic introduction guide.
Reading files in lua.writing to files in lua.
Math functions
The math library for lua has the typical math functions you'd expect.
Again, if you have any programming experience then you probably already know these.
These are the basics, but lua has a few more.
Math functions in lua.
String Functions
Some of the more commonly used and useful string functions in lua.
The only thing to take note of is that the regex flavor used by lua may differ a bit from what you find elsewhere.
Needles to say, but a look at the lua reference on regex would be beneficial.
string functions in lua.
Final remarks
Thank you for checking out my basic introduction to programming in lua.
I intentionally left out things which I felt were beyond the basic scope and goal of this guide.
Two big things which are missing are metatables and OOP.
I felt that there is just too much to cover for those topics and they each deserve their own separate posts.
You can find all the example code used in this guide over on github.
Welcome to part 4 of my ongoing series about game dev using Love2d.
In this part we will expand upon the work we did previously.
First we will slightly alter how we draw images to screen.
Then we will better organize our code to move the player.
Lastly we will add the ability to run into enemies and destroy them.
Working with offsets
The typical way that love2d draws an image onto screen is to use x,y coordinates.
So far the way we have been using them, love2d has been using x,y as the upper left corner of the image.
We would like to change this so that x,y refer to the center of the image.
We will place the offsets in two variables in CHARACTER class as ox, and oy.
We will set the value to these variables using the love2d functions getHeight() and getWidth()
New variable in CHARACTER class
We will now update our draw function to accommodate these offsets.
As you can see from the image below, .draw() now has a bunch of new arguments.
If you want to know what each of them are, please see the Love2d reference
Updated draw function
You will now see that the program draws the player character a bit off screen.
You can fix this by adjusting the x and y values you use when creating him.
You will also notice that our bounds checking is slightly off now. Don't worry, we will soon fix this.
Improving movement
The first thing we are going to do is break up our current keypress function into smaller functions.
We are going to create one function per direction: up,down,left,and right.
Since no other object in the game uses these functions, we make them belong to PLAYER class.
We place these functions in our Characters.lua file.
Player movement functions
We can now clean up our keypress function.
We can simply call the corresponding function to move player that direction.
Updated keypress function
If you test it out, this now works just as before, but much better organized.
But wouldn't it be nice to be able to move around by holding down the key?
I think it would, so we can replace our love.keypress() by a new function.
We make our keyboardInput() and use the function love.keyboard.isDown("key").
After making keyboardInput we call it at the end of love.update()
our new keyboard input function
You should now be able to move the player character around by holding down a movement key.
The speed at which he moves is governed by the speed variable inside of CHARACTER class.
We next need to slightly adjust our enemy move function to account for the offsets.
Once that is done you should now see the game working as it should.
Updated ENEMY:move()
Collision detection
We are now going to add the ability to collide into an enemy.
When player character collides into an enemy, the enemy is removed from the game.
We need to check for four conditions to account for a collision:
player's head is above the enemy's feet.
The player's feet are below the enemy's head.
Player's left side is to the left of the enemy's right side.
Player's right side is to the right of the enemy's left side.
If you walk through these four steps, maybe even draw out a diagram, you will see that it accounts for a collision between the player and enemy.
We will add a function in Character.lua to account for this.
We make this function belong to ENEMY class.
You can see that it checks for a collision and returns true if player and enemy collide.
function to check for a collision
Now that we can check for a collision between a player and a single enemy, we need to expand this for all enemies.
We accomplish this by writing a function in main.lua to loop through ENEMY_LIST.
This functions checks for collision with the player on each and every enemy in ENEMY_LIST.
If a collision is detected with an enemy, we remove that enemy object from ENEMY_LIST.
Once we have this function, we need to call it at the bottom of our love.update() function.
function to check all enemies for a collision
With everything in place we should now be able to touch an enemy and have them removed from the game.
Final remarks
Thank you for checking our part 4 of my series on Love2d.
Continue on to part 5
As always, you can get the complete code from github.
This is part 3 of my ongoing series on making a simple platformer using Love2d.
For this part we are going to continue our adventure into OOP and further improve our code.
We will make a dedicated player class,start doing some polymorphism, and create some more enemies.
get organized
The first thing we will do is get better organized.
In the directory where main.lua is, make a new directory named 'images'
We will now put our character images here to help keep us organized.
In the code where we load images, we need to change 'player.png' to '/images/player.png'
Do the same for the enemy image as well.
making a player class
The next thing we need to do is create a player class. This is identical to how we made an enemy class
So far we have no unique variables or functions for player but we will change that later on.
player class
Now that we have a player class we need a constructor functions.
This is also similar to what we did with enemy class.
player constructor
We now need to change one line in main.lua to make player a PLAYER object.
making player object in main.lua
We now have a working separate class for our player
Making multiple enemies
We will now make multiple enemies in our game.
To easily handle this we are going to create a table to hold enemy objects.
And to make this even easier, we make a function to populate this list for us.
So we have ENEMY_LIST to hold our enemies and makeEnemies() to populate our table with enemy objects making list of enemies
We now have multiple enemies in our game. This can easily be expanded to make as many as we want.
Notice that the i*100 in our function just places the enemies 100 pixels a part.
Also notice that the WIDTH has been increased to accommodate these new enemies.
Drawing characters
We will now go further with our OOP adventure.
To do this we need to create a function to draw character objects to screen.
Since both player and enemy are subclasses of character, this function will work for both of them.
Hurray for polymorphism.
function to draw character objects to screen
We now need to make a function to go through enemy_list and print each enemy to screen.
To accomplish this we just need a loop to go through enemy_list, pretty simple stuff.
Notice we make use of our newly made draw function.
function to draw enemies to screen
Now that we have our function to draw objects to screen and a loop to go through each enemy, we need to update love.draw().
With just these two lines we can now print both the player and all the enemies to screen on each frame.
updated our love.draw() function
We now have the capability to print a player and any number of enemies to screen.
so far the enemies don't do anything, but we are about to change that.
Moving Enemies
We will now make our enemies move back and forth as we previously did with a single enemy.
To do this we need a function to go through ENEMY_LIST.
With each enemy object in the loop we call the move function which we previous made
function to move each enemy on screen
We now need to update our code in love.update to call this new function.
updated love.update()
we should now have three enemies moving across the screen and a player who can also move around
Enemies moving around
Final remarks
With just a little bit of reorganizing and adding a tiny bit to our previous code we can now:
make multiple enemies in our game.
print them all to screen.
have each enemy move independent of each other.
It may not seem much, but it is progress we are making on our game.
The full code for this part is available on github
Thank you for checking out part 3 of my series on game dev.continue to part 4
This is part 2 of my ongoing journey to make a simple platformer utilizing Love2d.
In this part we will utilize basic OOP functionality and also make the enemy character move across the screen.
OOP
To improve upon our code, we are going to utilize basic principles of OOP(or as OOP as lua gets.)
The first thing we can do to improve our code is make a class for our characters.
The first step is to create a CHARACTER class followed by a constructor function:CHARACTER:new()
In the constructor we make a new character object then assign the variables in that object the correct values.
In a file called 'Characters.lua' we add the code:
basic Character class
The next step is to make some changes in main.lua
When need to let lua know about our character class,so we add require "Characters"
Next we need to create character objects for the player and enemy, so we change the code in love.load()
The final bit of change is that we need to use the x and y in these objects therefore we alter the code in love.draw() and love.keypress()
The updated main.lua code can be seen below:
updated main.lua
Moving the enemy character
Now we can work on getting the enemy character to move left and right.
we want him to move right until he reaches the limit set by WIDTH, then have him move left till he hit the edge.then rinse repeat
To accomplish this we are going to add a function in Character.lua to handle this. CHARACTER:move()
As long as move_r is true then the enemy should move to the right.
We employ the same bounds checking as we did in key.press in part 1.
When the enemy reaches the right hand boundary, we have to change his direction, that's where move_r = false comes into play.
And when the enemy reaches the left hand side, we simply set move_r back to true.
You can see the complete CHARACTER:move() function below.
function to control enemy movement
We next need to update our code in main.lua
We need to use the function love.update()
This function updates at the start of each frame.
Put everything which needs to update or run with each frame here.
Inside this function we call the CHARACTER:move() function on the enemy object.
You can see our new function in the code below.
love.update()
Improving our code further
To further improve our code we can employ encapsulation.
We can create an enemy class. The player character has no need for the move function nor move_r variable
So we create a class for the enemy object and attach that function and variable to it.
We have to modify our Characters.lua file to add the enemy class, constructor function, and a slight change to the move function.
We also need to make sure to set the metatable and index of the enemy class so that it is a subclass of the character class.
The code we added is below.
Added enemy class
After adding our enemy class we need to make one slight change to our main.lua
just change the type of object for enemy in our love.load function.
making enemy object
Final
We should now have a program which does the same thing as before, but now it is much more organize.
More importantly for our needs it can much more easily be expand and added to.
Using our class system we can now easily add many new objects and add or remove functions and variables to them.
as always, complete code can be found on github.
image previously loaded with love.graphics.newImage
x
x postion of image
y
y image of image
combining all of these together into code, it should look a bit like this.
code which draws player and enemy to scren
giving us an output which looks like this:
player and enemy together on screen
Adding movement
Now that we can display the player onto the screen, we need to move him about.
To accomplish this, we will need to add a few things to our code.
The things needed are a variable for player x, a variable for player y.
We also need the function love.keypressed()
PLAYER_X and PLAYER_Y holds the location of player's x and y co-ordinates.
love.keypresed() function which handles keyboard input
We start by creating the global variables for player position.
We next need to change the code in love.draw() to use these variables.
updated load and draw functions.
the last thing needed is to set up the love.keypressed() function
We used the standard 'wasd' for movement.
We update the global variable for player x and player y when the corresponding key is pressed.
keypress function should look like:
player can now move around
We next need to set boundary for the player. We don't want the player to wander off screen.
To accomplish this, we set up two global variables: HEIGHT and WIDTH
HEIGHT controls where the bottom of the screen is, and WIDTH controls where the right hand edge of screen is.
All we have to do is add an if statement to each of the movement keys checking if player is out of bounds.
The completed code should look like this:
player can move around but can't go out of bounds.
We should now have a simple program which draws characters to the screen and allows the player to move around, but not to move out of bounds.We now know how
to draw characters to screen, take in keyboard input, and use this keyboard input to update to change things in game.