Exemplo do Microsoft XNA Platformer, a Detecção de Collsion é implementada com precisão?

11

O exemplo fornecido pela Microsoft parece que a detecção de colisão (pelo que posso ver) terá um pequeno erro. Quando o usuário colide com um bloco Impossível, a profundidade da interseção é calculada. O menor dos valores de profundidade X e Y é usado para fixar a posição do usuário para que ele não colide mais com o bloco. Mas se o usuário estivesse viajando na diagonal, isso poderia resultar no final do ponto exatamente no ponto em que o personagem colidiria primeiro com o bloco?

Provavelmente estou errado, mas é assim que eu vejo.

   private void HandleCollisions()
        {
            // Get the player's bounding rectangle and find neighboring tiles.
            Rectangle bounds = BoundingRectangle;
            int leftTile = (int)Math.Floor((float)bounds.Left / Tile.Width);
            int rightTile = (int)Math.Ceiling(((float)bounds.Right / Tile.Width)) - 1;
            int topTile = (int)Math.Floor((float)bounds.Top / Tile.Height);
            int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / Tile.Height)) - 1;

            // Reset flag to search for ground collision.
            isOnGround = false;

            // For each potentially colliding tile,
            for (int y = topTile; y <= bottomTile; ++y)
            {
                for (int x = leftTile; x <= rightTile; ++x)
                {
                    // If this tile is collidable,
                    TileCollision collision = Level.GetCollision(x, y);
                    if (collision != TileCollision.Passable)
                    {
                        // Determine collision depth (with direction) and magnitude.
                        Rectangle tileBounds = Level.GetBounds(x, y);
                        Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds);
                        if (depth != Vector2.Zero)
                        {
                            float absDepthX = Math.Abs(depth.X);
                            float absDepthY = Math.Abs(depth.Y);

                            // Resolve the collision along the shallow axis.
                            if (absDepthY < absDepthX || collision == TileCollision.Platform)
                            {
                                // If we crossed the top of a tile, we are on the ground.
                                if (previousBottom <= tileBounds.Top)
                                    isOnGround = true;

                                // Ignore platforms, unless we are on the ground.
                                if (collision == TileCollision.Impassable || IsOnGround)
                                {
                                    // Resolve the collision along the Y axis.
                                    Position = new Vector2(Position.X, Position.Y + depth.Y);

                                    // Perform further collisions with the new bounds.
                                    bounds = BoundingRectangle;
                                }
                            }
                            else if (collision == TileCollision.Impassable) // Ignore platforms.
                            {
                                // Resolve the collision along the X axis.
                                Position = new Vector2(Position.X + depth.X, Position.Y);

                                // Perform further collisions with the new bounds.
                                bounds = BoundingRectangle;
                            }
                        }
                    }
                }
            }

            // Save the new bounds bottom.
            previousBottom = bounds.Bottom;
        }
PriestVallon
fonte
3
Por que o menos 1, pessoas? A pergunta é válida para mim. Mas aqui está uma resposta curta: a demo de plataforma que acompanha o XNA é apenas um exemplo. Não deve ser seguido estritamente como modelo para seus jogos. É para mostrar que um jogo pode ser feito. Você não deve se preocupar se a implementação não é a melhor.
Gustavo Maciel
Obrigado, apenas assumi com o exemplo que o que eles haviam feito era a melhor maneira de fazer e que estava faltando alguma coisa. Obrigado por esclarecer isso para mim.
PriestVallon

Respostas:

12

Você está absolutamente certo . Eu tive minha parcela de problemas com as rotinas de colisão na amostra de plataformas XNA. Mas consegui começar com o código, conforme fornecido na amostra, e modifiquei-o um pouco até obter resultados consistentes em todos os cenários de teste que pude lançar nele.

Em particular, o tipo de problema que eu estava tendo era tentar deslizar ao longo de uma parede movendo-se na diagonal contra ela. Devido à suposição que a amostra faz para resolver colisões com base no menor eixo de deslocamento, isso fez com que o personagem não pudesse se mover ao empurrar contra uma parede em alguma direção. Por exemplo, usando um sinal, eu ficava preso ao abraçar o teto e tentar me mover da esquerda para a direita (não consigo me lembrar dos detalhes). Mudar o sinal resolveria essa situação, mas um problema apareceria no cenário oposto. O ponto principal é que, com a implementação fornecida, eu não conseguia fazê-lo funcionar corretamente em todos os lados e em todas as direções - sempre falhava em pelo menos um caso.

Portanto, o núcleo das mudanças que eu fiz foi começar a lidar com o movimento no eixo X, independentemente do movimento no eixo Y, em duas etapas separadas. Eu escrevi sobre isso antes nesta resposta, então vá para os detalhes.

E se bem me lembro, o motivo real era algo como isto:

insira a descrição da imagem aqui

David Gouveia
fonte
1
David sempre usando o XNA!
Gustavo Maciel
1
@ Gustavo-Gtoknu eu meio que senti que ainda precisava de um desenho do problema: P
David Gouveia
1
Acabei de encontrar esta resposta - ótimo trabalho! Obrigado David.
Austin Brunkhorst
1

Quando você tiver várias colisões, se as retificar da mais próxima para a mais distante do centro de cada retângulo envolvido, você não terá o problema de "travar".

1) Encontre todos os retângulos em colisão

2) Se houver mais de um (dependendo do seu caso de uso, isso pode ser frequente ou pouco frequente), encontre o mais próximo.

3) Resolva colisões uma de cada vez e verifique se as outras ainda são colisões válidas

Na resposta aceita, a lógica de colisão e entrada é enlameada; possui verificações para determinar o rumo, etc. A implementação da maneira descrita mantém a lógica de colisão separada da lógica de entrada, ao custo de calcular a distância, quando necessário.

acordeão
fonte