2D Tile based scrolling with collision by pizzaman30th Apr 2005 19:06
|
---|
Summary An example of how to scroll a tile based map in 2D, with collision implemented. Description This is an example of how to scroll a tile based map in 2D, with collision implemented. It uses data staments to store the map which is then transferred to an array. The array is then used to display the correct images on screen. Code ` This code was downloaded from The Game Creators ` It is reproduced here with full permission ` http://www.thegamecreators.com `**************************************************************************************** `**************************** HOW TILE BASED COLLISION WORKS **************************** `**************************** by pizzaman **************************** `**************************** Date: 14/6/04 **************************** `**************************** Time to complete: 1 hour **************************** `**************************************************************************************** `THE PRINCIPLE - `The visible screen is devided up into boxes which we call tiles. This means that each `tile is sitting on a range of coordinates. For example a 20 pixel by 20 pixel tile that `is the top-left most tile is sitting on the coordinates (0,0) to (19,19). Since we know `each tile is sitting on a range of coordinates we can check to see whether a sprite (or `image) has the same coordinates as the tile. However, we must take into consideration `that a sprite (or image) is usually bigger than a pixel, so we have to form a collision `box around the sprite. `HOW TO IMPLEMENT A TILE BASED COLLISION SYSTEM - `Firstly we need a 2 dimensional array to store tile data in (Tile data is the image `numbers of the of the images you are going to use; e.g. 1 could be a wall, 2 could be `a grass image etc. Note: the image sizes should be the same as the tile size you will `use. `Secondly we store the data in the array. Note: you could already store data in an array `and just load the array - this would be faster for loading times. `Next we create some collision points around our sprite, usually 4 points which would `be placed at the corners of the sprite creating a collision box, you could have more `collision points if a collision box is not suitable. `Next we have to figure out which tile in the array each collision point is on to do `this you have to use the formula ` ` Tile = (x / Tile Sise, y / Tile size) ` ` where Tile is the tile in the array, x is the x coordinate of the ` collision point your checking for and y is the y coordinate of the ` collision point your checking for ` `Now if the tile is a tile you dont want your sprite to go on; just reset the sprites `coordinates to what they were before the collision and voila!!!! you've got tile `based collision. `**************************************************************************************** `*********************************** SCROLLING THE MAP ********************************** `**************************************************************************************** `THE PRINCIPLE - `To scroll a tile based map, you simply have to move all the tiles in the given direction `then when the tiles have moved more pixels then the size of the tiles, the tiles get `reset to the positions they were in before they were moved, but their images changed `to simulated the tiles have moved, and generating a seamless transition. `HOW TO IMPLEMENT A SCROLLING TILE BASED MAP - `Instead of using a system that relies on screen coordinates, we need to build our map `on a global coordinate system where coordinates (0,0) is the first tile (usually the `top-left most tile). Everything we do will be based in this global coordinate system, `which will include the player and their collision system. The coordinates then will be `transferred to screen coordinates, where the images can be pasted correctly. `Note that an extra row and column of tiles are pasted to the screen, this is to `generate the seamless scrolling. `**************************************************************************************** `*********************************** Start of program *********************************** `**************************************************************************************** `Declare some constants, they are constants because their values will not change `thoughout the program. There are placed at this point in the program to be easily `modified. #constant SCREENSIZEX 1024 #constant SCREENSIZEY 768 `gosubs are here instead of actual code, just to make things look neat gosub DeclareVariables gosub screensetup gosub createimages gosub loaddata gosub makeplayer `start the main loop do `exit the loop if space bar is pressed - effectively ending the program `Note: it has to be place here and not in a gosub as it wont work if spacekey() = 1 then exit gosub controls gosub collision gosub ScrollScreen gosub displayplayer gosub DisplayDebugInfo `update screen sync loop gosub cleanup `end the program end `**************************************************************************************** `************************************* End of program *********************************** `**************************************************************************************** DeclareVariables: `store how many horizontal/verticle tiles there are NumOfTilesX = 40 NumOfTilesY = 30 `store the size (in pixels) of a tile TileSizeX = 32 TileSizeY = 32 `store the speed of the tile SpeedX = 2 SpeedY = 2 `store the coordinates that will make the player be at the centre of the screen ScreenCentreX = SCREENSIZEX / 2 ScreenCentreY = SCREENSIZEY / 2 `Work out out how many tiles there are on screen for x and y directions TilesPerScreenX = SCREENSIZEX / TileSizeX TilesPerScreenY = SCREENSIZEY / TileSizeY `Works out if a tile is partially visible on screen, if so then add 1 to the `TilesPerScreen variable. This has the affect of drawing a whole column or/and row `of tiles to the screen, to cover up visual anomalies with tile sizes that do `not divide exactly into the screen resolution if SCREENSIZEX mod TileSizeX then TilesPerScreenX = TilesPerScreenX + 1 if SCREENSIZEY mod TileSizeY then TilesPerScreenY = TilesPerScreenY + 1 `Calculates the largest co-ordinates the map can scroll too MaxMapX = (TileSizeX * NumOfTilesX) - (SCREENSIZEX + SpeedX) MaxMapY = (TileSizeY * NumOfTilesY) - (SCREENSIZEY + SpeedY) `store predefined coordinates of player X1 = ScreenCentreX Y1 = ScreenCentreY `store predefined coordinates of map MapX = 0 MapY = 0 return screensetup: `delete anything stored in video memory flush video memory `set screen resolution set display mode 1024, 768, 32 `setup syncing sync on sync rate 150 return createimages: `create a green box box 0, 0, TileSizeX, TileSizeY, rgb(0, 255, 0), rgb(0, 225, 0), rgb(0, 225, 0), rgb(0, 200, 0) get image 1, 0, 0, TileSizeX, TileSizeY, 1 `create a yellow/orange box as the player box 0, 0, TileSizeX, TileSizeY, rgb(255, 255, 0), rgb(255, 128, 0), rgb(255, 128, 0), rgb(255, 255, 0) get image 2, 0, 0, TileSizeX, TileSizeY, 1 `create a red box cls rgb(255, 0, 0) get image 10, 0, 0, TileSizeX, TileSizeY, 1 `create a blue box box 0, 0, TileSizeX, TileSizeY, rgb(0, 0, 255), rgb(0, 0, 225), rgb(0, 0, 225), rgb(0, 0, 200) get image 11,0,0,TileSizeX,TileSizeY,1 return loaddata: `create an array called TileMap - we will use this to store the tile data. `an array is like a file cabinet - in each drawer of the file cabinet there are `folders that hold our data. Therefore in our "file cabinet" we have 20 drawers; and `inside of each drawer we 15 folders; which hold data we want to access. `Also arrays can have up to 5 dimensions (ours has 2 dimensions) - you can visualise `this as being more folders inside of the other folders and so on; and of course you `can just have 1 dimension array as well (in other words just the drawers) dim TileMap(NumOfTilesX - 1, NumOfTilesY - 1) `load data into the array - basically the computer will read 20 bits of data from one `data statment (at the end of the program), then move onto the next data statment `until all 15 data statments are read. Note: all 15 data statments could have been `combined into one large data statment of 300 items; the reason why its not currently `like that is its hard to read also you can kind of see the map the way its currently `written. for y = 0 to NumOfTilesY - 1 for x = 0 to NumOfTilesX - 1 read TileMap(x, y) next x next y return makeplayer: `make the player sprite 1, X1, Y1, 2 `this sets the sprites properties; by placing a 0 in the backsave flag the background `behind sprite will not be saved by the computer, hence will speed up our program a `little. We are not using the backsave feature because we will be replacing the `background ourselves; this is due to when a sprite is created the 3d backdrop is `automatically created (a blue screen wiping any 2d images previously draw to the `screen) and cannot be turned off as long as you have a sprite on screen. set sprite 1, 0, 1 return controls: `store the map's x and y coordinates into the variables OldMapX and OldMapY OldMapX = MapX OldMapY = MapY `store the x and y coordinates of the player into the variables oldX1 and oldY1 OldX1 = X1 OldY1 = Y1 `makes the player move by altering the sprites coordinates `makes the map move by altering the maps coordinates, if the player is roughly at the `centre of the screen if leftkey() = 1 if MapX < X1 - (ScreenCentreX - 5) and MapX > X1 - (ScreenCentreX + 5) then MapX = MapX - SpeedX X1 = X1 - SpeedX endif if rightkey() = 1 if MapX < X1 - (ScreenCentreX - 5) and MapX > X1 - (ScreenCentreX + 5) then MapX = MapX + SpeedX X1 = X1 + SpeedX endif if upkey() = 1 if MapY < Y1 - (ScreenCentreY - 5) and MapY > Y1 - (ScreenCentreY + 5) then MapY = MapY - SpeedY Y1 = Y1 - SpeedY endif if downkey() = 1 if MapY < Y1 - (ScreenCentreY - 5) and MapY > Y1 - (ScreenCentreY + 5) then MapY = MapY + SpeedY Y1 = Y1 + SpeedY endif return collision: `NOTE : you may have noticed the player sticking to the blocks, to stop this make the ` collision area of the player smaller and edit the players image to the same ` same size (so it doesn't look weird). You have to make the change in the ` players x and y sizes greater or equal to their speed or you wont notice a ` difference. `the following 8 lines of code stores the old and new left,right,top,bottom edges of `the sprite in the respective variables `gets which tiles the current left and right co-ordinates of the player are on LeftTileNum = int(X1 / TileSizeX) RightTileNum = int((X1 + (TileSizeX - 1)) / TileSizeX) `gets which tiles the current top and bottom co-ordinates of the player are on TopTileNum = int(Y1 / TileSizeY) BottomTileNum = int((Y1 + (TileSizeY - 1)) / TileSizeY) `gets which tiles the old left and right co-ordinates of the player are on OldLeftTileNum = int(oldX1 / TileSizeX) OldRightTileNum = int((oldX1 + (TileSizeX - 1)) / TileSizeX) `gets which tiles the old top and bottom co-ordinates of the player are on OldTopTileNum = int(oldY1 / TileSizeY) OldBottomTileNum = int((oldY1 + (TileSizeY - 1)) / TileSizeY) `this is where we check for those 4 collision points; if the collision point is on a `tile you are not able to pass through (in our case a tile that has an image number `of 10 or greater), then the old coordinates of the sprite are restored. `Note: to have sliding collision we must check the the current x tile against the `old y tile and vice versa. `checks for top-left collision point if TileMap(LeftTileNum, OldTopTileNum) >= 10 then X1 = oldX1 : MapX = OldMapX if TileMap(OldLeftTileNum, TopTileNum) >= 10 then Y1 = oldY1 : MapY = OldMapY `checks for top-right collision point if TileMap(RightTileNum, OldTopTileNum) >= 10 then X1 = oldX1 : MapX = OldMapX if TileMap(OldRightTileNum, TopTileNum) >= 10 then Y1 = oldY1 : MapY = OldMapY `checks for bottom-left collision point if TileMap(LeftTileNum, OldBottomTileNum) >= 10 then X1 = oldX1 : MapX = OldMapX if TileMap(OldLeftTileNum, BottomTileNum) >= 10 then Y1 = oldY1 : MapY = OldMapY `checks for bottom-right collision point if TileMap(RightTileNum, OldBottomTileNum) >= 10 then X1 = oldX1 : MapX = OldMapX if TileMap(OldRightTileNum, BottomTileNum) >= 10 then Y1 = oldY1 : MapY = OldMapY `stops player from scrolling past the edges of the map by resetting the maps `global coordinates to their maximum values. The maximum values are calculated by `(TileSizeX * NumOfTilesInRow) - SpeedX - for MapX `(TileSizeY * NumOfTilesInColumn) - SpeedY - for MapY if MapX < 0 then MapX = 0 if MapX >= MaxMapX then MapX = MaxMapX if MapY < 0 then MapY = 0 if MapY >= MaxMapY then MapY = MaxMapY return ScrollScreen: `works out the tile number within array to start displaying tiles at TileNumX = MapX / TileSizeX TileNumY = MapY / TileSizeY `cache calculation to tempX and TempY variables for a minor speed increase `this calculation works out how many pixels to offset the position where the tile `images should be placed. TempX = MapX - (TileNumX * TileSizeX) TempY = MapY - (TileNumY * TileSizeY) `reset values Temp1 = 0 Temp2 = 0 `Calculates if the map has scrolled to the last tile, if so we must remove a column `or/and row of tiles from the screen as they have no more map data to draw; also `because of this it would create an error if TilesPerScreenX + TileNumX >= NumOfTilesX then Temp1 = 1 if TilesPerScreenY + TileNumY >= NumOfTilesY then Temp2 = 1 `this pastes the images to screen in the correct order by working out where to start `pasting the images from in the array, then displaying the images at the correct `screen coordinates for y = 0 to TilesPerScreenY - Temp2 for x = 0 to TilesPerScreenX - Temp1 paste image TileMap(x + TileNumX, y + TileNumY), (x * TileSizeX) - TempX, (y * TileSizeY) - TempY next x next y return displayplayer: `display sprite sprite 1,X1 - MapX,Y1 - MapY,2 return DisplayDebugInfo: text 0, 0,"FPS " + str$(screen fps()) text 0,20,"MapX " + str$(MapX) text 0,40,"MapY " + str$(MapY) text 0,60,"X1 " + str$(MaxMapX) text 0,80,"Y1 " + str$(MaxMapY) text 100, 0,"Press spacebar to exit" return cleanup: `delete any media involved in this program for x = 1 to 11 if image exist(x) then delete image x if sprite exist(x) then delete sprite x next x `delete anything thats left in the video memory - if anything flush video memory return `store tile data at the end of the program so its out of the way data 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 data 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,10 data 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,10 data 10, 1, 1,11,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,11, 1, 1, 1, 1, 1, 1,11,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,11, 1, 1,10 data 10, 1, 1,11,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,11, 1, 1, 1, 1, 1, 1,11,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,11, 1, 1,10 data 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,10 data 10, 1, 1,11, 1,11, 1, 1, 1, 1, 1, 1, 1, 1,11, 1,11, 1, 1, 1, 1, 1, 1,11, 1,11, 1, 1, 1, 1, 1, 1, 1, 1,11, 1,11, 1, 1,10 data 10, 1, 1,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11, 1, 1, 1, 1, 1, 1,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11, 1, 1,10 data 10, 1, 1,11, 1,11, 1, 1, 1, 1, 1, 1, 1, 1,11, 1,11, 1, 1, 1, 1, 1, 1,11, 1,11, 1, 1, 1, 1, 1, 1, 1, 1,11, 1,11, 1, 1,10 data 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,10 data 10, 1, 1,11,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,11, 1, 1, 1, 1, 1, 1,11,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,11, 1, 1,10 data 10, 1, 1,11,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,11, 1, 1, 1, 1, 1, 1,11,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,11, 1, 1,10 data 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,10 data 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,10 data 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,10 data 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,10 data 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,10 data 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,10 data 10, 1, 1,11,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,11, 1, 1, 1, 1, 1, 1,11,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,11, 1, 1,10 data 10, 1, 1,11,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,11, 1, 1, 1, 1, 1, 1,11,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,11, 1, 1,10 data 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,10 data 10, 1, 1,11, 1,11, 1, 1, 1, 1, 1, 1, 1, 1,11, 1,11, 1, 1, 1, 1, 1, 1,11, 1,11, 1, 1, 1, 1, 1, 1, 1, 1,11, 1,11, 1, 1,10 data 10, 1, 1,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11, 1, 1, 1, 1, 1, 1,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11, 1, 1,10 data 10, 1, 1,11, 1,11, 1, 1, 1, 1, 1, 1, 1, 1,11, 1,11, 1, 1, 1, 1, 1, 1,11, 1,11, 1, 1, 1, 1, 1, 1, 1, 1,11, 1,11, 1, 1,10 data 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,10 data 10, 1, 1,11,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,11, 1, 1, 1, 1, 1, 1,11,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,11, 1, 1,10 data 10, 1, 1,11,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,11, 1, 1, 1, 1, 1, 1,11,11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,11,11, 1, 1,10 data 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,10 data 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,10 data 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10 |