Skip to main content
added 119 characters in body
Source Link
oxysoft
  • 713
  • 6
  • 15

In order to fix this problem, I recommend looping in every directionsloop from 0 to the amount of pixels to move in whichever direction you entity is moving towards. On top of that, you need to loop from 0 to HEIGHT_OF_ENTITY[HEIGHT_OF_ENTITY] for horizontal movement and WIDTH_OF_ENTITY for vertical movement, to ensure that the entity does not get stuck inside blocked tiles. This will ensure correctly working collisions in any scenario, and should never fail.

Here is some old code I wrote a few months back. I cleaned it a little bit and documented it so it would be easier to understand. It was working properly if

It's my base collision code, I recall correctlyrewrite this every time I want tile based collisions and it works perfectly.

public bool resolveCollisions() {
    if (xa == 0 && ya == 0) return false;
    if (xa != 0 && ya != 0) throw new ArgumentException("Can only resolve collisions along one axis at a time");

    if (xa != 0) { //horizontal movement
        if (xa < 0) { //movement towards the left
            for (int i = 0; i > xa; i--) {
                for (int j = OffsetY; j < OffsetY + Height; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2(Location.X + OffsetX + i, Location.Y + j) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.X = i + 1;
                        return true; //COLLISION!
                    }
                }
            }
        } else if (xa > 0) { //movement towards the right
            for (int i = 0; i < xa; i++) {
                for (int j = OffsetY; j < OffsetY + Height; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2(Location.X + OffsetX + Width + i, Location.Y + j) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.X = i; //stop movement!
                        return true; //COLLISION!
                    }
                }
            }
        }
        return false;
    } else if (ya != 0) { //vertical movement
        if (ya < 0) { //up movement
            for (int i = 0; i > ya; i--) {
                for (int j = OffsetX; j < OffsetX + Width; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2(Location.X + j, Location.Y + OffsetY + i) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.Y = i + 1;
                        return true; //COLLISION!
                    }
                }
            }
            InAir = true; //A boolean which indicts that the entity is not on ground!
        } else if (ya > 0) { //down movement
            for (int i = 0; i < ya; i++) {
                for (int j = OffsetX; j < OffsetX + Width; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2((int) (Location.X + j), (int) (Location.Y + OffsetY + Height + i)) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.Y = i;
                        if (InAir) //Is the player in the airs?
                            OnLand(); //Entity is landing, make sure you set InAir to false inside this method
                        return true; //COLLISION!
                    }
                }
            }
        }
        return false;
    } else {
        //sanity check
        throw new ArgumentException("Can only resolve collisions along one axis at a time");
    }
}
  • This code was written for a platformer, with gravity. But it can easily be edited to fit the needs of a top-down view game.

  • OffsetX and OffsetY are variables to help define a better hitbox in conjunction of the Width and Height variables

  • The tile classes should have a method OnCollide with a parameter of type Entity that will return true if this tile should block passage or false if not. You can also add special handling like damaging the entity if it's spikes or whatever.

  • I'm not entirely sure why you shouldn'tI'm not entirely sure why you shouldn't check collisions along both x and y axis at the same time but I remember I had problems when doing it that way, so I only check along x and y, separately. You can't check collisions along both x-axis and y axis at-axis both in the same time but I remember I had problems when doing it that way, so I only check along x and ymethod call because we can't return two booleans, separately.duh! (Unless we returned a Tuple of two booleans of course)

In order to fix this problem, I recommend looping in every directions from 0 to the amount of pixels to move. On top of that, loop from 0 to HEIGHT_OF_ENTITY for horizontal movement and WIDTH_OF_ENTITY for vertical movement. This will ensure correctly working collisions in any scenario.

Here is some old code I wrote a few months back. I cleaned it a little bit and documented it so it would be easier to understand. It was working properly if I recall correctly.

public bool resolveCollisions() {
    if (xa == 0 && ya == 0) return false;
    if (xa != 0 && ya != 0) throw new ArgumentException("Can only resolve collisions along one axis at a time");

    if (xa != 0) { //horizontal movement
        if (xa < 0) { //movement towards the left
            for (int i = 0; i > xa; i--) {
                for (int j = OffsetY; j < OffsetY + Height; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2(Location.X + OffsetX + i, Location.Y + j) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.X = i + 1;
                        return true; //COLLISION!
                    }
                }
            }
        } else if (xa > 0) { //movement towards the right
            for (int i = 0; i < xa; i++) {
                for (int j = OffsetY; j < OffsetY + Height; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2(Location.X + OffsetX + Width + i, Location.Y + j) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.X = i;
                        return true; //COLLISION!
                    }
                }
            }
        }
        return false;
    } else if (ya != 0) { //vertical movement
        if (ya < 0) { //up movement
            for (int i = 0; i > ya; i--) {
                for (int j = OffsetX; j < OffsetX + Width; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2(Location.X + j, Location.Y + OffsetY + i) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.Y = i + 1;
                        return true; //COLLISION!
                    }
                }
            }
            InAir = true; //A boolean which indicts that the entity is not on ground!
        } else if (ya > 0) { //down movement
            for (int i = 0; i < ya; i++) {
                for (int j = OffsetX; j < OffsetX + Width; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2((int) (Location.X + j), (int) (Location.Y + OffsetY + Height + i)) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.Y = i;
                        if (InAir) //Is the player in the airs?
                            OnLand(); //Entity is landing, make sure you set InAir to false inside this method
                        return true; //COLLISION!
                    }
                }
            }
        }
        return false;
    } else {
        //sanity check
        throw new ArgumentException("Can only resolve collisions along one axis at a time");
    }
}
  • This code was written for a platformer, with gravity. But it can easily be edited to fit the needs of a top-down view game.

  • OffsetX and OffsetY are variables to help define a better hitbox in conjunction of the Width and Height variables

  • The tile classes should have a method OnCollide with a parameter of type Entity that will return true if this tile should block passage or false if not. You can also add special handling like damaging the entity if it's spikes or whatever.

  • I'm not entirely sure why you shouldn't check collisions along both x and y axis at the same time but I remember I had problems when doing it that way, so I only check along x and y, separately.

In order to fix this problem, loop from 0 to the amount of pixels to move in whichever direction you entity is moving towards. On top of that, you need to loop from 0 to [HEIGHT_OF_ENTITY] for horizontal movement and WIDTH_OF_ENTITY for vertical movement, to ensure that the entity does not get stuck inside blocked tiles. This will ensure correctly working collisions in any scenario, and should never fail.

Here is some old code I wrote a few months back. I cleaned it a little bit and documented it so it would be easier to understand.

It's my base collision code, I rewrite this every time I want tile based collisions and it works perfectly.

public bool resolveCollisions() {
    if (xa == 0 && ya == 0) return false;
    if (xa != 0 && ya != 0) throw new ArgumentException("Can only resolve collisions along one axis at a time");

    if (xa != 0) { //horizontal movement
        if (xa < 0) { //movement towards the left
            for (int i = 0; i > xa; i--) {
                for (int j = OffsetY; j < OffsetY + Height; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2(Location.X + OffsetX + i, Location.Y + j) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.X = i + 1;
                        return true; //COLLISION!
                    }
                }
            }
        } else if (xa > 0) { //movement towards the right
            for (int i = 0; i < xa; i++) {
                for (int j = OffsetY; j < OffsetY + Height; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2(Location.X + OffsetX + Width + i, Location.Y + j) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.X = i; //stop movement!
                        return true; //COLLISION!
                    }
                }
            }
        }
        return false;
    } else if (ya != 0) { //vertical movement
        if (ya < 0) { //up movement
            for (int i = 0; i > ya; i--) {
                for (int j = OffsetX; j < OffsetX + Width; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2(Location.X + j, Location.Y + OffsetY + i) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.Y = i + 1;
                        return true; //COLLISION!
                    }
                }
            }
            InAir = true; //A boolean which indicts that the entity is not on ground!
        } else if (ya > 0) { //down movement
            for (int i = 0; i < ya; i++) {
                for (int j = OffsetX; j < OffsetX + Width; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2((int) (Location.X + j), (int) (Location.Y + OffsetY + Height + i)) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.Y = i;
                        if (InAir) //Is the player in the airs?
                            OnLand(); //Entity is landing, make sure you set InAir to false inside this method
                        return true; //COLLISION!
                    }
                }
            }
        }
        return false;
    } else {
        //sanity check
        throw new ArgumentException("Can only resolve collisions along one axis at a time");
    }
}
  • This code was written for a platformer, with gravity. But it can easily be edited to fit the needs of a top-down view game.

  • OffsetX and OffsetY are variables to help define a better hitbox in conjunction of the Width and Height variables

  • The tile classes should have a method OnCollide with a parameter of type Entity that will return true if this tile should block passage or false if not. You can also add special handling like damaging the entity if it's spikes or whatever.

  • I'm not entirely sure why you shouldn't check collisions along both x and y axis at the same time but I remember I had problems when doing it that way, so I only check along x and y, separately. You can't check along x-axis and y-axis both in the same method call because we can't return two booleans, duh! (Unless we returned a Tuple of two booleans of course)

added 119 characters in body
Source Link
oxysoft
  • 713
  • 6
  • 15

Here is some old code I may edit this withwrote a few months back. I cleaned it a little bit of pseudocode laterand documented it so it would be easier to understand. It was working properly if I recall correctly.

It's in C# but it should be very easy to understand for you since Java's syntax is almost exactly the same.

public bool resolveCollisions() {
    if (xa == 0 && ya == 0) return false;
    if (xa != 0 && ya != 0) throw new ArgumentException("Can only resolve collisions along one axis at a time");

    if (xa != 0) { //horizontal movement
        if (xa < 0) { //movement towards the left
            for (int i = 0; i > xa; i--) {
                for (int j = OffsetY; j < OffsetY + Height; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2(Location.X + OffsetX + i, Location.Y + j) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.X = i + 1;
                        return true; //COLLISION!
                    }
                }
            }
        } else if (xa > 0) { //movement towards the right
            for (int i = 0; i < xa; i++) {
                for (int j = OffsetY; j < OffsetY + Height; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2(Location.X + OffsetX + Width + i, Location.Y + j) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.X = i;
                        return true; //COLLISION!
                    }
                }
            }
        }
        return false;
    } else if (ya != 0) { //vertical movement
        if (ya < 0) { //up movement
            for (int i = 0; i > ya; i--) {
                for (int j = OffsetX; j < OffsetX + Width; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2(Location.X + j, Location.Y + OffsetY + i) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.Y = i + 1;
                        return true; //COLLISION!
                    }
                }
            }
            InAir = true; //A boolean which indicts that the entity is not on ground!
        } else if (ya > 0) { //down movement
            for (int i = 0; i < ya; i++) {
                for (int j = OffsetX; j < OffsetX + Width; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2((int) (Location.X + j), (int) (Location.Y + OffsetY + Height + i)) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.Y = i;
                        if (InAir) //Is the player in the airs?
                            OnLand(); //Entity is landing, make sure you set InAir to false inside this method
                        return true; //COLLISION!
                    }
                }
            }
        }
        return false;
    } else {
        //sanity check
        throw new ArgumentException("Can only resolve collisions along one axis at a time");
    }
}

Plug that method into your entity class and then these two calls into your update method

resolveCollisions(velocity.x, 0);
resolveCollisions(0, velocity.y);

The code loops complicated and very math/logic intensive but it really isn't and is fairly straightforward once you get the meaning behind it. The same logic is repeated 4 times essentially. (4 different directions)

A few clarifications

  • This code was written for a platformer, with gravity. But it can easily be edited to fit the needs of a top-down view game.

  • OffsetX and OffsetY are variables to help define a better hitbox in conjunction of the Width and Height variables

  • The tile classes should have a method OnCollide with a parameter of type Entity that will return true if this tile should block passage or false if not. You can also add special handling like damaging the entity if it's spikes or whatever.

  • I'm not entirely sure why you shouldn't check collisions along both x and y axis at the same time but I remember I had problems when doing it that way, so I only check along x and y, separately.

I may edit this with a bit of pseudocode later.

Here is some old code I wrote a few months back. I cleaned it a little bit and documented it so it would be easier to understand. It was working properly if I recall correctly.

It's in C# but it should be very easy to understand for you since Java's syntax is almost exactly the same.

public bool resolveCollisions() {
    if (xa == 0 && ya == 0) return false;
    if (xa != 0 && ya != 0) throw new ArgumentException("Can only resolve collisions along one axis at a time");

    if (xa != 0) { //horizontal movement
        if (xa < 0) { //movement towards the left
            for (int i = 0; i > xa; i--) {
                for (int j = OffsetY; j < OffsetY + Height; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2(Location.X + OffsetX + i, Location.Y + j) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.X = i + 1;
                        return true; //COLLISION!
                    }
                }
            }
        } else if (xa > 0) { //movement towards the right
            for (int i = 0; i < xa; i++) {
                for (int j = OffsetY; j < OffsetY + Height; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2(Location.X + OffsetX + Width + i, Location.Y + j) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.X = i;
                        return true; //COLLISION!
                    }
                }
            }
        }
        return false;
    } else if (ya != 0) { //vertical movement
        if (ya < 0) { //up movement
            for (int i = 0; i > ya; i--) {
                for (int j = OffsetX; j < OffsetX + Width; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2(Location.X + j, Location.Y + OffsetY + i) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.Y = i + 1;
                        return true; //COLLISION!
                    }
                }
            }
            InAir = true; //A boolean which indicts that the entity is not on ground!
        } else if (ya > 0) { //down movement
            for (int i = 0; i < ya; i++) {
                for (int j = OffsetX; j < OffsetX + Width; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
                    Vector2 cVec = new Vector2((int) (Location.X + j), (int) (Location.Y + OffsetY + Height + i)) / Tile.Size;
                    Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
                    if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
                        Velocity.Y = i;
                        if (InAir) //Is the player in the airs?
                            OnLand(); //Entity is landing, make sure you set InAir to false inside this method
                        return true; //COLLISION!
                    }
                }
            }
        }
        return false;
    } else {
        //sanity check
        throw new ArgumentException("Can only resolve collisions along one axis at a time");
    }
}

Plug that method into your entity class and then these two calls into your update method

resolveCollisions(velocity.x, 0);
resolveCollisions(0, velocity.y);

The code loops complicated and very math/logic intensive but it really isn't and is fairly straightforward once you get the meaning behind it. The same logic is repeated 4 times essentially. (4 different directions)

A few clarifications

  • This code was written for a platformer, with gravity. But it can easily be edited to fit the needs of a top-down view game.

  • OffsetX and OffsetY are variables to help define a better hitbox in conjunction of the Width and Height variables

  • The tile classes should have a method OnCollide with a parameter of type Entity that will return true if this tile should block passage or false if not. You can also add special handling like damaging the entity if it's spikes or whatever.

  • I'm not entirely sure why you shouldn't check collisions along both x and y axis at the same time but I remember I had problems when doing it that way, so I only check along x and y, separately.

added 119 characters in body
Source Link
oxysoft
  • 713
  • 6
  • 15

I haven't read into your code too much but the collision logic appears to be flawed. Imagine a projectile moving at over 200 pixels every updates. The projectile will just fly right through everything.

In order to fix this problem, I recommend looping in every directions from 0 to the amount of pixels to move. On top of that, loop from 0 to HEIGHT_OF_ENTITY for horizontal movement and WIDTH_OF_ENTITY for vertical movement. This will ensure correctly working collisions in any scenario. 

I may edit this with a bit of pseudocode later.

I haven't read into your code too much but the collision logic appears to be flawed. Imagine a projectile moving at over 200 pixels every updates. The projectile will just fly right through everything.

In order to fix this problem, I recommend looping in every directions from 0 to the amount of pixels to move. This will ensure correctly working collisions in any scenario. I may edit this with a bit of pseudocode later.

I haven't read into your code too much but the collision logic appears to be flawed. Imagine a projectile moving at over 200 pixels every updates. The projectile will just fly right through everything.

In order to fix this problem, I recommend looping in every directions from 0 to the amount of pixels to move. On top of that, loop from 0 to HEIGHT_OF_ENTITY for horizontal movement and WIDTH_OF_ENTITY for vertical movement. This will ensure correctly working collisions in any scenario. 

I may edit this with a bit of pseudocode later.

Source Link
oxysoft
  • 713
  • 6
  • 15
Loading