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) ;----