2d Tile map collision help
BlitzMax Forums/BlitzMax Beginners Area/2d Tile map collision help
| ||
| Hi, Can someone please explain how collisions work using a simple map array and a collision map. I have been told that this is the best way to do collision, but if someone has a simple example of how to do this, it would really help me out. Thanks |
| ||
| Usually you have a `visible tilemap` comprising images which you draw on-screen, and an `invisible collision map` which is often at a higher resolution, against which you do collision tests. At its simplest, all you are doing is finding the bounding rectagles around the player/enemy - which is a simple matter of taking their x,y coordinates in map-space and deciding how many collision tiles they cover and checking all of those tiles to see if it is `solid` or `pass through`. Then when you've found out which tiles around the player/enemy are blocking it from moving in a certain direction, you can take action or not allow movement in that direction. You could also simply get the tile in the collision map which the character/enemy is close to, treat it like a bounding rectangle, and see if the two overlap. So as a real basic example, maybe you check the tile which would be immediate below the feet of your game character. If that tile is considered solid, you do not allow the player to move down the screen - ie he `lands on` the tile. He can walk left and right on the tiles or jump up but he can't go down. Then you also check to the left, right, and above, to see if he can move left, right, or whether he's bumping his head. If it helps, instead of thinking of your game world as 2d viewed from the side, imagine it as viewed from above, where you are navigating around a maze of walls and you keep bumping into some. You simply are trying to only allow movement if the tile in the direction of your movement is not solid. |
| ||
| Here's my plat code. Using tiles. Tiles can be read in via data statements or via a tile map editor. Thanks go to jesse and a few other peeps for helping me to get it to work. Jesse being the guy that provided the "mod" based landing on tile code. :) Enjoy! :) Also after this code I'll also post the code to a simple Single Screen Tilemap editor which reads and saves files. Hopefully it should get you on your way to making that next great platform game or tilebased game. :) http://www.lunaticninja.com/storage/plat_tiles.rar
SuperStrict
Graphics 800 , 600
Global Tiles:TImage = LoadImage("tile.png")
Global Player:TImage = LoadImage("player1632.png")
Const MAPWIDTH:Int = 800/32
Const MAPHEIGHT:Int = 600/32
Global Map:Int[MAPWIDTH , MAPHEIGHT]
Global PlayerX:Float
Global PlayerY:Float
Global Player_Width:Int = 16
Global Player_Height:Int = 32
Global CheckPlayerPosition:Int = 0
Global Direction:Int = -1
Global Jump:Int = 0
Const Gravity:Float = 0.2
Global JumpHeight:Float = 5.3
Global CanJump:Int = 1
Global Falling:Int = 0
ReadLevelData() ' We Read the level data
While Not KeyHit(KEY_ESCAPE)
Cls
DrawMap()
CheckIfPlayerCollideWithTile()
DrawPlayer()
MovePLayer()
DoJump()
Flip
Wend
'######################################################
'#### Function DrawPLayer() #
'#### Draws Player to Screen and gets Player Position #
'######################################################
Function DrawPlayer()
If CheckPlayerPosition = 0
For Local x:Int = 0 Until MAPWIDTH
For Local y:Int = 0 Until MAPHEIGHT
Select Map[x, y]
Case 2
PlayerX = getX(x)
PlayerY = getY(y)
CheckPlayerPosition = 1
End Select
Next
Next
EndIf
DrawText "PlayerX = " +PlayerX , 0 , 20
DrawImage Player, PlayerX , PlayerY , 0
End Function
'#################################################
'### Function MovePLayer() #
'## Moves Player and checks outer boundaries #
'#################################################
Function MovePLayer()
If KeyDown(KEY_LEFT) And Not KeyDown(KEY_RIGHT)
Direction = 0
PlayerX:-2
ElseIf KeyDown(KEY_RIGHT) And Not KeyDown(KEY_LEFT)
Direction = 1
PlayerX:+2
End If
If PlayerX - 32 <= 0
PlayerX:+2
ElseIf PlayerX >= 800 - 32
PlayerX:-2
End If
End Function
'##########################################################
'### Function CheckIfPlayerCollideWithTile() #
'### Checks to see if the player has collided with a tile #
'### Checks also if the Player is falling #
'##########################################################
Function CheckIfPlayerCollideWithTile()
'#Region
Select Direction
Case 1
If Jump = 0
If Map[(PlayerX - Player_Width) / 32 + 1, PlayerY / 32]= 1
PlayerX:-2
EndIf
EndIf
If Jump = 1 and PlayerX >= 784 - 32
If Map[(PlayerX - Player_Width) / 32 + 1, PlayerY / 32]= 1
PlayerX:-2
EndIf
End If
Case 0
If Jump = 0
If Map[(PlayerX + Player_Width) / 32, PlayerY / 32]= 1
PlayerX:+2
End If
EndIf
End Select
'#End Region
If Falling = 1
PlayerY:+3.2
If Map[(PlayerX + Player_Width) / 32, PlayerY / 32 + 1]= 1 or Map[(PlayerX - Player_Width * 2) / 32 + 1, PlayerY / 32 + 1]= 1
If (PlayerY mod 32.0) <= 6.4
PlayerY = PlayerY - (PlayerY mod 32)
Jump = 0
Falling = 0
CanJump = 1
JumpHeight = 5.5
EndIf
EndIf
End If
If not Map[(PlayerX + Player_Width) / 32, PlayerY / 32 + 1]and not Map[(PlayerX - Player_Width * 2) / 32 + 1, PlayerY / 32 + 1]
If Jump = 0
Falling = 1
End If
End If
End Function
'##############################################
'### Function DoJump() #
'### Makes our player jump #
'##############################################
Function DoJump()
If KeyHit(KEY_SPACE) and CanJump = 1
Jump = 1
CanJump = 0
End If
If Jump = 1
PlayerY:-JumpHeight
JumpHeight:-Gravity
If JumpHeight <= - 1.0 or PlayerY < 32
Falling = 1
EndIf
End If
End Function
'########################################
'### Function DrawMap() #
'### We Draw the Map to screen #
'########################################
Function DrawMap()
For Local x:Int = 0 Until MAPWIDTH
For Local y:Int = 0 Until MAPHEIGHT
Select Map[x, y]
Case 1
DrawImage Tiles, x * 32, y * 32, 0
End Select
Next
Next
End Function
'#######################################################
'### Function ReadLevelData() #
'### We read the data stored in the defdata statements #
'#######################################################
Function ReadLevelData()
For Local y:Int = 0 Until MAPHEIGHT
For Local x:Int = 0 Until MAPWIDTH
Local Data:Int
ReadData Data
Map[x, y]= Data
Next
Next
End Function
Function getX:Int(x:Int)
Return 32 * x '+ offsetX
End Function
Function getY:Int(y:Int)
Return 32 * y '+ offsetX
End Function
DefData 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
DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
DefData 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1
DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
DefData 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1
DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1
DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
DefData 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
DefData 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1
DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
DefData 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1
DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
DefData 1, 0, 0, 0, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1
DefData 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
DefData 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
DefData 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
Here is the very simple Single Screen Tilemap Editor. http://www.lunaticninja.com/storage/TileMapEditor.zip
'// A simple TileMap Editor which saves and Loads Maps.
'// Programmed Amon
'// v1.0
SuperStrict
'// We setup our Graphics Mode
Graphics 800, 600
'// We Load our images, 6 frames in an AnimImage
Global Tiles:TImage = LoadAnimImage("tiles.png", 32, 32, 0, 6)
'// We setup 2 Constants to hold our MapWidth and MapHeight.
Const MAPWIDTH:Int = 25 '// 800/32 = 25 tiles going across the screen
Const MAPHEIGHT:Int = 18 ' 600/32 = 18 tiles going down the screen
Const TILESIZE:Int = 32 '// Our tiles are 32x32 pixels square
'// We setup an Array to hold our map data
Global MapArray:Int[MAPWIDTH, MAPHEIGHT]
'// We init and prefill the MapArray with a value
For Local x:Int = 0 Until MAPWIDTH
For Local y:Int = 0 Until MAPHEIGHT
MapArray[x, y] = - 1
Next
Next
'// We set a Global to store the current tile number
Global TileSelected:Int = 0
'// We create another variable for holding the mapfile number we save with
'// This will increase by 1 everytime we save a map.
Global iter:Int = 0
'// We setup our loop
While Not KeyHit(KEY_ESCAPE)
Cls
DrawMap()
SelectTile()
Place_Tile()
SaveMap()
LoadMap()
Flip
Wend
'// Our DrawMap Function
Function DrawMap()
For Local y:Int = 0 Until MAPHEIGHT
For Local x:Int = 0 Until MAPWIDTH
'//There are 2 ways we can draw the map to screen
'// We can use the array data to to select which frame we draw
'// or we can write if or case statements for the individual tile.
'// I'll show both below.
'// Method 1 - Draw with frame data from array. Comment out the line below and comment
'// The second method to see how each works
'DrawImage Tiles, x * TILESIZE, y * TILESIZE, MapArray[x, y]
'// above we do x*Tilesize, y * Tilesize because as the for loop loops it goes from
'// 0 to MAPWIDTH/MAPHEIGHT which is 25 and 18. So , 0 * Tilesize(32) is 0, 1*32 = 32 and so on and it will draw
'// the tiles according to where they are in the array.
'// Method 2 - If statements
If MapArray[x, y] = 0
DrawImage Tiles, x * TILESIZE, y * TILESIZE, 0
ElseIf MapArray[x, y] = 1
DrawImage Tiles, x * TILESIZE, y * TILESIZE, 1
ElseIf MapArray[x, y] = 2
DrawImage Tiles, x * TILESIZE, y * TILESIZE, 2
ElseIf MapArray[x, y] = 3
DrawImage Tiles, x * TILESIZE, y * TILESIZE, 3
ElseIf MapArray[x, y] = 4
DrawImage Tiles, x * TILESIZE, y * TILESIZE, 4
ElseIf MapArray[x, y] = 5
DrawImage Tiles, x * TILESIZE, y * TILESIZE, 5
EndIf
Next
Next
End Function
'// Our SelectTile Function
Function SelectTile()
'// If we hit the space bar we increase the tileSelected variable by 1
'// if Tileselected = 2 then we know we will be drawing image frame 2 to the screen.
'// if Tileselected is Greater than 5 (how many images we have in our animimage) then
'// we set it to 0.
If KeyHit(KEY_SPACE)
TileSelected:+1
If TileSelected > 5 Then TileSelected = 0
End If
End Function
'// Our Place_Tile Function
Function Place_Tile()
If MouseX() > 0 And MouseX() < 800 '// if the mouse in the screen boundaries
If MouseY() > 0 And MouseY() < 600 - 50 '// if the mouse is within the screen and array boundaries
If MouseDown(MOUSE_LEFT) '// If we hit or hold MouseLeft
'// What this next line does is find what position we are within the MapArray
'// and places a tile in that cell. For example if mousex() position = 64 and MouseY() position = 64
'// and we divide by our TileSize then we know that 64/32 = 2 so our mouse will place a tile
'// in the place in the MapArray and that screen location.
MapArray[MouseX() / TILESIZE, MouseY() / TILESIZE] = TileSelected
End If
End If
End If
End Function
'// Our LoadMap Function
Function LoadMap()
If KeyHit(KEY_F5)
Local MapFile:String = RequestFile("MapFile", "map") '//We load the map file
Local FileToRead:TStream = ReadFile(MapFile)
If FileToRead '// if the mapfile has loaded
For Local y:Int = 0 Until MAPHEIGHT
For Local x:Int = 0 Until MAPWIDTH
MapArray[x, y] = ReadInt(FileToRead)
Next
Next
End If
End If
End Function
'// Our SaveMap Function
Function SaveMap()
'// Below we save our Map when we press F6
If KeyHit(KEY_F6)
Local Mapfile:String = "Map" + "_" + iter + ".map" '// Create a MapFile name add an underscore and what
'// iter equals to the end of it
Local Filewrite:TStream = WriteFile(MapFile) '// We tell max that we want to write a stream called
If Filewrite ' if the stream exists
For Local y:Int = 0 Until MAPHEIGHT '// Loop through our array
For Local x:Int = 0 Until MAPWIDTH'// and save the map data to the file
WriteInt FileWrite, MapArray[x, y] '// by writing ints to file
Next
Next
EndIf
CloseFile FileWrite '// We close the file when we've finished with it
iter:+1 '// add 1 to iter
Cls
DrawText "MapSaved", 380, 585 '// Display a map save text
Flip
Delay 1000
End If
End Function
Hope it helps. :) |
| ||
| and here is a really simplified version in case you need more help: edited - been messing with this: it will work even if you change the tilesize. I have a better example in the code archives: http://www.blitzbasic.com/codearcs/codearcs.php?code=2235 |