Re: Tutorial requests and ideas
Posted: Mon Mar 10, 2008 2:33 am
Well, it took way more than expected, but I finally managed to put together my humble little tile-based scrolling tutorial. As many might already know, coding a real, efficient, complete tile engine is no trivial task, so I decided to go with the very basic idea: draw a simple tilemap and be able to scroll it. Hopefully, this will give our belöved lövers the basic tools they need to code their own engines. Without further ado, here it is:
Tile-based scrolling
In this little tutorial we will cover a widely used technique used to draw game backgrounds: tilemaps. Using this technique, our backgrounds are going to be made up of pieces (tiles) that, drawn together, will allow us to create bigger and more complex levels without using huge amounts of memory. The basic idea is to have a 2d value table, where each value represents a tile. Using that table, we will know what tiles to draw at a certain position, given some basic parameters like the map width, height and the current coordinates of an imaginary viewport. Don't worry if you don't understand these terms right now, I'll explain them in more detail as we start coding.
So, let's start with some variables:
We first create a Lua table to store our tiles. As I already said, these are little bitmaps we'll use to draw our maps, something like mosaics, wich are made up of smaller pieces. Since this will be a very simple tilemap, we'll just use four tiles, loading them inside a for loop.
Now, the next thing we have to do is set up a handful of map and tile variables to be able to draw the map correctly. This ones should be pretty self-explanatory: besides height and width of the tiles and our tilemap, we also have the size of the region of tiles to display. The two offset variables are used to define the point where we will start drawing out tilemap. You migh want to play a little with these variables to see their effects.
Now, to the map structure itself:
As you can see, not much magic here, we just define a 2d table where each number represents an index of our tile table. In our example, the zero represents grass, the one represents dirt, the two represents stone and the three represents water. Obviously, your tiles may represent whatever you want, even parts of a bigger structure if you so desire.
Let's continue:
To keep our draw callback clean, I decided to move our little map drawing routine its own function. Again, nothing really impressive here, we just go through our map table, checking every column (x values) for each row (y values). You can draw as many tiles as you want, just change the map_display_h and map_display_w variables accordingly. Just notice that if you give them too high values you'll start drawing tiles off-screen, wasting time and resources. You can change the coordinates of the region to be drawed just by changing the map_x and map_y values.
Obviously, you have to call draw_map() on your draw() callback. If you run your example right now, you should see our little tilemap on screen. You can't do any scrolling right now, but we'll get onto that right now, adding the following to our update() callback:
This is fairly simple: first, we check if the arrow keys are being pressed and we update the map_x and map_y variables accordingly. Remember, here we're talking on tile coordinates so when we're increasing or decreasing map_x or map_y by one, we're actually moving 64 px (the size of a single tile). Finaly, to avoid off-map scrolling, we include some boundary checking code.
Now go and run your script. Isn't LÖVE cool? You bet it is, congratulations on making your first tilemap scroller!
Closing words
While this little example should be enough to get you up and running, there are quite a few possible enhancements:
- Writing our maps by hand is a little stupid, we should make them with tools like Tiled and load them in our game.
- Drawing characters inside the tilemap.
- Fine scrolling: As you probably noticed, the map scrolls tile by tile and you probably don't want that if you need to move your characters/object pixel by pixel.
- Multi-layered maps: Most real tile engines are able to render more than one layer. Together with fine scrolling, you would be able to scroll a near layer at a certain speed and move another one behind it more slowly.
- Character to map collisions.
-- END OF TUTORIAL
Teh screen:
Teh filez:
http://www.cloverpunk.com.ar/tehstuffs/love-tut2.love
EDIT: Fixed typo, thanks Sardtok n_n
EDIT2: Seems like my hosting is acting up. I'll annoy the hell out of a few support guys...
Tile-based scrolling
In this little tutorial we will cover a widely used technique used to draw game backgrounds: tilemaps. Using this technique, our backgrounds are going to be made up of pieces (tiles) that, drawn together, will allow us to create bigger and more complex levels without using huge amounts of memory. The basic idea is to have a 2d value table, where each value represents a tile. Using that table, we will know what tiles to draw at a certain position, given some basic parameters like the map width, height and the current coordinates of an imaginary viewport. Don't worry if you don't understand these terms right now, I'll explain them in more detail as we start coding.
So, let's start with some variables:
Code: Select all
-- our tiles
tile = {}
for i=0,3 do
tile[i] = love.objects:newImage( "gfx/tex"..i..".jpg" )
end
love.graphics:setFont(love.objects:newFont(love.default_font, 12))
-- map vaiables
map_w = 20
map_h = 20
map_x = 0
map_y = 0
map_offset_x = 30
map_offset_y = 30
map_display_w = 14
map_display_h = 10
tile_w = 48
tile_h = 48
Now, the next thing we have to do is set up a handful of map and tile variables to be able to draw the map correctly. This ones should be pretty self-explanatory: besides height and width of the tiles and our tilemap, we also have the size of the region of tiles to display. The two offset variables are used to define the point where we will start drawing out tilemap. You migh want to play a little with these variables to see their effects.
Now, to the map structure itself:
Code: Select all
map={
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 1, 0, 0, 2, 2, 2, 0, 3, 0, 3, 0, 1, 1, 1, 0, 0, 0, 0, 0},
{ 0, 1, 0, 0, 2, 0, 2, 0, 3, 0, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0},
{ 0, 1, 1, 0, 2, 2, 2, 0, 0, 3, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
{ 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
{ 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 2, 2, 2, 0, 3, 3, 3, 0, 1, 1, 1, 0, 2, 0, 0, 0, 0, 0, 0},
{ 0, 2, 0, 0, 0, 3, 0, 3, 0, 1, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0},
{ 0, 2, 0, 0, 0, 3, 0, 3, 0, 1, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0},
{ 0, 2, 2, 2, 0, 3, 3, 3, 0, 1, 1, 1, 0, 2, 2, 2, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
}
Let's continue:
Code: Select all
function draw_map()
for y=1, map_display_h do
for x=1, map_display_w do
love.graphics:draw( tile[map[y+map_y][x+map_x]], (x*tile_w)+map_offset_x, (y*tile_h)+map_offset_y )
end
end
end
Obviously, you have to call draw_map() on your draw() callback. If you run your example right now, you should see our little tilemap on screen. You can't do any scrolling right now, but we'll get onto that right now, adding the following to our update() callback:
Code: Select all
function update( dt )
if love.keyboard:isDown( love.key_up ) then
map_y = map_y-1
end
if love.keyboard:isDown( love.key_down ) then
map_y = map_y+1
end
if love.keyboard:isDown( love.key_left ) then
map_x = map_x-1
end
if love.keyboard:isDown( love.key_right ) then
map_x = map_x+1
end
-- check boundaries
if map_x < 0 then
map_x = 0
end
if map_x > map_w-map_display_w then
map_x = map_w-map_display_w
end
if map_y < 0 then
map_y = 0
end
if map_y > map_h-map_display_h then
map_y = map_h-map_display_h
end
end
Now go and run your script. Isn't LÖVE cool? You bet it is, congratulations on making your first tilemap scroller!
Closing words
While this little example should be enough to get you up and running, there are quite a few possible enhancements:
- Writing our maps by hand is a little stupid, we should make them with tools like Tiled and load them in our game.
- Drawing characters inside the tilemap.
- Fine scrolling: As you probably noticed, the map scrolls tile by tile and you probably don't want that if you need to move your characters/object pixel by pixel.
- Multi-layered maps: Most real tile engines are able to render more than one layer. Together with fine scrolling, you would be able to scroll a near layer at a certain speed and move another one behind it more slowly.
- Character to map collisions.
-- END OF TUTORIAL
Teh screen:
Teh filez:
http://www.cloverpunk.com.ar/tehstuffs/love-tut2.love
EDIT: Fixed typo, thanks Sardtok n_n
EDIT2: Seems like my hosting is acting up. I'll annoy the hell out of a few support guys...