Tile based Collision Detection not working right

Okay, so now that my jump is working it’s breaking my collision detection. I need to figure out how to properly do it, so could someone please take a look at my code and tell me whats wrong?


public void update(Map m, Enemy[] e1, int max_enemies, Item[] i1, int max_Items){
		//System.out.println("x: "+(int) (x/m.tileWidth));
		//System.out.println("y: "+(int) ((y/m.tileHeight) - 0.1));
		if(state != 2)
		{
			if(jumping){
				if(m.tiles[(int) (x/m.tileWidth)][(int)  ((y/m.tileHeight))] == 0 &&
					m.tiles[(int) (x/m.tileWidth)][(int)  ((y + height)/m.tileHeight)] == 0 &&
					m.tiles[(int) ((x + width)/m.tileWidth)][(int)  ((y/m.tileHeight))] == 0 &&
					m.tiles[(int) ((x + width)/m.tileWidth)][(int)  ((y + height)/m.tileHeight)] == 0 &&
					!colbottom(i1, max_Items)){
					velo = -jumpspeed;
					y -= velo;
					jumping = false;
				}
				
			else
				velo = jumpspeed;
					
					
		}
			
			if(m.tiles[(int) ((x/m.tileWidth) + 0.1)][(int) (((y/m.tileHeight) - 0) - 0.0005)] == 0 &&
			   m.tiles[(int) ((((x + width)/m.tileWidth)) - 0.1)][(int) (((y/m.tileHeight) - 0) - 0.0005)] == 0 &&
			   	!coltop(i1, max_Items) && !jumping){
				falling = true;
				velo += jumpangle;
			}
			
			else {
				falling = false;
				velo = 0;
			}
			
			//max fall speed
			if(velo > 4)
				velo = 4;
				
			System.out.println("velo: " + velo + " " + falling);
			
			y -= velo;
			
			if(m.tiles[(int) ((x/m.tileWidth))][(int) (y/m.tileHeight)] == 1)
			{	
				y = (((y) + m.tileHeight) - 0.0005f);
				falling = false;
			}
			
			if(collision(e1,max_enemies))
			{
				state = 2;
				frame = 0;
			}
		}
	}

Could you say what’s going wrong first? It’ll be easier to figure out the problem.

Also, I don’t know if you want to test every corner against the tile map like that, at least not with a regular sized bounding box. Try with a smaller bounding box, aside from the guy’s feet, probably.

[please delete this post]

In this build he teleport up one tile each time he hits the ground, aka bouncing. in another build I tried he fell too far into the ground before stopping and couldn’t move nor jump. I’m wondering how to fix both of these problems. Also, what do you mean corners? left and right? they have a smaller bounding box already.

Yeah, your problem is really a question of the best way to handle smooth collision, I think. And that’s a topic you could spend years researching. One super easy and super slow way to do it is simply to use a while loop to move your character, and only move him one pixel at a time either until he has hit something or he has moved as far as you wanted him to in that step. This way once you find a collision, you know all you need to do is set his velocity to 0 and you’re all set. Tile-based collision is obviously really easy, though, so you can simply calculate which tiles he will move through with his next velocity change, then test those tiles. When you find a tile he will collide with, you simply want to move him as close to that tile as possible, then set his velocity to 0.

Looks like maybe you’re not setting your velocity to 0 correctly. Typically what works is this:

  1. Apply world velocity (like gravity).
  2. Apply velocity caused by the player (moving left or right, jumping).
  3. Apply velocity caused by other entities (getting shot with a bullet, punched, etc).
  4. Apply velocity caused by collisions with the environment (like standing on a block).
  5. Move the object by its final velocity.

#4 will always apply an equal and opposite force, just like you learned in physics. So say you discover he’s got a final velocity of x = 5 and y = -20, which is the total of 1 - 3. Find the angle of the collision, then use sine and cosine to figure out how much equal and opposite force you will apply in each direction. With a velocity of (5,-20), the actual magnitude is sqrt(55 + -20-20) or around 20.6. Now we find that because we’re standing on top of a block, the tangent of collision is 90. cos(90) is 0, and sin(90) is 1. So we’ve discovered that all the force pushed back by the environment will be going upward, because we’re applying (20.6 * 0, 20.6 * 1) or (0,20.6) back to the object. Add this to his current downward velocity, and he’ll appear to “bounce” a little bit with a resulting force of (5, -0.6), causing a sort of “skid” across the ground. To make this all look natural, you need some damping (i.e. a simple form of damping would say any vector component that is < 1 is set to 0), and you also need to come up with some sort of friction for the ground, so that while skidding his X velocity is also reduced. Typically a simple multiple (like 0.05) every timestep works. When you apply friction, you want to use the perpendicular tangent, so you’d be using 0 degrees, which will come out with an x of 1 and a y of 0. So your friction then becomes vel.x -= vel.x * 1 * 0.05 and vel.y -= vel.y * 0 * 0.05. Effectively, vel.x -= vel.x * 0.05 and vel.y -= 0.

So there you go. Super easy, no? :stuck_out_tongue:

I think that’s way too complicated. I’m looking for a simple fix. Like a 3-5 line change in my update method. That’s way over my head. Can’t anyone actually try running the code, and try their hand at fixing it like that? That’s one of the main reasons I open sourced it was so I could get help like that. Than it’d be there for everyone to see and learn from.

Bump.

The problem is, it’s actually a decent bit of work to even get that code running - it involves both tracking down the full source code that it’s a part of (http://prime.turquoisegrotto.com/java/xenplat_src.zip, from what I gather), manually patching in this modified update() method, and digging up the OpenGL jars and binaries and figuring out how to get it all compiling and running correctly. Worse, even if someone was to go through that, they’d find that there are still problems, for instance, the “velo” member variable is nowhere to be found in the Player class. So I can’t help but wonder if there are other things that have changed, and it all just seems like too much trouble.

Make it dead simple for people to help, and they will, but “dead simple” usually means you can cut and paste code into a new .java file and run it immediately.

In any case, in taking a look through that method I didn’t see an obvious solution. One thing I’m sure others are thinking is that your code is a little hard to understand, and might make more sense (to you, as well as us) if you pulled out and named some of the important things it does. For instance:


if(m.tiles[(int) (x/m.tileWidth)][(int)  ((y/m.tileHeight))] == 0 &&
					m.tiles[(int) (x/m.tileWidth)][(int)  ((y + height)/m.tileHeight)] == 0 &&
					m.tiles[(int) ((x + width)/m.tileWidth)][(int)  ((y/m.tileHeight))] == 0 &&
					m.tiles[(int) ((x + width)/m.tileWidth)][(int)  ((y + height)/m.tileHeight)] == 0 &&
					!colbottom(i1, max_Items))

Reading that inline makes everything seem quite complicated, it might be better to pull it into its own function, say Player::collidesWith(Map m), and do that whenever you have snippets of code that can reasonably be considered to carry out a single task.

I think the way you’re doing the jumping and movement overall might be complicating matters, though, it’s very woven in with the collision detection and resolution, and that’s going to make it tricky to debug. The typical approach to handling movement is something like this (only showing the relevant stuff):


class Player {
  private float x, y; //positions
  private float vx, vy; //velocities
  private float ax, ay; //accelerations, before gravity is applied

  static private float GRAVITY = -9.8;

  //getters/setters and game-specific stuff omitted
  
  public void physicsUpdate(float dt) {
    vx += dt * ax;
    vy += dt * (ay + GRAVITY);
    x += dt * vx;
    y += dt * vy;
  }
}

Then your jump method would be:


public void jump(float jumpSpeed) {
  if (this.canJump()) { //have to write the canJump() method, too
    vy += jumpSpeed;
  }
}

and you would handle collisions in another method, probably called right after (or at the end of) the updatePhysics one.

I would strongly recommend thinking about the stuff Demonpants said - collision detection is not easy, and in my experience (I’ve hacked together several physics engines in my time), it’s almost impossible to get rid of all the bugs unless you have the logic really squared away. In a tile engine it’s much simpler, but it still usually involves figuring out which side of the tile the player collided with, projecting the position out of the tile in the normal direction, and setting the appropriate component of the velocity to zero.

I have too much trouble reading other peoples code, but if he sinks into the floor, then you need to do further testing after you have found a collision with another object and back him up to where he should be before making the placement. You could do this by finding his last position and direction before the collision perhaps and then checking per pixel, or getting the edges of both objects and simply putting him where he should be if he came right up to it. The bouncing is just a logic error that you need to find. You also need to confirm in some way exactly what the problem is either by print statement or debugging right at the time of collision to see if your thinking is right, but both errors sound like inexact collision detection.
Another thing you can do is put a little play in how close he has to be to the ground before he makes a jump. You may be just a few pixels off and it doesn’t really matter.
Also, remember that the only thing you changed is vertical velocity, so that’s probably the only problem that needs fixing. Your horizontal collision is probably still all right.

Also there was sort of an error in my code, which was just that I was indicating the wrong reflection angle. The reflection angle should pass over the tangent of the surface you’re colliding against, so basically it would go from (5,-20) to (-5,20), but only in an environment with no lost energy. Anyway that seems to be above your head anyway so let me try to go on a simpler (less robust) way on doing it.

  1. Adjust your guy’s “velo” by the combination of gravity, movement keys, etc.
  2. Check for collision.
  3. If he has collision below him, set the velo to 0.
  4. Move his position by his velo.

You really just need to think about keeping components separate and in logical steps. If you know that his position is only ever changed in one place and it’s changed by his velocity and nothing else (which is usually a smart way to do things), then you know that you have a good chance to adjust the velocity before his position is ever changed. Thus if you always catch and adjust the velocity based upon collision before you get to that step #4, then you know that he’ll never sink into the floor.

Your code above is very coupled, making it difficult to determine what’s going on for anyone (including yourself).

It works now! Thank you all for your help! Here’s the working function so far:



	public void update(Map m, Enemy[] e1, int max_enemies, Item[] i1, int max_Items){
		//System.out.println("x: "+(int) (x/m.tileWidth));
		//System.out.println("y: "+(int) ((y/m.tileHeight) - 0.1));
		if(state != 2)
		{
			
					
					
		}
			if(velo >= 0)
			{
				if(jumping){
					velo = -jumpspeed;
					
					if(m.tiles[(int) ((x/m.tileWidth))][(int) ((((y + height) -velo)/m.tileHeight) + 0.0006)] == 0 &&
						m.tiles[(int) (((x + width)/m.tileWidth))][(int) ((((y + height) -velo)/m.tileHeight) + 0.0006)] == 0)
					y -= velo;
					
					jumping = false;
				}
				
				if(m.tiles[(int) ((x/m.tileWidth) + 0.1)][(int) (((y/m.tileHeight) - 0.0005))] == 0 &&
				   m.tiles[(int) ((((x + width)/m.tileWidth)) - 0.1)][(int) (((y/m.tileHeight)  - 0.005f))] == 0 &&
				   	!coltop(i1, max_Items)){
					falling = true;
					velo += jumpangle;
				}
				
				else {
					falling = false;
					velo = 0;
				}
				
				//max fall speed
				if(velo > 4)
				velo = 4;
				
				if(m.tiles[(int) ((x/m.tileWidth))][(int) (((y -velo)/m.tileHeight) - 0.0006)] == 0 &&
				   m.tiles[(int) (((x + width)/m.tileWidth))][(int) (((y -velo)/m.tileHeight) - 0.0006)] == 0)
				y -= velo;
				
				else	
				{
					velo = 0;
					falling = false;
				}
			}
			
			else
			{
				if(m.tiles[(int) ((x/m.tileWidth) + 0.1)][(int) ((((y + height)/m.tileHeight) - 0.0005))] == 0 &&
					m.tiles[(int) (((x + width)/m.tileWidth) + 0.1)][(int) ((((y + height)/m.tileHeight) - 0.0005))] == 0 &&
					!coltop(i1, max_Items)){
					falling = true;
					velo += jumpangle;
				}
				
				else
					velo = 0;
					
				if(m.tiles[(int) ((x/m.tileWidth))][(int) ((((y + height) -velo)/m.tileHeight) + 0.0006)] == 0 &&
				   m.tiles[(int) (((x + width)/m.tileWidth))][(int) (((y -velo)/m.tileHeight) - 0.0006)] == 0)
				y -= velo;
				
				else
					velo = 0;
			}
			
			
			
			
			
				
			System.out.println("velo: " + velo + " " + falling);
			
			
			
			
			
			if(collision(e1,max_enemies))
			{
				state = 2;
				frame = 0;
			}
	}

Awesome, man, glad I my ranting about physics helped. :slight_smile: