LibGDX mantiene la cámara dentro de los límites de TiledMap

9

Tengo un TiledMap simple que puedo procesar bien. Tengo un jugador saltando (con Box2D) y mi cámara sigue al jugador:

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

Sin embargo, la cámara se moverá "fuera" del TiledMap. ¿Cómo podría mantener mi cámara en el TiledMap, de modo que cuando me acerco a los bordes de mi mapa, la cámara deja de desplazarse y el jugador se mueve hacia el borde de la cámara?

Ariejan
fuente

Respuestas:

12

Muy bien, entonces estás trabajando con dos rectángulos aquí. Una estática más grande (el mapa) y una más pequeña en movimiento (la cámara) dentro de ella. Lo que desea es no dejar que los límites del rectángulo más pequeño se muevan fuera de los límites internos del rectángulo más grande.

// 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;
}

Entonces la lógica es bastante simple. Mantenga la caja pequeña dentro de la caja más grande. Una vez que comprenda esa idea, siéntase libre de colapsar ese código. Incluso podría moverlo a una serie de declaraciones Min / Max anidadas en el seguimiento de posición de su cámara si lo prefiere.

Matt Sams
fuente
1
Debería acostarme antes. Tenía algo como esto implementado, pero no pude hacerlo funcionar. Pero en lugar de llamar a un buen setPosition()método, eso arreglaría los límites, todavía estaba configurando la posición de la leva directamente (por ejemplo cam.position.set()) ¡Esto funciona de maravilla! ¡Gracias por la explicación!
Ariejan
7

Puede sujetar fácilmente la posición de la cámara a los límites del mapa de esta manera:

camera.position.x = MathUtils.clamp(camera.position.x, camViewportHalfX, mapWidth - camViewportHalfX);
camera.position.y = MathUtils.clamp(camera.position.y, camViewportHalfY, mapHeight - camViewportHalfY);
Matías
fuente
¿Qué es camViewportHalfXy camViewportHalfY?
Cypher
@Cypher: tiene la mitad del tamaño de los ejes X e Y de la ventana gráfica.
Matthias
Entonces camViewportHalfX, ¿ sería el equivalente a camera.viewportWidth / 2?
Cypher
0

Para moverse Cameraen los TiledMaplímites, OrthogonalTiledMapRendererse utilizó.

También he notado que se comporta inesperadamente: mientras Cameraalcanza los límites del mapa, el mapa en mosaico, como por inercia, mueve algunos píxeles demasiado (depende de la velocidad del deslizamiento).

Como solución , en cada Cameramovimiento, Camerase pone a la fuerza en los límites del mapa. Ver putInMapBounds()método

Para evitar fallas en el TiledMaprenderizado, se utiliza Math.min(float, float).

Use este oyente para manejar su 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;
    }
}
Gramo
fuente
0

Si tiene que cuidar el factor de zoom, descubrí que esto funciona mucho mejor:

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
fuente