LibGDX giữ máy ảnh trong giới hạn của TiledMap


9

Tôi có một TiledMap đơn giản mà tôi có thể kết xuất tốt. Tôi có một người chơi nhảy (với Box2D) xung quanh và máy ảnh của tôi theo dõi người chơi xung quanh:

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

Tuy nhiên, máy ảnh sẽ di chuyển "tắt" TiledMap. Làm cách nào tôi có thể giữ máy ảnh của mình trên TiledMap, vì vậy khi tôi ở gần các cạnh của bản đồ, máy ảnh sẽ ngừng cuộn và người chơi di chuyển về phía rìa của máy ảnh?

Câu trả lời:


12

Được rồi, vì vậy bạn đang làm việc với hai hình chữ nhật ở đây. Một tĩnh lớn hơn (bản đồ) và một di chuyển nhỏ hơn (máy ảnh) bên trong nó. Những gì bạn muốn là không để giới hạn của hình chữ nhật nhỏ hơn di chuyển bên ngoài giới hạn bên trong của hình chữ nhật lớn hơn.

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

Vì vậy logic khá đơn giản. Giữ hộp nhỏ bên trong hộp lớn hơn. Một khi bạn hiểu ý tưởng đó, hãy thoải mái thu gọn mã đó xuống. Bạn thậm chí có thể chuyển nó thành một loạt các câu lệnh Min / Max lồng nhau trong theo dõi vị trí máy ảnh của bạn nếu bạn muốn.


1
Tôi nên đi ngủ sớm hơn. Tôi đã có một cái gì đó như thế này được thực hiện, nhưng không thể làm cho nó hoạt động. Nhưng thay vì gọi một setPosition()phương thức hay, điều đó sẽ khắc phục giới hạn, tôi vẫn trực tiếp đặt vị trí cam (ví dụ cam.position.set()) Điều này hoạt động như một bùa mê! Cảm ơn đã giải thích!
Ariejan

7

Bạn có thể dễ dàng kẹp vị trí camera vào ranh giới bản đồ như thế này:

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

Là gì camViewportHalfXcamViewportHalfY?
Cypher

@Cypher: Đó là một nửa kích thước của trục X và Y của khung nhìn.
Matthias

Vậy camViewportHalfXsẽ tương đương với camera.viewportWidth / 2?
Cypher

0

Để di chuyển Cameratrong TiledMapgiới hạn, OrthogonalTiledMapRendererđã được sử dụng.

Tôi cũng đã nhận thấy nó hoạt động bất ngờ: trong khi Camerađạt đến giới hạn bản đồ, bản đồ được lát gạch, giống như theo quán tính, di chuyển một số pixel quá xa (nó phụ thuộc vào tốc độ của thao tác vuốt).

Như một giải pháp , trên mỗi Camerachuyển động, Camerabuộc phải đưa vào giới hạn bản đồ. Xem putInMapBounds()phương pháp.

Để tránh sự cố trong TiledMapkết xuất, sử dụng Math.min(float, float).

Sử dụng trình nghe này để xử lý 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

Nếu bạn có yếu tố thu phóng cần quan tâm, tôi thấy điều này hoạt động tốt hơn nhiều:

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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.