LibGDX mantém a câmera dentro dos limites do TiledMap

9

Eu tenho um TiledMap simples que posso renderizar bem. Eu tenho um jogador pulando (com Box2D) e minha câmera segue o jogador:

cam.position.set(
    player.position().x * Game.PPM + Game.V_WIDTH / 4,
    player.position().y * Game.PPM,
    0
);
cam.update();

No entanto, a câmera "desliga" o TiledMap. Como posso manter minha câmera no TiledMap, para que, quando me aproximo das bordas do mapa, a câmera pare de rolar e o player se mova em direção à borda da câmera?

Ariejan
fonte

Respostas:

12

Tudo bem, então você está trabalhando com dois retângulos aqui. Um estático maior (o mapa) e outro menor (a câmera) dentro dele. O que você deseja é não deixar que os limites do retângulo menor se movam para fora dos limites internos do retângulo maior.

// These values likely need to be scaled according to your world coordinates.
// The left boundary of the map (x)
int mapLeft = 0;
// The right boundary of the map (x + width)
int mapRight = 0 + map.getWidth();
// The bottom boundary of the map (y)
int mapBottom = 0;
// The top boundary of the map (y + height)
int mapTop = 0 + map.getHeight();
// The camera dimensions, halved
float cameraHalfWidth = cam.viewportWidth * .5f;
float cameraHalfHeight = cam.viewportHeight * .5f;

// Move camera after player as normal

float cameraLeft = cam.position.x - cameraHalfWidth;
float cameraRight = cam.position.x + cameraHalfWidth;
float cameraBottom = cam.position.y - cameraHalfHeight;
float cameraTop = cam.position.y + cameraHalfHeight;

// Horizontal axis
if(map.getWidth() < cam.viewportWidth)
{
    cam.position.x = mapRight / 2;
}
else if(cameraLeft <= mapLeft)
{
    cam.position.x = mapLeft + cameraHalfWidth;
}
else if(cameraRight >= mapRight)
{
    cam.position.x = mapRight - cameraHalfWidth;
}

// Vertical axis
if(map.getHeight() < cam.viewportHeight)
{
    cam.position.y = mapTop / 2;
}
else if(cameraBottom <= mapBottom)
{
    cam.position.y = mapBottom + cameraHalfHeight;
}
else if(cameraTop >= mapTop)
{
    cam.position.y = mapTop - cameraHalfHeight;
}

Então a lógica é bem simples. Mantenha a caixa pequena dentro da caixa maior. Depois de entender essa ideia, sinta-se à vontade para recolher esse código. Você pode até movê-lo para uma série de instruções Mín / Máx. Aninhadas no rastreamento de posição da câmera, se preferir.

Matt Sams
fonte
11
Eu deveria ir para a cama mais cedo. Eu tinha algo assim implementado, mas não consegui fazê-lo funcionar. Mas, em vez de chamar um setPosition()método legal , que corrigisse os limites, eu ainda estava definindo a posição da câmera diretamente (por exemplo cam.position.set()) Isso funciona como um encanto! Obrigada pelo esclarecimento!
Ariejan
7

Você pode fixar facilmente a posição da câmera nos limites do mapa como este:

camera.position.x = MathUtils.clamp(camera.position.x, camViewportHalfX, mapWidth - camViewportHalfX);
camera.position.y = MathUtils.clamp(camera.position.y, camViewportHalfY, mapHeight - camViewportHalfY);
Matthias
fonte
O que é camViewportHalfXe camViewportHalfY?
Cypher
@ Code: É metade do tamanho dos eixos X e Y da janela de visualização.
Matthias
Então camViewportHalfXseria o equivalente a camera.viewportWidth / 2?
precisa
0

Para se mover Cameradentro dos TiledMaplimites, OrthogonalTiledMapRendererfoi utilizado.

Também notei que ele se comporta de maneira inesperada: enquanto Cameraatinge os limites do mapa, o mapa lado a lado, como por inércia, move alguns pixels demais (depende da velocidade do golpe).

Como solução , em cada Cameramovimento, Cameraé colocado à força nos limites do mapa. Veja o putInMapBounds()método

Para evitar falhas na TiledMaprenderização, usado Math.min(float, float).

Use este ouvinte para lidar com Camera:

/**
 * @author Gram <[email protected]>
 */
public class CameraListener extends InputAdapter {

    private final UIStage stage;
    private final Camera camera;
    private final Vector3 curr;
    private final Vector3 last;
    private final Vector3 delta;
    private final int mapWidth;
    private final int mapHeight;

    public CameraListener(UIStage stage) {
        this.stage = stage;
        this.camera = stage.getViewport().getCamera();

        curr = new Vector3();
        last = new Vector3(-1, -1, -1);
        delta = new Vector3();

        TiledMapTileLayer layer = stage.getLevel().getMap().getFirstLayer();
        mapWidth = layer.getWidth() * DDGame.TILE_HEIGHT;
        mapHeight = layer.getHeight() * DDGame.TILE_HEIGHT;
    }

    @Override
    public boolean touchDragged(int x, int y, int pointer) {

        camera.unproject(curr.set(x, y, 0));

        if (!(last.x == -1 && last.y == -1 && last.z == -1)) {
            camera.unproject(delta.set(last.x, last.y, 0));
            delta.sub(curr);
            camera.translate(Math.min(delta.x, 5), Math.min(delta.y, 5), 0);
            if (isInMapBounds()) {
                stage.moveBy(Math.min(delta.x, 5), Math.min(delta.y, 5));
            }
        }

        last.set(x, y, 0);

        putInMapBounds();

        return false;
    }


    private boolean isInMapBounds() {

        return camera.position.x >= camera.viewportWidth / 2f
                && camera.position.x <= mapWidth - camera.viewportWidth / 2f
                && camera.position.y >= camera.viewportHeight / 2f
                && camera.position.y <= mapHeight - camera.viewportHeight / 2f;

    }

    private void putInMapBounds() {

        if (camera.position.x < camera.viewportWidth / 2f)
            camera.position.x = camera.viewportWidth / 2f;
        else if (camera.position.x > mapWidth - camera.viewportWidth / 2f)
            camera.position.x = mapWidth - camera.viewportWidth / 2f;

        if (camera.position.y < camera.viewportHeight / 2f)
            camera.position.y = camera.viewportHeight / 2f;
        else if (camera.position.y > mapHeight - camera.viewportHeight / 2f)
            camera.position.y = mapHeight - camera.viewportHeight / 2f;

        stage.moveTo(
                camera.position.x,
                camera.position.y);

    }

    @Override
    public boolean touchUp(int x, int y, int pointer, int button) {
        last.set(-1, -1, -1);
        Log.info("Camera at " + camera.position.x + ":" + camera.position.y);
        return false;
    }
}
Grama
fonte
0

Se você tem o fator de zoom para cuidar, achei que funcionaria muito melhor:

public void fixBounds() {
    float scaledViewportWidthHalfExtent = viewportWidth * zoom * 0.5f;
    float scaledViewportHeightHalfExtent = viewportHeight * zoom * 0.5f;

    // Horizontal
    if (position.x < scaledViewportWidthHalfExtent)
        position.x = scaledViewportWidthHalfExtent;
    else if (position.x > xmax - scaledViewportWidthHalfExtent)
        position.x = xmax - scaledViewportWidthHalfExtent;

    // Vertical
    if (position.y < scaledViewportHeightHalfExtent)
        position.y = scaledViewportHeightHalfExtent;
    else if (position.y > ymax - scaledViewportHeightHalfExtent)
        position.y = ymax - scaledViewportHeightHalfExtent;
}
Rowland Mtetezi
fonte