Tilemap collision

Started by
14 comments, last by Jesperklippel 8 years, 10 months ago

Hello everybody!

I've got a bit of a problem with my tilemap collision. I've been working on this the whole afternoon, so I'm not lazy and just asking you guys.. ;)

I am programming in C++, with SFML library.

Here is my main.cpp http://pastebin.com/eAUKHLRb
And here is my player.cpp http://pastebin.com/kixD2MFX

At the bottom of the main.cpp you can see my collision code. The player position is getting updated every frame and is correct, also the positions of the tiles are correct. But when I compare those, to check for collision its just not working, and I'm getting tired of the fact that I spend alot of hours looking for a solution, and not finding one. So in the meanwhile when I look for more solutions, I wanted to ask you guys!

If you see any other things that shouldn't be, please also tell me! I am a beginner ;)

Thanks,

Jesper Klippel

Advertisement

1) You are just checking for equality of the coordinates in your collision code. If you should ever happen to move into a tile which you should actually collide with your code will not recognize that as you will already be inside the tile and not exactly at the edge. Moreover you are mixing Integers and Floats which will make that even less predictable. I guess that could be causing the problems you are experiencing.

2) Instead of looping over the whole array you can compute the coordinates of the tile the player is currently at like so:


sf::Vector2f PlayerPos = player.GetPosition() + player.GetSize() * 0.5f;
sf::Vector2i PlayerTileIndex = sf::Vector2i(floor(PlayerPos.x / TILE_WIDTH), floor(PlayerPos.y / TILE_HEIGHT))

Then you can check for walls surrounding the tile the player is at which is more elegant.
Additionally, the way you did it before any tile which is "colliding" with the player on just one axis could be detected as a collision which you probably dont want to happen.

Oh yeah and you could replace these magic numbers with constants like TILE_WDITH and TILE_HEIGHT like I did.

1) You are just checking for equality of the coordinates in your collision code. If you should ever happen to move into a tile which you should actually collide with your code will not recognize that as you will already be inside the tile and not exactly at the edge. Moreover you are mixing Integers and Floats which will make that even less predictable. I guess that could be causing the problems you are experiencing.

2) Instead of looping over the whole array you can compute the coordinates of the tile the player is currently at like so:


sf::Vector2f PlayerPos = player.GetPosition() + player.GetSize() * 0.5f;
sf::Vector2i PlayerTileIndex = sf::Vector2i(floor(PlayerPos.x / TILE_WIDTH), floor(PlayerPos.y / TILE_HEIGHT))

Then you can check for walls surrounding the tile the player is at which is more elegant.
Additionally, the way you did it before any tile which is "colliding" with the player on just one axis could be detected as a collision which you probably dont want to happen.

Oh yeah and you could replace these magic numbers with constants like TILE_WDITH and TILE_HEIGHT like I did.

So I should only check for collision at the four corners of the player sprite?

One common technique is to first check for overlap of the player bounding box with tiles (test the tiles it touches) and if any should block the player, re-test using pixel perfect collision detection.
So I should only check for collision at the four corners of the player sprite?

Well more like:

When youre moving left you just look up the 2 tiles to the left (This is the left edge tile and one of the corner tiles depending on whether the center of the player is in the upper or the lower half of its current tile). that you could be colliding with. Then you can do boundingbox collision checks for each of them and if you detect a collision you just have to push the player back out of the tile (= in that case to the right). You can do this by calculating the edge of the tile(s) the player is colliding with.

Ofc this dows only work when the player is movin to the left. If the player is moving up, for example, you have to check the upper 2 tiles and push him down.

I guess thats a pretty easy and clear way to do that.

Hello Jesperklippel!
I see that you followed my suggestion in regards of collision detection. The way I went around this problem you're having in my game was to have boolean values that store the state of the player movement.

For collision detection on left or right: Whenever a collision happens, I would check which way the player was moving. (I have an integer that stores the last key player has pressed). If he was moving for instance to the right, I will turn a boolean called "canMoveRight" to false. In the movement function, I will only move the player to the right if canMoveRight is set to true. canMoveRight remains false until the player presses the left key. same thing for the other way.

For Jumping, I had an "inAir" boolean. While in air, player's movement on the X axis remained the same as walking, while on the Y axis it changed following a simple gravity algorithm. If the player's movement was upwards, he would continue rising, and when he'd begin to fall he'd stop at the first tile his feet collided with, and "inAir" would be set to false.

My implementation is hacky-er than what other people suggested here, but it required less computation and seemed easier to implement so I went with this.

Hello Jesperklippel!
I see that you followed my suggestion in regards of collision detection. The way I went around this problem you're having in my game was to have boolean values that store the state of the player movement.

For collision detection on left or right: Whenever a collision happens, I would check which way the player was moving. (I have an integer that stores the last key player has pressed). If he was moving for instance to the right, I will turn a boolean called "canMoveRight" to false. In the movement function, I will only move the player to the right if canMoveRight is set to true. canMoveRight remains false until the player presses the left key. same thing for the other way.

For Jumping, I had an "inAir" boolean. While in air, player's movement on the X axis remained the same as walking, while on the Y axis it changed following a simple gravity algorithm. If the player's movement was upwards, he would continue rising, and when he'd begin to fall he'd stop at the first tile his feet collided with, and "inAir" would be set to false.

My implementation is hacky-er than what other people suggested here, but it required less computation and seemed easier to implement so I went with this.

Thanks dude! I'm definately gonna try this out, because the game I am making is an assigment for a study I want to do, and the interview is next tuesday. So I hate wasting time on this kind of stuff. I don't understand it because I've done collision and stuff before, and I'm not a noob.. just a beginner ;)

But well, I'll try this out and I'll let you know! :)

- Jesper

I just can't get it working... I really wanna get it done today so I can finish some other stuff up tomorrow, because like I said it needs to be done before next tuesday.
I feel I've tried everything, maybe some psuedo, or actual code would help me clear things up! I'm desperate! :(

Thank you so much for your help and patience!

- Jesper

GameDev's website is acting up for me and I can't properly use it right now. If you upload your latest code and explain what you're having difficulties with me or someone else might be able to help to a degree. Right now I'm not sure what you've done and what has happened since you started the thread.

//MOVEMENT CODE
if(upPressed)
	{
		velocity.x = 0;
		velocity.y = upSpeed;
		player.setRotation(90);
	}
	else if(downPressed)
	{
		velocity.x = 0;
		velocity.y = downSpeed;
		player.setRotation(270);
	}
	else velocity.y = 0;
	
	if(rightPressed)
		{
			velocity.y = 0;
			velocity.x = rightSpeed;
			player.setRotation(180);
		}
		else if(leftPressed)
		{
			velocity.y = 0;
			velocity.x = leftSpeed;
			player.setRotation(0);
		}
		else
			velocity.x = 0;

	
	player.move(velocity.x, velocity.y);

//COLLISION CODE
for(int i = 0; i < HEIGHT; i++)
		{
			for(int j = 0; j < WIDTH; j++)
			{
				if(level1.level[i][j] == 1)
				{
					//Bottom, top, left and right position of the tile.
					int bottom, top, left, right;
					bottom = i*32 + 32;
					top = i*32;
					left = j*32;
					right = j*32 + 32;

					if(player.right > left && player.right > left - 32)
					{
						player.player.setPosition(left-32, player.player.getPosition().y);
					}
					else if(player.left < right && player.left < right + 32)
					{
						player.player.setPosition(right + 1, player.player.getPosition().y);
					}
					else if(player.top < bottom && player.top < bottom + 32)
					{
						player.player.setPosition(player.player.getPosition().x, bottom + 1);
					}
					else if(player.bottom > top && player.bottom > top - 32)
					{
						player.player.setPosition(player.player.getPosition().x, top-32);
					}
				}

This the the movement code and the collision code. Right now my character is just glitching and constantly changing position. I can't move, it just glitches!

Thanks again..

This topic is closed to new replies.

Advertisement