Page 1 of 1

Top Down Game Collision Help

Posted: Fri Aug 28, 2015 2:43 am
by JuiceBox
Alright, let me just say it. I'm a complete noob to making games without the aid of a visual editor. Love2D is one of my first, uhh, frameworks? Or engine, whatever it wants to call itself. Up until now, I've just been using Unity, actually thinking I'm good at programming. :crazy:

Anyways, I really want to make a simple top down game similar to The Binding of Isaac, just without all of the procedurally generated maps and such. I followed Kikito's tile map editor tutorial up until it, well, just decided to stop. And I'd love to make a game with the code they provided! Only problem is that I'm not sure how to handle collisions with it. Anyways, I'm hoping someone here can help me out with my noobly problems. Here's a .love file of my project. :ultrahappy:

Re: Top Down Game Collision Help

Posted: Fri Aug 28, 2015 12:59 pm
by Imaculata
I'm a newby myself, but I happen to be making the exact same kind of game (only with the random dungeons). Here's what I do:

First I check which tile the player is standing on. This is simple to figure out, since you know what the x-location of the player is, and what the offset is of the map and the camera. So, since you know how big a tile is, and what the size is of your map, you can calculate the exact tile that the player is on. Its simply a matter of unleashing some good old math on the x and y location of the player.

Code: Select all

 camOffset = { (zoomX*(mapX%1)*tileSize),(zoomY*(mapY%1)*tileSize) }

player.tile =  { math.floor((player.x + camOffset[1]) / tileSize) , math.floor((player.y + camOffset[2]+(player.size/5)+math.floor(player.size/5)) / tileSize) }
Most of the newb tutorials write the tiles in a table, with an x and y value. What I did was create a second table, where I write all the collision. I created a reference file that the game loads, which basically instructs the game what the collision type is for any tile on my sprite sheet. Then as I create the initial sprite batch, I also look up the collision, and write the collision to a separate table of the exact same size as the map. So each tile in the map-table, also has an entry in my collision-table.

This is what the collision file looks like for my spritesheet:

Code: Select all

	-- Collision reference sheet for the tilesheet
	--"X"=wall--"."=floor--"W"=water--"L"=lava--"F"=firepit--"E"=emptyfirepit
	--"P"=pit--"T"=spiketrap--"S"=stairs--"U"=hammerblockup--"C"=chest
	--"D"=hammerblockdown--"I"=blockup--"O"=blockdown--"B"=blockswitch--"Q"=pressurepad

	local collisionsheet={
	{ "X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","X"}, 
	{ "X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","X"}, 
	{ "X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","."}, 
	{ "X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","."}, 
	{ "X","X","X",".",".",".",".",".",".",".","X","X","X","X","X","X","."}, 
	{ "X","X","X",".",".",".",".",".",".",".","X","X","X","X","X","X","Q"}, 
	{ "X","X","X","X","X","X","X","X","X","X","X","X","X",".",".",".","."}, 
	{ "X","X","X","X","X","X","X","X","X","X","X","X",".",".",".","X","P"}, 
	{ "X","X","X","X","X","X","X","X","X","X","X","X","S","S","S","S","X"}, 
	{ "X","X","X","X","X","X","X","X","X","X","X","X","S","S","S","S","X"}, 
	{ "X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","X"}, 
	{ "X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","."}, 
	{ "X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","."}, 
	{ "X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","."}, 
	{ "X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","."}, 
	{ "X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","X","."}, 
	{ "X","X","X","X","X","X","X","X","X","X","X","X","S","S","S","S","."}, 
	{ "X","X","X","X","X","X","X","X","X","X","X","X","S","S","S","S","."}, 
	{ ".",".",".",".",".",".","X","X","X","X",".",".",".",".",".",".","X"}, 
	{ ".",".",".",".",".",".","X","X","X","X",".",".",".",".",".",".","."}, 
	{ ".","X","X","X","X","X","X","E","F","F","F","X","X","X","T","T","T"}, 
	{ ".","X","X","X","X","X","X","O","I","I","O","I","I",".",".","X","X"}, 
	{ "L","W","W","W","X",".",".",".","X",".",".","X",".","B","B","X","."}, 
	{ "L","X","X","X","X",".","X","X","X",".","X","X","X",".",".",".","."}, 
	{ "L","X","X","X",".",".",".","X","X","X","X","X","X",".",".",".","."}, 
	{ "X","X","X","X","X",".",".","X","X","X","X","X",".",".",".",".","."}, 
	{ "X","X","X","X","X",".",".","X","X","X",".",".",".","D","U","U","U"}, 
	{ ".",".",".",".","X",".",".",".",".",".",".",".","S","S","S","S","C"}, 
	{ ".",".",".",".",".",".",".",".",".",".",".",".","S","S","S","S","X"},
	{ ".",".",".",".","S","S","S","S","S","S","S","S","X","X","X","X","C"},
	{ "C","C","X","X","S","S","S","S","S","S","S","S","X","X","X","X","X"}
	}

 return collisionsheet
An "X" is a wall, and a "." is a floor. I load this file, and then all this information is saved to a variable. So I only need to know what sprite the tile is using, and then check this table to know if there's an "X" (a collision).

Code: Select all

---------------------------------------------------------------------------
--Put this code in the startup
 collisionReference = {}
 collisionReference = loadCollision("/tiles/dungeontiles.lua") --This is the path to the above  collision file
----------------------------------------------------------------------------

function loadCollision(path)
   newcollision = love.filesystem.load(path)() -- attention! extra parenthesis
   local collisiondata = newcollision -- execute the chunk
   return collisiondata
end
And whenever I change the tiles in the spritebatch, I run this script to also update the collisions-table accordingly. The collisions-table must always be updated along with the map-table.

Code: Select all

collisions[x+((roomX*roomWidth)-roomWidth)][y+((roomY*roomHeight)-roomHeight)] = collisionReference[math.ceil(newroom[1][y][x] / (tilesetImage:getWidth()/tileSize))][(newroom[1][y][x] - (math.floor(newroom[1][y][x] / (tilesetImage:getWidth()/tileSize))*(tilesetImage:getWidth()/tileSize)))]
So since I now know what tile the player is standing on, I can check the collision table to see if the tile is considered a wall or a floor. I can also check this for any adjacent tiles. For example, if the player is moving right, I check tile: playertileX+1,playertileY

Or if the player is moving up, I check tile: playertileX,playertileY-1

To make sure that the player doesn't cheat by walking between two tiles, I check 3 tiles in a row in front of the player. Lastly, I make sure that the sprite of the player is actually overlapping with the location of the tile. Because maybe the player is still a few pixels away from the wall. I also auto correct the location of the player, so that he is no longer overlapping with the wall. Because I don't want the player to push himself through the wall.

Image
Example picture of checking 3 tiles ahead of the player

One important tip I can give you, is to debug the collision by drawing a rectangle on the location you are checking, like I did in the picture. This allows you to check if the location is updated correctly as the map is scrolling. I use this code to draw the debug info:

Code: Select all

  for i=1,#collisionTile do
  --Collision tile
	  if (collisionTile[i][2]) then
	    love.graphics.setColor( 255, 0, 0, 255 )
	  else
	    love.graphics.setColor( 0, 255, 0, 255 )
	  end
	  love.graphics.rectangle("line", collisionTile[i][1][1],collisionTile[i][1][2],tileSize,tileSize)
	  love.graphics.setColor( 255, 255, 255, 255 )
  end
"collisionTile" is a table that contains the number of the tile, and a boolean that either indicates a collision or not. I have another script that checks the actual collision. I then run this script over all those tiles when drawing the game. The script automatically draws the rectangle as red, if the player is colliding, or green if there is no collision.

If all of this sounds a bit overwhelming, then start by figuring out what tile the player is on, and drawing a rectangle on that tile. That is step one, and you can slowly figure out the rest from there.

Re: Top Down Game Collision Help

Posted: Fri Aug 28, 2015 7:23 pm
by JuiceBox
Imaculata wrote:I'm a newby myself, but I happen to be making the exact same kind of game (only with the random dungeons). Here's what I do...
Hey, thanks for all of the info and tips! I'll be sure to try all of this out myself. :awesome:

Re: Top Down Game Collision Help

Posted: Fri Aug 28, 2015 7:56 pm
by Imaculata
My pleasure, and good luck. This is all new stuff to me as well, but so far it seems to be running smoothly.