LibGDX utrzymuje kamerę w granicach TiledMap


9

Mam prostą TiledMap, którą mogę dobrze renderować. Wokoło mam gracza skaczącego (z Box2D), a moja kamera podąża za nim:

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

Jednak kamera przesunie się „poza” TiledMap. Jak mogę trzymać kamerę na TiledMap, więc kiedy zbliżam się do krawędzi mapy, kamera przestaje się przewijać, a gracz przesuwa się w kierunku krawędzi kamery?

Odpowiedzi:


12

W porządku, więc pracujesz tutaj z dwoma prostokątami. Większy statyczny (mapa) i mniejszy ruchomy (kamera) wewnątrz. Chcemy, aby granice mniejszego prostokąta nie poruszały się poza wewnętrznymi granicami większego prostokąta.

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

Logika jest więc dość prosta. Trzymaj małe pudełko wewnątrz większego pudełka. Gdy zrozumiesz ten pomysł, możesz zwinąć ten kod. Możesz nawet przenieść go do szeregu zagnieżdżonych instrukcji Min / Max w śledzeniu pozycji kamery, jeśli wolisz.


1
Powinienem iść wcześniej do łóżka. Zaimplementowałem coś takiego, ale nie mogłem tego uruchomić. Ale zamiast wywoływać fajną setPosition()metodę, która naprawiłaby granice, wciąż ustawiałem bezpośrednio pozycję kamery (np. cam.position.set()) To działa jak urok! Dziękuję za wyjaśnienie!
Ariejan

7

Możesz łatwo zablokować pozycję kamery do granic mapy w następujący sposób:

camera.position.x = MathUtils.clamp(camera.position.x, camViewportHalfX, mapWidth - camViewportHalfX);
camera.position.y = MathUtils.clamp(camera.position.y, camViewportHalfY, mapHeight - camViewportHalfY);

Co to jest camViewportHalfXi camViewportHalfY?
Cypher

@Cypher: Jest o połowę mniejszy od osi X i Y rzutni.
Matthias

Czy camViewportHalfXbyłby to odpowiednik camera.viewportWidth / 2?
Cypher

0

Do poruszania się Cameraw TiledMapgranicach OrthogonalTiledMapRendererużyto.

Zauważyłem też, że zachowuje się nieoczekiwanie: gdy Cameraosiąga granice mapy, mapa kafelkowa, podobnie jak bezwładność, przesuwa się o kilka pikseli za daleko (zależy to od prędkości przeciągnięcia).

Jako rozwiązanie , przy każdym Cameraruchu, Camerajest siłą umieszczane w granicach mapy. Zobacz putInMapBounds()metodę.

Aby uniknąć błędów w TiledMaprenderowaniu, zastosowano Math.min(float, float).

Użyj tego detektora do obsługi Camera:

/**
 * @author Gram <gram7gram@gmail.com>
 */
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;
    }
}

0

Jeśli masz do zrobienia współczynnik powiększenia, okazało się, że działa on znacznie lepiej:

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;
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.