LibGDX garde la caméra dans les limites de TiledMap

9

J'ai un simple TiledMap que je peux rendre très bien. J'ai un joueur qui sautille (avec Box2D) et ma caméra suit le joueur:

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

Cependant, la caméra se déplacera «hors» du TiledMap. Comment pourrais-je garder ma caméra sur le TiledMap, donc quand je m'approche des bords de ma carte, la caméra cesse de défiler et le joueur se déplace vers le bord de la caméra?

Ariejan
la source

Réponses:

12

Très bien, donc vous travaillez avec deux rectangles ici. Un plus grand statique (la carte) et un plus petit en mouvement (la caméra) à l'intérieur. Ce que vous voulez, c'est de ne pas laisser les limites du petit rectangle se déplacer en dehors des limites intérieures du grand rectangle.

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

La logique est donc assez simple. Gardez la petite boîte à l'intérieur de la plus grande boîte. Une fois que vous avez compris cette idée, n'hésitez pas à réduire ce code. Vous pouvez même le déplacer dans une série d'instructions Min / Max imbriquées dans le suivi de position de votre caméra si vous préférez.

Matt Sams
la source
1
Je devrais me coucher plus tôt. J'ai eu quelque chose comme ça implémenté, mais je n'ai pas pu le faire fonctionner. Mais au lieu d'appeler une belle setPosition()méthode, qui fixerait les limites, je réglais toujours la position de la came directement (par exemple cam.position.set()) . Cela fonctionne comme un charme! Merci pour l'explication!
Ariejan
7

Vous pouvez facilement fixer la position de la caméra aux limites de la carte comme ceci:

camera.position.x = MathUtils.clamp(camera.position.x, camViewportHalfX, mapWidth - camViewportHalfX);
camera.position.y = MathUtils.clamp(camera.position.y, camViewportHalfY, mapHeight - camViewportHalfY);
Matthias
la source
Qu'est-ce que camViewportHalfXet camViewportHalfY?
Cypher
@Cypher: c'est la moitié de la taille des axes X et Y de la fenêtre.
Matthias
Alors , camViewportHalfXserait l'équivalent camera.viewportWidth / 2?
Cypher
0

Pour se déplacer Cameradans les TiledMaplimites, a OrthogonalTiledMapRendererété utilisé.

J'ai également remarqué qu'il se comporte de manière inattendue: alors qu'il Cameraatteint les limites de la carte, la carte en mosaïque, comme par inertie, déplace trop de pixels (cela dépend de la vitesse du balayage).

Comme solution , à chaque Cameramouvement, Cameraon place de force des limites de carte. Voir putInMapBounds()méthode.

Pour éviter les pépins TiledMap rendu, utilisé Math.min(float, float).

Utilisez cet écouteur pour gérer votre 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;
    }
}
Gramme
la source
0

Si vous avez le facteur de zoom à prendre en charge, j'ai trouvé que cela fonctionnait beaucoup mieux:

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
la source