LibGDX menjaga kamera dalam batas TiledMap

9

Saya punya TiledMap sederhana yang bisa saya render dengan baik. Saya memiliki pemain yang melompat-lompat (dengan Box2D) di sekitar dan kamera saya mengikuti pemain di sekitar:

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

Namun, kamera akan memindahkan "dari" TiledMap. Bagaimana saya bisa menyimpan kamera saya di TiledMap, jadi ketika saya berada di dekat tepi peta saya, kamera berhenti bergulir dan pemain bergerak ke arah tepi kamera?

Ariejan
sumber

Jawaban:

12

Baiklah, jadi Anda bekerja dengan dua persegi panjang di sini. Yang statis lebih besar (peta) dan yang lebih kecil bergerak (kamera) di dalamnya. Yang Anda inginkan adalah untuk tidak membiarkan batas-batas persegi panjang yang lebih kecil bergerak di luar batas-batas dalam persegi panjang yang lebih besar.

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

Jadi logikanya cukup sederhana. Simpan kotak kecil di dalam kotak yang lebih besar. Setelah Anda memahami gagasan itu, jangan ragu untuk menciutkan kode itu. Anda bahkan bisa memindahkannya ke serangkaian pernyataan Min / Max bersarang di pelacakan posisi kamera Anda jika Anda mau.

Matt Sams
sumber
1
Saya harus tidur lebih awal. Saya memiliki sesuatu seperti ini diterapkan, tetapi tidak bisa membuatnya bekerja. Tetapi alih-alih memanggil setPosition()metode yang bagus , yang akan memperbaiki batasan, saya masih mengatur posisi cam secara langsung (mis. cam.position.set()) Ini berfungsi seperti pesona! Terima kasih untuk penjelasannya!
Ariejan
7

Anda dapat dengan mudah menjepit posisi kamera ke batas peta seperti ini:

camera.position.x = MathUtils.clamp(camera.position.x, camViewportHalfX, mapWidth - camViewportHalfX);
camera.position.y = MathUtils.clamp(camera.position.y, camViewportHalfY, mapHeight - camViewportHalfY);
Matthias
sumber
Apa camViewportHalfXdan camViewportHalfY?
Cypher
@Cypher: Ini setengah ukuran sumbu viewport X dan Y.
Matthias
Jadi camViewportHalfXakan sama dengan camera.viewportWidth / 2?
Cypher
0

Untuk bergerak Cameradalam TiledMapbatas, OrthogonalTiledMapRendererdigunakan.

Saya juga memperhatikan bahwa ia berperilaku tak terduga: saat Cameramencapai batas peta, peta ubin, seperti dengan inersia, memindahkan beberapa piksel terlalu jauh (tergantung pada kecepatan gesek).

Sebagai solusi , pada setiap Cameragerakan, Camerasecara paksa dimasukkan ke dalam batas peta. Lihat putInMapBounds()metode.

Untuk menghindari gangguan dalam TiledMaprendering, digunakan Math.min(float, float).

Gunakan pendengar ini untuk menangani 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;
    }
}
Gram
sumber
0

Jika Anda memiliki faktor zoom untuk diurus, saya menemukan ini berfungsi lebih baik:

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
sumber