Estou trabalhando em um clone do Mario baseado em blocos.
Isso funciona muito bem ao caminhar e cair. Mas quando o jogador pula perto de uma parede e caminha para a direita no ar, ele fica preso na parede. O sprite do jogador cai novamente quando o jogador libera a chave.
A instalação é bem fácil e não consigo encontrar o problema. O mapa é construído como uma matriz 2D com blocos de mapa. Um bloco pode ser sólido ou não. O jogador não pode mover objetos sólidos, duh ..
No ciclo do jogo:
- a localização do jogador é atualizada (gravidade, movimento etc.)
- verifique o mapa em colisões. Quando uma colisão é encontrada no Y, a localização do jogador é atualizada, ficando acima ou abaixo do bloco (dependendo da direção do jogador), a caixa de colisão é atualizada com a nova localização. Em seguida, o mesmo processo para o X.
- A caixa de colisão é atualizada para o novo local (um local gratuito). A caixa é ajustada para que seja um pouco mais alto colocar o bloco abaixo do jogador para verificar se ele caiu. Isso é para mudar o estado do jogador, de sprite voador para ect ocioso.
Também tentei mudar a verificação X e Y para que o jogador fosse movido na linha X. Então, quando o jogador se move, o movimento fica muito lento. Quando pressiono e solto o botão para mover, o jogador se move mais rápido, mas em pontos. Muito trippy ..
Alguém vê o erro ou pode me dar um algoritmo de colisão melhor para isso?
UPDATE (não atualizou o código)
Troquei o método de verificação xey e implementei a variável isonland. Então, quando andar e pular contra paredes funciona perfeitamente. Só agora, quando o jogador pula, Mario volta atrás quando pousa. Isso porque o método X check vai primeiro e ajusta a posição de Mario.
Como posso resolver isso?
Método de atualização da classe de mapa:
public void update(int timeElapsed) {
//update entities
for(Entity entity : _mapEntities) {
entity.update(timeElapsed);
}
//update objects
for(MapObject mapObt : _mapObjects) {
mapObt.update(timeElapsed);
}
//check for collisions
checkMapCollision();
}
Método de atualização da entidade (abstrato):
public void update(int timeElapsed) {
_velocity = new Vector2d(0.0F, 0.0F);
//add gravity
_velocity.y = Map._GRAVITY_PER_SEC * timeElapsed;
}
Mario (estende a entidade) atualizar methos:
@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);
}
setPosition((int)(getX() + getVelocity().x), (int)(getY() + getVelocity().y));
}
Classe de mapa método CheckMapCollision:
public void checkMapCollision() {
//enteties move so check it
for(Entity entity : _mapEntities) {
//get the corners
Rectangle bounds = entity.getBounds();
Block[] corners = getCornerBlocks(bounds);
Vector2d dir = entity.getDirection();
//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);
}
} else {
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);
}
}
bounds = entity.getBounds();
corners = getCornerBlocks(bounds);
//moving to the right
if(dir.x > 0) {
if(corners[1].isSolid() || corners[3].isSolid()) {
Rectangle blkBounds;
if(corners[1].isSolid()) {
blkBounds = corners[1].getBounds();
} else {
blkBounds = corners[3].getBounds();
}
entity.setPositionX(blkBounds.x - (bounds.width-entity.getCurrentSprite().getOffsetX())-1);
}
} else {
if(corners[0].isSolid() || corners[2].isSolid()) {
Rectangle blkBounds;
if(corners[0].isSolid()) {
blkBounds = corners[0].getBounds();
} else {
blkBounds = corners[2].getBounds();
}
entity.setPositionX(blkBounds.x + blkBounds.width + (bounds.width/2));
}
}
bounds = new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height+1);
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.landed();
System.out.println("landed");
}
}
}
}
entity.setPositionX()
ou entity.setPositionY()
é chamado após a verificação de colisão. Quando ando contra a parede sem pular, o jogador é empurrado de volta corretamente.