Tutorial requests and ideas

General discussion about LÖVE, Lua, game development, puns, and unicorns.
User avatar
Merkoth
Party member
Posts: 186
Joined: Mon Feb 04, 2008 11:43 pm
Location: Buenos Aires, Argentina

Re: Tutorial requests and ideas

Post by Merkoth » 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:

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
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:

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},

	    }
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:

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
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:

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
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:
Image

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...
Last edited by Merkoth on Mon Mar 10, 2008 3:49 pm, edited 3 times in total.

User avatar
Sardtok
Party member
Posts: 108
Joined: Thu Feb 21, 2008 2:37 pm
Location: Norway/Norge/諾威/挪威 (Yes, I'm teh back!)
Contact:

Re: Tutorial requests and ideas

Post by Sardtok » Mon Mar 10, 2008 10:43 am

Merkoth wrote:You can draw as many tiles as you want, just change the map_display_h and map_display_y variables accordingly.
Found a typo, should say display_w, not display_y.
Take off every Zigg for great rapist.
Now, outgay that!

User avatar
rude
Administrator
Posts: 1051
Joined: Mon Feb 04, 2008 3:58 pm
Location: Oslo, Norway

Re: Tutorial requests and ideas

Post by rude » Mon Mar 10, 2008 4:02 pm

Nice work, Merkoth. Nice coding style too, hopefully understandable for everyone. ^.^)/

I'll get this up on the page in a few days ...
Merktoh wrote: - Writing our maps by hand is a little stupid, we should make them with tools like Tiled and load them in our game.
We actually had support for this (Tiled map format) in an ancient version of LÖVE. We could [eventually] bring it back, if people are interested.

User avatar
Merkoth
Party member
Posts: 186
Joined: Mon Feb 04, 2008 11:43 pm
Location: Buenos Aires, Argentina

Re: Tutorial requests and ideas

Post by Merkoth » Mon Mar 10, 2008 11:04 pm

Thanks rude n_n
rude wrote: We actually had support for this (Tiled map format) in an ancient version of LÖVE. We could [eventually] bring it back, if people are interested.
That would be pretty cool :D

User avatar
rude
Administrator
Posts: 1051
Joined: Mon Feb 04, 2008 3:58 pm
Location: Oslo, Norway

Re: Tutorial requests and ideas

Post by rude » Thu Mar 13, 2008 3:23 pm


User avatar
Merkoth
Party member
Posts: 186
Joined: Mon Feb 04, 2008 11:43 pm
Location: Buenos Aires, Argentina

Re: Tutorial requests and ideas

Post by Merkoth » Fri Mar 14, 2008 2:04 am

Thanks man n_n

User avatar
mike
Administrator
Posts: 364
Joined: Mon Feb 04, 2008 5:24 pm

Re: Tutorial requests and ideas

Post by mike » Fri Mar 14, 2008 8:00 pm

Great tutorial, Merk. I am looking foreward to one-up'ing you by making a tutorial about displaying isometric tiles. ;) I wrote one a long time ago but LÖVE has changed a lot since then and I need to fix it for the "new generation".
Then again, taking into consideration how productive I've been the past couple of months I doubt anything will happen. Thanks for all the help, as we really need it! (heart emoticon goes here)
Now posting IN STEREO (where available)

User avatar
aes
Prole
Posts: 13
Joined: Sat Mar 29, 2008 2:24 am

Re: Tutorial requests and ideas

Post by aes » Sat Mar 29, 2008 3:11 am

Uh. hello, ppl.
I wrote a very small walking-character demo for those charas sheets. With a little more löve it could probably make a nice tutorial. (If not standard kit, the charas class is quite reusable.)

http://www.cs.umu.se/~c96aes/walkdemo.love if the attaxhment don't working.

--
Oh, jah, I'll need to get a real avatar at some point too.

löve, aes

User avatar
rude
Administrator
Posts: 1051
Joined: Mon Feb 04, 2008 3:58 pm
Location: Oslo, Norway

Re: Tutorial requests and ideas

Post by rude » Sun Mar 30, 2008 12:17 am

Hey aes.

Cute characters. Did you make them?

Your demo didn't work at first, beucase you use Lua's built-in require function. That's fine while in a directory, but fails when you pack your game into a .love-file. ^^

You should use love.filesystem.require("util.lua"), or if you really like require:

Code: Select all

function require(file)
  love.filesystem.require(file .. ".lua")
end
I've made the necessary changes to make it work with the brand new LOVE 0.2.1. Remember to update beore running this!

8-)
Attachments
walkdemo.love
For LOVE 0.2.1 ONLY
(13.34 KiB) Downloaded 189 times

User avatar
aes
Prole
Posts: 13
Joined: Sat Mar 29, 2008 2:24 am

Re: Tutorial requests and ideas

Post by aes » Sun Mar 30, 2008 10:44 am

rude wrote:Cute characters. Did you make them?
Ehm, well, not so much, I was just clicking aroung in charas. (linked from free game resources.)
rude wrote:Your demo didn't work at first, beucase you use Lua's built-in require function. That's fine while in a directory, but fails when you pack your game into a .love-file. ^^
Ok, this is interesting. I'll try to remember this. Or submit a patch. (Yes, I know)
rude wrote:I've made the necessary changes to make it work with the brand new LOVE 0.2.1. Remember to update beore running this!
I'm working on trying to make a cozy infrastructure on the lua side: a scenegraph and an inputstack. I'll get back to the tutorial in while. (Meanwhile, there's your 0.2.1-fix)

--[[
The thing that freaks me out about the scenegraph is that I can't seem to find a set of complete primitives. An R^2 vector class seems like a no-brainer, but how do you cope with having rotation and scaling but not general transformation?
--]]

aes

Post Reply

Who is online

Users browsing this forum: No registered users and 9 guests