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");
}
}
}
}
fonte
entity.setPositionX()
ouentity.setPositionY()
é chamado após a verificação de colisão. Quando ando contra a parede sem pular, o jogador é empurrado de volta corretamente.Respostas:
Seu personagem sprite fica preso nas interseções entre duas peças. Veja o diagrama a seguir, que descreve a situação no início de sua rotina de colisão.
Mario foi movido para a direita devido aos controles do jogador. Isso colocará o sprite nas três peças. Então, você realiza a detecção de colisão em Y descendente. Observe o ponto no círculo azul: isso detectará uma colisão com o segundo bloco e tentará resolvê-lo, definindo a coordenada Y no limite superior desse bloco, suspendendo efetivamente Mario em no ar.
Assim que o jogador soltar a chave para mover Mario para a direita, o sprite não se moverá contra a parede e o ponto que anteriormente detectaria uma colisão não registrará um acerto. Isso permitirá que Mario caia no chão.
A solução rápida e suja para esse problema é resolver as colisões de X primeiro e aplicar a gravidade apenas quando o jogador não estiver no chão. Se você mantivesse a gravidade, o mesmo problema ocorreria ao andar por aí (esse é o comportamento 'trippy' que você já viu depois de trocar a verificação X e Y;)).
fonte
Eu descobri o que estava fazendo de errado.
A chave está quando você atualiza a posição. O que fiz de errado foi que fiz o método 'mudar de posição primeiro e depois verificar e ajustar', e tive que fazer isso na ordem, verificar e atualizar.
Meu código anterior fez o seguinte:
Isso resultou em que, depois que o jogador pulou, a localização de Mario foi alterada no método de atualização para que a caixa de colisão ficasse nos ladrilhos no chão. Em seguida, o método de colisão alterava a posição X para x no canto inferior esquerdo e então o movia para cima do bloco do solo. Isso significa que Mario volta alguns pixels toda vez que o jogador aterrissa de um salto.
Eu tentei trocar os métodos de verificação X e Y, mas o problema muda para outra coisa (o abraço na parede).
Agora a solução, eu mudei para o seguinte:
Agora, o X é verificado / alterado primeiro e, em seguida, a caixa de colisão é atualizada com o novo local da entidade. Então o Y é verificado / alterado.
Isso funciona como um encanto :)
fonte