Tile Based Collisions Problem

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
tjmolinski
Prole
Posts: 4
Joined: Wed Mar 26, 2014 3:38 am

Tile Based Collisions Problem

Post by tjmolinski »

Hi everyone, new to Lua and the Love framework so trying to wrap my head around some things.

I have been following a tile based game tutorial (http://www.tonypa.pri.ee/). It's written for flash but I'm trying to translate it into Lua and get it to work with Love too. I'm getting stuck though on tutorial "Hit the Wall". It's technically working but there is some funny behavior when colliding with the right side and the bottom side of my character. The top side and left side collisions are smooth and don't "grab" me or make me stick against the wall when holding in those directions. For example when I'm holding up and left while colliding at the top wall I will still continue to move left. On the other hand when I am moving to the right and down then collide with the right wall, it will grab me and I stop moving down.

I fiddled with the helper functions I wrote thinking I was off by one but that didn't do anything. I have a feeling I'm missing something stupid but figured I'd ask and maybe see the solution as I'm typing or thinking. My thought process for how this should work is that I check the 4 corners of my character. Then I take those 4 corners and look at them through the tile map to see if it is an open space if so we are good to move otherwise stop my movement.

I have also attached the love file and you can move around with the arrow keys. You are the red square and the walls are they grey square.

This is my movement code

Code: Select all

function Hero:move(dx, dy)                                
  local myY = getObjectTileY(self)                        
                                                          
  --FIX: Shit is still wonky                              
  getCorners(self.x, self.y+dy, self)                     
  if dy < 0 then                                          
    if self.upleft and self.upright then                  
      self.y = self.y + dy                                
    else                                                  
      print('hit top')                                    
      self.y = getPixelPositionY(myY)                     
    end                                                   
  end                                                     
  if dy > 0 then                                          
    if self.downleft and self.downright then              
      self.y = self.y + dy                                
    else                                                  
      print('hit bottom')                                 
      self.y = getPixelPositionY(myY)                     
    end                                                   
  end                                       
             
  local myX = getObjectTileX(self)                        
  getCorners(self.x+dx, self.y, self)                     
  if dx < 0 then                                          
    if self.downleft and self.upleft then                 
      self.x = self.x + dx                                
    else                                                  
      print('hit left')                                   
      self.x = getPixelPositionX(myX)                     
    end                                                   
  end
  if dx > 0 then
    if self.upright and self.downright then
      self.x = self.x + dx
    else
      print('hit right')
      self.x = getPixelPositionX(myX)
    end
  end
end
This is the getting corners

Code: Select all

function getCorners(x, y, ob)
  local obj = ob
  obj.x = x
  obj.y = y
  local downY = getObjectTileBottomMostY(obj)
  local upY = getObjectTileTopMostY(obj)
  local leftX = getObjectTileLeftMostX(obj)
  local rightX = getObjectTileRightMostX(obj)

  ob.upleft = map[upY][leftX] == 0
  ob.downleft = map[downY][leftX] == 0
  ob.upright = map[upY][rightX] == 0
  ob.downright = map[downY][rightX] == 0
end
These are the helper functions I have to get tile positions

Code: Select all

function getPixelPositionY(y)
  return mapY + ((y-1) * tileH)
end

function getPixelPositionX(x)
  return mapX + ((x-1) * tileW)
end

function getObjectTileLeftMostX(obj)
  return math.floor(((obj.x-mapX)/tileW)+1)
end

function getObjectTileRightMostX(obj)
  return math.floor((((obj.x+obj.width+1)-mapX)/tileW)+1)
end

function getObjectTileX(obj)
  return math.floor((((obj.x+(obj.width/2))-mapX)/tileW)+1)
end

function getObjectTileTopMostY(obj)
  return math.floor(((obj.y-mapY)/tileH)+1)
end

function getObjectTileBottomMostY(obj)
  return math.floor((((obj.y+obj.height-1)-mapY)/tileH)+1)
end

function getObjectTileY(obj)
  return math.floor((((obj.y+(obj.height/2))-mapY)/tileH)+1)
end
Attachments
MustachedNemesis.love
(18.14 KiB) Downloaded 71 times
User avatar
micha
Inner party member
Posts: 1083
Joined: Wed Sep 26, 2012 5:13 pm

Re: Tile Based Collisions Problem

Post by micha »

The love-file you uploaded seems to be packed incorrectly. I cannot run it.

Your code is very well organized, so I think I know where the problem is. You get tile coordinates of the four corners of the player and store them in bottomY, topY etc. These work fine except for the case, when the player corner is exactly aligned with the face of a tile. In these cases, bottomY and rightX give you the wrong position. To fix this, replace the math.floor by a math.ceil and remove one +1.

Code: Select all

function getObjectTileRightMostX(obj)
  return math.ceil((((obj.x+obj.width-1)-mapX)/tileW))
end
function getObjectTileBottomMostY(obj)
  return math.ceil((((obj.y+obj.height-1)-mapY)/tileH))
end
tjmolinski
Prole
Posts: 4
Joined: Wed Mar 26, 2014 3:38 am

Re: Tile Based Collisions Problem

Post by tjmolinski »

That's strange that you can't open the love file I was able to download it and run in. I'm on Love 0.9.0 and linux if that means anything. I could upload a windows/mac executable if that would make it easier to see but not sure how people feel about running an executable. I'm attempting to attach another love file please let me know if it works.

So when I changed those two functions to this:

Code: Select all

function getObjectTileRightMostX(obj)
  return math.ceil((((obj.x+obj.width)-mapX)/tileW))
end
function getObjectTileBottomMostY(obj)
  return math.ceil((((obj.y+obj.height)-mapY)/tileH))
end
It seemed to work a lot better now when I removed the -1 after the height and width, but there seems to be another problem with the initial collision. This issue doesn't always show but it's pretty frequent. When I am moving diagonally (up/left, up/right, down/left, down/right) and make an initial collision with any of the walls, the character then snaps into a tile position. After the initial snap though I'm able to move freely alongside the wall.

Also thank you for the help, still not quite sure why ceil works for those two cases. I tried ceils for the left and top side and they began acting like my first problem so I reverted those changes.
Attachments
MustachedNemesis.love
(18.15 KiB) Downloaded 70 times
User avatar
micha
Inner party member
Posts: 1083
Joined: Wed Sep 26, 2012 5:13 pm

Re: Tile Based Collisions Problem

Post by micha »

Sorry for not specifying more precisely. I got this error, when I ran the .love file:

Code: Select all

Error: puzzleBoard.lua:3: module 'wallBlock' not found:
	no file "wallBlock.lua" in LOVE game directories.
And I am on linux, too. So you don't need to make and executable.

I will have a look into the new collision issue you report.
tjmolinski
Prole
Posts: 4
Joined: Wed Mar 26, 2014 3:38 am

Re: Tile Based Collisions Problem

Post by tjmolinski »

Woops, definitely forgot to include that in my script. Hopefully this new love file works.
Attachments
MustachedNemesis.love
(18.51 KiB) Downloaded 80 times
User avatar
micha
Inner party member
Posts: 1083
Joined: Wed Sep 26, 2012 5:13 pm

Re: Tile Based Collisions Problem

Post by micha »

I found it. May I say again, that your code is very well organized. That made it quiet easy to find the problem.

The problem lies in the function getCorners:

Code: Select all

function getCorners(x, y, ob)
  local obj = ob
  obj.x = x
  obj.y = y

  -- more stuff
end
When you call obj=ob then the table ob is not copied, but obj just gets a reference to the very same table. That means that in the following two lines you actually change the coordinates of the object. This, of course, is not what you want. This function is not supposed to change the object's coordinates.
So replace the first three lines by this:

Code: Select all

function getCorners(x, y, ob)
  local obj = {x = x,y=y,width=ob.width,height=ob.height}

  -- more stuff
end
This creates a new table, with the x,y coordinates as given to the function and the height and width taken from the inserted object. Alternatively you could rewrite the function such that height and width are given as parameters, too, instead of the full object.

You will notice that the red box will now move only half as fast as before, because you accidentally did the movement twice.

Now the collision detection looks solid.
tjmolinski
Prole
Posts: 4
Joined: Wed Mar 26, 2014 3:38 am

Re: Tile Based Collisions Problem

Post by tjmolinski »

Ahhh well that's good to know for later.

Thanks so much for that, definitely made it work flawlessly.
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot], Google [Bot] and 2 guests