LibGDX mantiene la fotocamera entro i limiti di TiledMap


9

Ho una semplice TiledMap che posso rendere bene. Ho un giocatore che salta (con Box2D) in giro e la mia videocamera segue il giocatore in giro:

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

Tuttavia, la telecamera si sposterà "off" su TiledMap. Come potrei tenere la mia fotocamera su TiledMap, quindi quando mi avvicino ai bordi della mia mappa, la fotocamera smette di scorrere e il giocatore si sposta verso il bordo della fotocamera?

Risposte:


12

Bene, quindi stai lavorando con due rettangoli qui. Una statica più grande (la mappa) e una più piccola in movimento (la telecamera) al suo interno. Quello che vuoi è non lasciare che i limiti del rettangolo più piccolo si spostino al di fuori dei limiti interni del rettangolo più 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;
}

Quindi la logica è piuttosto semplice. Conservare la scatola piccola all'interno della scatola più grande. Una volta che hai capito quell'idea sentiti libero di comprimere quel codice. Se lo preferisci, puoi anche spostarlo in una serie di istruzioni Min / Max nidificate nel rilevamento della posizione della videocamera.


1
Dovrei andare a letto prima. Avevo implementato qualcosa del genere, ma non riuscivo a farlo funzionare. Ma invece di chiamare un setPosition()metodo carino , che correggesse i limiti, stavo ancora impostando direttamente la posizione della camma (ad es. cam.position.set()) Funziona come un fascino! Grazie per la spiegazione!
Ariejan,

7

Puoi facilmente bloccare la posizione della telecamera sui confini della mappa in questo modo:

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

Che cos'è camViewportHalfXe camViewportHalfY?
Cypher,

@Cypher: è la metà delle dimensioni degli assi X e Y della finestra.
Matthias,

Quindi camViewportHalfXsarebbe l'equivalente di camera.viewportWidth / 2?
Cypher,

0

Per spostarsi Cameranei TiledMaplimiti, è OrthogonalTiledMapRendererstato utilizzato.

Ho anche notato che si comporta inaspettatamente: mentre Cameraraggiunge i limiti della mappa, la mappa piastrellata, come per inerzia, sposta alcuni pixel troppo lontano (dipende dalla velocità del colpo).

Come soluzione , su ogni Cameramovimento, Cameraviene forzatamente inserito nei limiti della mappa. Vedi putInMapBounds()metodo

Per evitare problemi tecnici nel TiledMaprendering, utilizzato Math.min(float, float).

Usa questo listener per gestire 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

Se hai il fattore di zoom di cui occuparti, ho scoperto che funziona molto meglio:

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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.