Renderizando apenas o que está na tela

6

Eu sou bastante novo neste mundo, então tenha paciência comigo.

Eu tenho uma grade de blocos em um jogo 2D de cima para baixo escrito usando Slick. A melhor maneira de pensar nisso é o mundo Pokemon.

No momento, estou renderizando todos os blocos em um mapa específico, estejam eles na tela no momento ou não.

Existe uma maneira melhor de fazer isso? Meu pensamento talvez fosse tentar criar uma imagem do que eu teria renderizado e pegar a seção que se encaixa na tela para renderizar. Não sei como fazer isso, mas queria consultar os especialistas antes de mergulhar nisso. Obrigado! ;)

EDITAR

Apenas pensei em acrescentar que o Slick tem uma maneira de lidar com o recorte em casos especializados, bem como na janela de exibição da tela. Veja a página Wiki para mais informações.

Andy
fonte
11
Sim, você deve renderizar apenas o que está na tela. Talvez um pouco de amortecedor do lado de fora também, para que você não pisque ao longo da borda se o jogador se mover rápido. Eu não sei se java / slick fornece uma maneira integrada de fazer isso. Eu imagino que eles fazem.

Respostas:

11

Uma maneira de fazer isso é verificar a caixa delimitadora do bloco contra a caixa delimitadora da exibição atual (mais um pouco por segurança).

Se o bloco interage com a vista (totalmente interna ou cortada), desenhe-o. Se não - ou seja, é totalmente fora, então não desenhe.

Essa verificação pode ser realizada rapidamente - principalmente em 2D. Você não precisa saber quanto do bloco é visível - apenas que parte dele é potencialmente visível.

Se a coordenada X máxima do bloco for menor que a coordenada X mínima da vista ou a coordenada X mínima do bloco for maior que a coordenada X máxima da vista, esse bloco será definitivamente invisível. O mesmo teste pode ser feito na coordenada Y.

Como Joe aponta no comentário, a estrutura / bibliotecas que você está usando pode ter essa funcionalidade incorporada.

ChrisF
fonte
2

Faça algo parecido com isto:

if (renderX >= 0 && renderY >= 0
    && renderX <= container.getWidth() && renderY <= container.getWidth()) {
    //Render
}

Ele precisará ser personalizado para cada objeto, pois cada objeto tem um tamanho diferente. Eu renderizo cada grupo de componentes separadamente. Se você estiver usando o TiLeD, aqui está um bom código que eu criei:

TiledMap map = mapComp.getMap();

    //Render positions as integers
    int renderX = (int) renderPos.getX();
    int renderY = (int) renderPos.getY();

    //Render offset for map
    int xRenderOffset = renderX % 32;
    int yRenderOffset = renderY % 32;

    //Get the first tile to be rendered
    int firstTileX = (int) -renderX / 32;
    int firstTileY = (int) -renderY / 32;

    //Render tiles only if their indices are greater than 0
    if (firstTileX < 0) {
        xRenderOffset += Math.abs(firstTileX) * 32;
        firstTileX = 0;
    }

    if (firstTileY < 0) {
        yRenderOffset += Math.abs(firstTileY) * 32;
        firstTileY = 0;
    }

    //Get the last tile to be rendered
    int lastTileX = firstTileX + (client.getContainer().getWidth() / 32) + 1;
    int lastTileY = firstTileY + (client.getContainer().getHeight() / 32) + 2;

    //Verify that the last tile is valid
    if (lastTileX > 127) {
        lastTileX = 127;
    }

    if (lastTileY > 127) {
        lastTileY = 127;
    }

    //Check if the map will be visible on the screen
    if (lastTileX >= 0 && lastTileY >= 0) {
        map.render(xRenderOffset, yRenderOffset, firstTileX, firstTileY, lastTileX, lastTileY, 0, true);
        map.render(xRenderOffset, yRenderOffset, firstTileX, firstTileY, lastTileX, lastTileY, 1, true);
        map.render(xRenderOffset, yRenderOffset, firstTileX, firstTileY, lastTileX, lastTileY, 2, true);
        map.render(xRenderOffset, yRenderOffset, firstTileX, firstTileY, lastTileX, lastTileY, 3, true);
    }

32 é a largura do seu bloco e 127 é o tamanho do seu mapa - 1.

Ian Macalinao
fonte
Bom pedaço de código, mas não vou usar o TiLeD neste. Decidi usar minha própria maneira de lidar com mapas dos quais estou gostando muito bem. Ele se baseia na implementação de arquivos de texto do Platformer Starter Kit lançado pela Microsoft. +1. :)
Andy
1

Se seus blocos são de tamanho uniforme (como é o caso dos jogos clássicos de Pokemon) e armazenados em uma matriz 2D, provavelmente a melhor maneira de fazer isso é fazer loops for-loops, algo como isto:

int startX = playerX - halfWidth;
int startY = playerY - halfHeight;

for( int x = startX , maxX = playerX + halfWidth; x < maxX; ++x )
{
    for( int y = startY , maxY = playerY + halfHeight; y < maxY; ++y )
    {
        DrawBlock( x - startX, y - startY, tiles[x][y] );
    }
}

onde halfWidth e halfHeight são metade da largura e altura da tela, respectivamente. Isso reduzirá as chamadas ao DrawBlock para apenas as que estão visíveis. Se você estiver rolando pela grade, poderá desenhar um bloco extra em cada direção e adicionar seus valores de deslocamento animados aos parâmetros do DrawBlock, conforme necessário.

Deve-se mencionar que esse método é complementar ao método de recorte ao qual você vinculou na sua edição. Usando o método que apresentei aqui, o recorte seria útil se seus blocos não ocupassem a tela inteira (por exemplo, existe uma borda ao redor deles para uma interface do usuário), para impedir que blocos extras sejam desenhados para fins de animação. sendo desenhado sobre outros elementos. Nesse caso, você definiria sua região de recorte como exatamente o espaço em que deseja que sua grade fosse exibida e desenharia apenas os blocos mínimos necessários.

Gnoll
fonte
boa recomendação! parece que Slick lida com essas coisas normalmente em segundo plano, portanto, não será necessário adicionar meu próprio código para lidar com o recorte.
Andy