One of Those Collision Threads

So I’m trying to make collision work in my platformer. I have a player entity, and a collision component that I’m adding to the entity. Here’s the update function in the collision component:

    @Override
    public void update(GameContainer gc, StateBasedGame sb, int delta) {
        Vector2f position = owner.getPosition();
        
        position.x += owner.getVelocity().x;
        rect.setLocation(position);
        for (int i = 0; i < map.getWidth(); i++) {
            for (int j = 0; j < map.getHeight(); j++) {
                if (map.getTileId(i, j, 0) > 0) {
                    Rectangle tileRect = new Rectangle(i*16, j*16, 16, 16);
                    
                    if (rect.intersects(tileRect)) {
                        if (owner.getVelocity().x < 0) {
                            position.x = tileRect.getX()+tileRect.getWidth();
                        }
                        if (owner.getVelocity().x > 0) {
                            position.x = tileRect.getX()-rect.getWidth();
                        }
                        rect.setLocation(position);
                    }
                }
            }
        }
        
        position.y += owner.getVelocity().y;
        rect.setLocation(position);
        for (int i = 0; i < map.getWidth(); i++) {
            for (int j = 0; j < map.getHeight(); j++) {
                if (map.getTileId(i, j, 0) > 0) {
                    Rectangle tileRect = new Rectangle(i*16, j*16, 16, 16);
                    
                    if (rect.intersects(tileRect)) {
                        if (owner.getVelocity().y < 0) {
                            position.y = tileRect.getY()+tileRect.getHeight();
                        }
                        if (owner.getVelocity().y > 0) {
                            position.y = tileRect.getY()-rect.getHeight();
                        }
                        rect.setLocation(position);
                    }
                }
            }
        }
        
        owner.setPosition(position);
        
    }

If you’re having trouble understanding what the code does, here’s some pseudo code:

-Move the player's Rectangle across the x axis according to the players x velocity
-Check to see if the player's Rectangle collides with any of the tiles' rectangles
-if it does:
    -if the player is going left:
        -set the player's x position to be just right of the tile
    -if the player is going right:
        -set the player's x position to be just left of the tile

-Move the player's Rectangle across the x axis according to the player's y velocity
-Check to see if the player's Rectangle collides with any of the tiles' rectangles
-if it does:
    -if the player is going up:
        -set the player's y position to be just below the tile
    -if the player is going down:
        -set the player's y position to be just above the tile

This works well until I start moving the player across the x-axis. When the player moves right or left, suddenly the player is shot to the edge of the very left tile in the row. Anyone know how to fix this?

Try setting the y position at the same time you’re setting the x.

Otherwise, your entity is starting out at a y = 0, which will always intersect anything in your array at height = 0.

Why can’t you just combine them into 1 set of double for loops? The above code takes twice as long and twice as much memory than it should :slight_smile:

EDIT: Also, your player gets shot to the end because you keep looping to the end. You check intersection against each and every tile on the map! Shouldn’t you be only checking against the tile that you moved to?


//apply velocities to X and Y

if(map.getTileId(position.x/16,position.y/16) > 0 && /* check for rect v rect collision */)
    //you intersected, move back

Only way i could get it to work is to make two collison methods to check X and then Y collision separately, if x returned true the player moved x and if y = true then player.moveY…

The meathods create a temporary variable which represents where the player will be, for the x method for example
int newX = player.getX() + player.getVelY()
work out where he is on the grid by divison to work out the tile he is on. Work out a tile for each corner of the player e.g. the top left point, on 64x64 tiles
tileTopLeftPointX = newX/64;
tileTopRightPointY = player.getY()/64; // as this is just checking x collision player.getY() is just as it is

Then if this new tile is solid that means if the player moves he will be colliding with something so return the meathod false so the player doesnt move.
To stop the player from having a gap between him and the wall hes walking into if he has a high x velocity try to find the minmum x velocity the player could have moved and not intersect with the tile, if this is found then set the player velocty X to this value so he will squish against the wall, you can change how accurate you want it to be e.g.

if(tiles[i][p].isSolid()){          // where i, p are integers worked out previously of the tile the top left player point is currently on
				if(tiles[i][p].getBounds().contains(topLeftX, topLeftY)){
					while(!(nDX > -0.2 && nDX < 0.2)){ // so if velocity is between here stop the loop becuase it is negligable
						if(nDX > 0){
							nDX -= 0.02;    //you could change the 0.02 smaller or larger to make it more or less accurate
						}else if(nDX < 0){
							nDX += 0.02;
						}
						topLeftX = (int) (player.getX() + nDX); // store where the player would be to see if he can now move
						if(!(tiles[i][p].getBounds().contains(topLeftX, topLeftY))){ // if he can move set his velocity and return true to move x
							player.setDX(nDX);
							return true;
						}
					}
					return false; // the while loop broke meaning he is close to the wall
				}else if // check the other points around the player for collision as say the bottom right of the player rect may be one a diffrent tile


I was trying to port this python code to java. This code worked perfectly in python.

If I understand correctly, this assumes that the player has the same dimensions as my tiles (16x16,) but my player is actually 39x60 pixels. Will this still work?

Bwarg! Don’t use ‘magic’ constants please. It makes things difficult to read/edit at times (In other words, make the replace the various 16 with an int variable equal to 16, perhaps two, to ensure that you can easily update things later.)

If I read it correctly, that will not work if your character is larger than the tile size. Since it will only return a single object (That’s located at some corner/position relative to your character).

My advice? Check what your position is prior to that. And find out which block is actually causing you to jump to the left side. That will at least tell you which block is giving you an odd result. Collect some other data as well, see if there’s some odd value of speed that is causing it. If you can tell us what your map looks like and which of the blocks is causing your algorithm to break, we can probably give you better input on the ‘why’.