I'm creating a side scroller and I'm have a problem with my collision detection.
The collision detection works perfect when the player is falling at a constant speed, but when I implement more realistic gravity the player falls too fast - resulting in that the collision will be checked with the tiles under the floor/platform causing the player to be able to jump through platforms.
The way my update and collision works is like following:
- Update entities velocity (no position change)
- Check for collisions (first x, then y)
- if collision, then move the entity as close as possible, otherwise set the position with the velocity.
The collision method works like this:
- Get the four corners of the player.
- Get from that four corners the tiles.
- Collision check.
How can I solve this problem?
UPDATE
Added the code to make it more clear.
The main loop:
_frames_a_sec = 60;
public void run() {
while(true) {
long timeElapsed = System.currentTimeMillis() - _lastUpdate;
_lastUpdate = System.currentTimeMillis();
if(timeElapsed > 80) {
timeElapsed = 80;
}
update((int)timeElapsed);
try {
Thread.sleep((1000/_FRAMES_A_SEC));
} catch(Exception ex) { }
}
}
Map update method:
public void update(int timeElapsed) {
//update entities
for(Entity entity : _mapEntities) {
entity.update(timeElapsed);
}
//check for collisions
checkMapCollision();
}
Entity (abstract) update method:
public void update(int timeElapsed) {
_velocity.x = 0.0F;
if(!_isOnLand) {
//add gravity
_velocity.y += Map._GRAVITY_PER_SEC * timeElapsed;
} else {
_velocity.y = 0.0F;
}
}
Mario (extends Entity) update method:
@Override
public void update(int timeElapsed) {
super.update(timeElapsed);
if(_state == STATES.IDLE) {
} else if(_isMoving) {
_marioSmallWalk.update(timeElapsed);
}
if(_state == STATES.JUMPING) {
setVelocityY(getVelocity().y + _jumpSpeed);
_jumpSpeed += _JUMP_DECREASE * timeElapsed;
//falling?
if(getVelocity().y > 0) {
setState(STATES.FALLING);
}
}
if(_isMoving) {
double walkSpd = (_WALK_SPEED_SEC * timeElapsed);
if(getFacing() == FACING.LEFT) {
walkSpd = -walkSpd;
}
setVelocityX(getVelocity().x + walkSpd);
}
//falling?
if(getVelocity().y > (Map._GRAVITY_PER_SEC * timeElapsed) + 1.0F) {
setState(STATES.FALLING);
}
}
Map Collsion check method:
public void checkMapCollision() {
for(Entity entity : _mapEntities) {
placeEntityAtX(entity);
placeEntityAtY(entity);
}
}
X check method:
private void placeEntityAtX(Entity entity) {
Vector2d dir = entity.getDirection();
Rectangle bounds = entity.getBounds();
boolean positionSet = false;
bounds = new Rectangle((int)(bounds.x + dir.x), bounds.y, bounds.width, bounds.height);
Block[] corners = getCornerBlocks(bounds);
if(dir.x > 0) {
if(corners[1].isSolid() || corners[3].isSolid()) {
Rectangle blkBounds;
if(corners[3].isSolid()) {
blkBounds = corners[3].getBounds();
} else {
blkBounds = corners[1].getBounds();
}
entity.setPositionX(blkBounds.x - (bounds.width-entity.getCurrentSprite().getOffsetX())-1);
positionSet = true;
}
} else if(dir.x < 0) {
if(corners[0].isSolid() || corners[2].isSolid()) {
Rectangle blkBounds;
if(corners[2].isSolid()) {
blkBounds = corners[2].getBounds();
} else {
blkBounds = corners[0].getBounds();
}
entity.setPositionX(blkBounds.x + blkBounds.width + (bounds.width/2) + 1);
positionSet = true;
}
}
if(!positionSet) {
//set the original position
entity.setPositionX((int)(entity.getX() + dir.x));
}
}
and the y:
private void placeEntityAtY(Entity entity) {
Vector2d dir = entity.getDirection();
Rectangle bounds = entity.getBounds();
boolean positionSet = false;
bounds = new Rectangle(bounds.x, (int)(bounds.y + dir.y), bounds.width, bounds.height);
Block[] corners = getCornerBlocks(bounds);
//moving down
if(dir.y > 0) {
if(corners[2].isSolid() || corners[3].isSolid()) {
Rectangle blkBounds = null;
if(corners[2].isSolid()) {
blkBounds = corners[2].getBounds();
} else {
blkBounds = corners[3].getBounds();
}
entity.setPositionY(blkBounds.y);
entity.landed();
positionSet = true;
}
} else if (dir.y < 0) {
if(corners[0].isSolid() || corners[1].isSolid()) {
Rectangle blkBounds = null;
if(corners[0].isSolid()) {
blkBounds = corners[0].getBounds();
} else {
blkBounds = corners[1].getBounds();
}
entity.setPositionY(blkBounds.y + blkBounds.height + bounds.height);
entity.roof();
positionSet = true;
}
} else {
if(!corners[2].isSolid() && !corners[3].isSolid()) {
entity.falling();
}
}
if(!positionSet) {
//set the original position
entity.setPositionY((int)(entity.getY() + dir.y));
}
}