Graphics 640 , 480

    ;
;----
;
;   ;--------------------------------------------------------------------------;
;   ;   Define and load tile and player images                                 ;
;   ;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,;
;
;,,,,
    ;

    Const TileImage_Width  = 32
    Const TileImage_Height = 32
    Const TileImage_Count  =  4  ; Number of images

    TileImages = LoadAnimImage ( "Tiles.BMP" , TileImage_Width , TileImage_Height , 0 , TileImage_Count )

    PlayerImage = LoadImage ( "Player.BMP" )
    MidHandle PlayerImage  ; Player world coordinates is in center of image.

    ;
;----
;
;   ;--------------------------------------------------------------------------;
;   ;   Define tile map                                                        ;
;   ;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,;
;
;,,,,
    ;

    .Tilemap
    Data 10  ; Width
    Data 10  ; Height
    Data 2,2,2,2,2,2,2,2,2,2
    Data 2,0,0,0,0,0,0,0,0,2
    Data 2,0,0,0,0,0,0,0,0,2
    Data 2,0,0,3,1,1,3,0,0,2
    Data 2,0,0,1,0,0,1,0,0,2
    Data 2,0,0,1,0,0,1,0,0,2
    Data 2,0,0,3,1,1,3,0,0,2
    Data 2,0,0,0,0,0,0,0,0,2
    Data 2,0,0,0,0,0,0,0,0,2
    Data 2,2,2,2,2,2,2,2,2,2

    Global Tilemap_Width   ; Using globals instead of constants,
    Global Tilemap_Height  ; so we can load a tilemap with any size.

    Dim Tilemap( 0 , 0 )  ; Can be redimensioned later on.

    ;
;----
;
;   ;--------------------------------------------------------------------------;
;   ;   Load tile map                                                          ;
;   ;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,;
;
;,,,,
    ;

    Restore Tilemap
    Read Tilemap_Width
    Read Tilemap_Height

    Dim Tilemap( Tilemap_Width , Tilemap_Height )

    For TileY = 1 To Tilemap_Height
        For TileX = 1 To Tilemap_Width
            Read Tilemap( TileX , TileY )
        Next
    Next

    ; Imagine that our tilemap begins at coordinate (0,0) in our world.
    ;
    ;       (0,0)
    ;          \
    ;           O---------------+
    ;           |               | - Tilemap
    ;           |               |
    ;           |               |
    ;           |               |
    ;           |               |
    ;           |               |
    ;           |               |
    ;           +---------------+
;----
;
;   ;--------------------------------------------------------------------------;
;   ;   Define and set 'player'                                                ;
;   ;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,;
;
;,,,,
    ;

    Global PlayerX
    Global PlayerY

    ; And we want to place our player in the center of the tilemap.
    ; Meaning in the center in world coordinates.
    ;
    ; As we know the size of each tile, we know the *real* size of
    ; our tilemap on screen, right?
    ;
    ;           O---+---+---+- -
    ;           |   |   |   |
    ;           |   |   |   |
    ;           +---+---+---+- -
    ;           |   |   |   |
    ;           .   .   .   .
    ;
    ; So, for the horizontal axis:
    ;
    ;     Horizontal number of tiles * Horizontal tile image size =
    ;             Horizontal tile map screen size (World)
    ;
    ; So.. 10 * 32 = 320 pixels wide
    ; Naturally, the center would be 160, so divided by 2.

    PlayerX = Tilemap_Width  * TileImage_Width  / 2
    PlayerY = Tilemap_Height * TileImage_Height / 2

    ;
;----
;
;   ;--------------------------------------------------------------------------;
;   ;   The game                                                               ;
;   ;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,;
;
;,,,,
    ;

    SetBuffer BackBuffer ()
    Repeat

    ;,,,,
        ;   Draw tile map
    ;----

        For TileY = 1 To Tilemap_Height
            For TileX = 1 To Tilemap_Width

                Image = TileImages
                Frame = Tilemap( TileX , TileY )

                ; What we are doing here is converting the
                ;   tilemap coordinate
                ; to
                ;   world/real/screen coordinate
                ; right?
                ;
                ; The tile alignment in the array:
                ;
                ;   1   2   3   4 ...
                ;
                ; For example, the tiles are 32 pixels wide,
                ; so if we want our tiles to start at (0,0) on screen,
                ; the alignment on screen/in our world would be:
                ;
                ;   0  31  63  95 ...

                Screen_PosX = ( TileX - 1 ) * TileImage_Width
                Screen_PosY = ( TileY - 1 ) * TileImage_Width

                DrawImage Image , Screen_PosX , Screen_PosY , Frame

            Next
        Next

    ;,,,,
        ;   Draw player
    ;----

        DrawImage PlayerImage , PlayerX , PlayerY

    ;,,,,
        ;   Player control
    ;----

        ; Suppose the player presses the 'up' key.
        ; We have to know if the player can go there.
        ; We could check this by remembering where the player would have been.
        ; And testing for a collision between the tilemap
        ; and that player position.

        FutureX = PlayerX  ; Player is not moving, by default
        FutureY = PlayerY

        If KeyDown ( 200 )  ; Up
            FutureY = PlayerY - 1  ; Moving 1 pixel up
        End If
        If KeyDown ( 208 )  ; Down
            FutureY = PlayerY + 1  ; Moving 1 pixel down
        End If
        If KeyDown ( 203 )  ; Left
            FutureX = PlayerX - 1  ; Moving 1 pixel left
        End If
        If KeyDown ( 205 )  ; Right
            FutureX = PlayerX + 1  ; Moving 1 pixel right
        End If

        ; If the player can be moved 1 pixel at a time,
        ; and consists of only 4 (2x2) pixels (or more),
        ; the image could overlap 4 tiles at once.
        ; If the player image is larger than the size of a tile,
        ; which it usually is, multiple tiles need to be checked.
        ; This can be done similarly to drawing the tilemap.
        ; But instead of drawing the tiles, testing for collision.
        ;
        ; To know which tile, for example, is directly under the player,
        ; you can use basically the same equation as before.
        ;
        ;       O-------------+-------------+-------------+--- -- -  -
        ;       |             |             |             |
        ;       |             |             |             |
        ;       |             |             |             |
        ;       |             |             |             |
        ;       |             |             |             |
        ;       +-------------+-------------+-------------+--- -- -  -
        ;       |             |             |             |
        ;       |             |             |  P          |
        ;       |             |             |   \         |
        ;       |             |             | (71,45)     |
        ;       |             |             |             |
        ;       +-------------+-------------+-------------+--- -- -  -
        ;       |             |             |             |
        ;       |             |             |             |
        ;
        ;       |             |             |             |
        ;       .             .             .             .
        ;
        ; So, suppose our player is at world coordinate (71,45).
        ; We can see that he's on the third horizontal tile,
        ; and the second vertical tile.
        ;
        ; If we would divide this position by the tile's dimensions;
        ;   71 / 32 = 2.21875
        ;   45 / 32 = 1.40625
        ; Wouldn't that give us what we need?
        ;
        ; A handy thing about Blitz is that it uses rounded numbers by default.
        ; Rounded down, to be exact. So, 2 and 1, respectively.
        ; Technically; automatically typecasted integers; Forget this *LoL* :)
        ; So if we were to do the above in code to find out which tile (number)
        ; is directly under the player;
        ;
        ;   Tile under Player = Player pos / Tile image size
        ;
        ; We would be converting from
        ;   world/real/screen coordinates
        ; to
        ;   tilemap coordinates
        ; right?
        ;
        ; Only thing is, as you can see in above results, is that our resulting
        ; tile number, when rounded down, starts at zero, instead of one.
        ; I mean, we were using a 1-based tilemap, so starting from one.
        ; So, just add 1 :)

        TileUnderPlayerX = PlayerX / TileImage_Width  + 1
        TileUnderPlayerY = PlayerY / TileImage_Height + 1

        ; Now, I'm gonna assume you're using a player image that's slightly
        ; larger than the size of a tile, say 48 by 48 pixels.
        ; So the player could overlap 9 tiles at a time.
        ;
        ; Effectively, we could check all surrounding tiles and the tile
        ; directly under the player.
        ;
        ;       O----+----+----+----+-- -
        ;       |    |1   |2   |3   |
        ;       |    |    |    |    |
        ;       +----+----+----+----+-- -
        ;       |    |4   |5   |6   |
        ;       |    |    |P   |    |
        ;       +----+----+----+----+-- -
        ;       |    |7   |8   |9   |
        ;       |    |    |    |    |
        ;       +----+----+----+----+-- -
        ;       |    |    |    |    |
        ;       .    .    .    .    .
        ;
        ; In the pic above you can see which tiles should be checked.
        ; The pic below shows an easy way to do this with 2 For loops.
        ; Yes, just like drawing the tilemap.
        ;
        ;       O----+----+----+----+-- -
        ;       |    |1,1 |2,1 |3,1 |
        ;       |    |    |    |    |
        ;       +----+----+----+----+-- -
        ;       |    |1,2 |2,2 |3,2 |
        ;       |    |    |P   |    |
        ;       +----+----+----+----+-- -
        ;       |    |1,3 |2,3 |3,3 |
        ;       |    |    |    |    |
        ;       +----+----+----+----+-- -
        ;       |    |    |    |    |
        ;       .    .    .    .    .

        TileStartX = TileUnderPlayerX - 1  ; Upper left of tile under player
        TileStartY = TileUnderPlayerY - 1

        TileEndX = TileUnderPlayerX + 1  ; Lower right of tile under player
        TileEndY = TileUnderPlayerY + 1

        For TileX = TileStartX To TileEndX
            For TileY = TileStartY To TileEndY

                ; For the sake of clarity:

                PlayImg = PlayerImage
                PlayFrame = 0

                PlayPosX = FutureX  ; Test for collision with future player position
                PlayPosY = FutureY

                TileImg = TileImages
                TileFrame = Tilemap( TileX , TileY )

                ; We're gonna need the world/real/screen coordinates
                ; of this tile again to test for a collision.
                ;
                ;   1   2   3   4 ...
                ;
                ;   |   |   |   |
                ;   v   v   v   v
                ;
                ;   0  31  63  95 ...

                TilePosX = ( TileX - 1 ) * TileImage_Width
                TilePosY = ( TileY - 1 ) * TileImage_Height

                ; Ofcourse we only want to test for a collision with tiles that
                ; the player may not walk on.
                ; Suppose any tile number above 1 is 'impassible'.

                ;--(Debug)
                    ; For debug purposes, so you can see this algorithm in action,
                    ; I'm drawing hollow rectangles over the tilemap where we:
                    ;  (1) are testing for a collision (green).
                    ;  (2) have found an 'impassible' tile (orange).
                    ;  (3) have found a collision (red).
                    Color 0 , 255 , 0  ; By default -> green.
                ;--(Debug)

                If TileFrame > 1  ; 2 or higher, e.g. the 3rd or 4th frame.

                    ;--(Debug)
                        Color 255 , 128 , 0  ; Found 'impassible' tile -> orange.
                    ;--(Debug)

                    If ImagesCollide ( PlayImg , PlayPosX , PlayPosY , PlayFrame , TileImg , TilePosX , TilePosY , TileFrame )

                        ; If player collides, set only a flag.
                        ; Because the player might collide with more than 1 tile.
                        ;
                        ; There are many ways to do this ofcourse.
                        ; For example, if you were using a function for this you could use 'Return'.

                        Collision = True

                        ;--(Debug)
                            Color 255 , 0 , 0  ; Collision -> red.
                        ;--(Debug)

                    End If

                End If

                ;--(Debug)
                    ; Draw a hollow rectangle over the current tile
                    ; using the currently selected color.
                    Rect TilePosX , TilePosY , TileImage_Width , TileImage_Height , False
                ;--(Debug)

            Next
        Next

        ; After testing for a collision between player and tilemap (or tiles 'possibly' under player),
        ; see if there was indeed a collision.

        If Collision
            Collision = False  ; Reset the flag, so we don't automatically have a collision next time.

            ; The player may not move, so leaving the current position unchanged.

        Else  ; There was no collision
            PlayerX = FutureX  ; Move the player to where 'it would have been'.
            PlayerY = FutureY
        End If

    ;,,,,
        ;   Show on screen
    ;----

        Flip
        Cls

    Until KeyHit ( 1 )
    End

    ;
;----
;
;   ;--------------------------------------------------------------------------;
;   ;   Things to look out for                                                 ;
;   ;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,;
;
;,,,,,,,,
        ;   0-based/1-based orientation
    ;----
    ;
    ;   It's a pesky thingy that always comes back at ya.
    ;   Heck, even some Blitz commands start counting from 1,
    ;   and others start counting from 0.
    ;   Even though, easily solved by a +1 or -1,
    ;   it can cause bedazzling misalignments.
    ;
    ;,,,,
        ;   X/Y order
    ;----
    ;
    ;   For example, when using 2 For loops to iterate through
    ;   a 2-dimensional array, the values could be read in, respectively,
    ;   from left to right, and from top to bottom, like in the above code.
    ;   When mixing X with Y a lot you could even rotate the entire tilemap
    ;   90 degrees clockwise or counter-clockwise.
;----
;
;   ;--------------------------------------------------------------------------;
;   ;   Notes                                                                  ;
;   ;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,;
;
;,,,,,,,,
        ;   Improvement
    ;----
    ;
    ;   Yes! I've left a lot of room for optimization to keep the code
    ;   relatively small and easy to read.
    ;
    ;   For example in the collision detection routine,
    ;   a call to ImagesOverlap could speed up the process immensely.
    ;   E.g. checking if the images overlap (bounding box checking)
    ;   is a lot faster than checking each pixel (!).
    ;
    ;   Another great speed-up is checking for collisions
    ;   only when the player is actually moving.
    ;
    ;   But wait - there's more! :P
    ;   The code can be easily modified to feature the screen following
    ;   the player world coordinates instead of a static position (0,0).
    ;
    ;   Well, I'll leave all of these up to you :o)
    ;
    ;   Any probs, just ask ;)
    ;
    ;,,,,
        ;   Coordinate conversion
    ;----
    ;
    ;   You could also use a set of functions to convert a position from one
    ;   coordinate system to another. For example:

    Function WorldX_To_TileX ( World_PosX )
        Return World_PosX / TileImage_Width
    End Function

    Function TileX_To_WorldX ( Tile_PosX )
        Return Tile_PosX * TileImage_Width
    End Function

    ;   Personally, I do this a lot to make the code look a lot more readable.
    ;   And above all - no more maths! :P
    ;   (Maths give me headaches)
;----