LibGDX hält die Kamera innerhalb der Grenzen von TiledMap

9

Ich habe eine einfache TiledMap, die ich gut rendern kann. Ich habe einen Spieler (mit Box2D) herumhüpfen und meine Kamera folgt dem Spieler herum:

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

Die Kamera bewegt sich jedoch von der TiledMap "weg". Wie kann ich meine Kamera auf der TiledMap belassen? Wenn ich mich also den Rändern meiner Karte nähere, hört die Kamera auf zu scrollen und der Player bewegt sich zum Rand der Kamera?

Ariejan
quelle

Antworten:

12

Also gut, Sie arbeiten hier mit zwei Rechtecken. Eine größere statische (die Karte) und eine kleinere bewegliche (die Kamera) darin. Sie möchten, dass sich die Grenzen des kleineren Rechtecks ​​nicht außerhalb der inneren Grenzen des größeren Rechtecks ​​bewegen.

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

Die Logik ist also ziemlich einfach. Bewahren Sie die kleine Box in der größeren Box auf. Sobald Sie diese Idee verstanden haben, können Sie diesen Code reduzieren. Sie können es sogar in eine Reihe verschachtelter Min / Max-Anweisungen in Ihrer Kamerapositionsverfolgung verschieben, wenn Sie dies bevorzugen.

Matt Sams
quelle
1
Ich sollte früher ins Bett gehen. Ich hatte so etwas implementiert, konnte es aber nicht zum Laufen bringen. Aber anstatt eine nette setPosition()Methode aufzurufen , die die Grenzen festlegt, habe ich immer noch die Nockenposition direkt eingestellt (zB cam.position.set()). Das funktioniert wie ein Zauber! Danke für die Erklärung!
Ariejan
7

Sie können die Kameraposition einfach wie folgt an die Kartengrenzen klemmen:

camera.position.x = MathUtils.clamp(camera.position.x, camViewportHalfX, mapWidth - camViewportHalfX);
camera.position.y = MathUtils.clamp(camera.position.y, camViewportHalfY, mapHeight - camViewportHalfY);
Matthias
quelle
Was ist camViewportHalfXund camViewportHalfY?
Cypher
@Cypher: Es ist halb so groß wie die X- und Y-Achse des Ansichtsfensters.
Matthias
Wäre camViewportHalfXdas also gleichbedeutend mit camera.viewportWidth / 2?
Cypher
0

Um sich Camerain TiledMapGrenzen zu bewegen , OrthogonalTiledMapRendererwurde verwendet.

Mir ist auch aufgefallen, dass es sich unerwartet verhält: Während Cameradie Kartengrenzen erreicht werden, bewegt sich die gekachelte Karte wie durch Trägheit einige Pixel zu weit (dies hängt von der Geschwindigkeit des Wischens ab).

Als Lösung wird bei jeder CameraBewegung Camerazwangsweise in Kartengrenzen gesetzt. Siehe putInMapBounds()Methode.

Um Störungen beim TiledMapRendern zu vermeiden , wird verwendet Math.min(float, float).

Verwenden Sie diesen Listener, um Folgendes zu handhaben 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;
    }
}
Gramm
quelle
0

Wenn Sie den Zoomfaktor haben, um den Sie sich kümmern müssen, fand ich, dass dies viel besser funktioniert:

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
quelle