LibGDX는 TiledMap의 경계 내에 카메라를 유지


9

잘 렌더링 할 수있는 간단한 TiledMap이 있습니다. Box2D로 도약하는 플레이어가 있고 카메라가 플레이어를 따라갑니다.

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

그러나 카메라는 TiledMap을 "끄기"이동합니다. 카메라를 TiledMap에 유지하려면 어떻게해야합니까?지도 가장자리 근처에있을 때 카메라가 스크롤을 멈추고 플레이어가 카메라 가장자리쪽으로 이동합니까?

답변:


12

자, 여기서 두 개의 직사각형으로 작업하고 있습니다. 더 큰 정적 인 것 (지도)과 더 작은 정적 인 것 (카메라). 원하는 것은 작은 사각형의 경계가 큰 사각형의 내부 경계 밖으로 이동하지 않도록하는 것입니다.

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

따라서 논리는 매우 간단합니다. 작은 상자를 큰 상자 안에 보관하십시오. 이 아이디어를 이해하면 해당 코드를 자유롭게 축소하십시오. 원하는 경우 카메라 위치 추적에서 일련의 중첩 된 Min / Max 문으로 이동할 수도 있습니다.


1
나는 일찍 자러 가야한다. 나는 이와 같은 것을 구현했지만 작동시키지 못했습니다. 그러나 좋은 setPosition()방법 을 호출하는 대신 경계를 수정하는 대신 여전히 캠 위치를 직접 설정했습니다 (예 :) cam.position.set()이것은 매력처럼 작동합니다! 설명 주셔서 감사합니다!
Ariejan

7

다음과 같이 카메라 위치를 맵 경계에 쉽게 고정 할 수 있습니다.

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

무엇 camViewportHalfXcamViewportHalfY?
Cypher

@Cypher : 뷰포트 X 및 Y 축 크기의 절반입니다.
Matthias

따라서 ?에 camViewportHalfX해당합니다 camera.viewportWidth / 2.
Cypher

0

바운드 로 이동 Camera하는 데 사용되었습니다.TiledMapOrthogonalTiledMapRenderer

또한 예기치 않은 동작을 발견했습니다. Camera지도 경계에 도달 하는 동안 관성에 따라 타일 맵이 일부 픽셀을 너무 멀리 움직입니다 (스 와이프의 속도에 따라 다름).

해결책으로 , 각 Camera움직임 에 대해 Camera강제로 맵 경계에 배치됩니다. putInMapBounds()방법을 참조하십시오 .

TiledMap렌더링에서 글리치를 피하려면을 사용하십시오 Math.min(float, float).

이 리스너를 사용하여 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

당신이 돌보는 줌 팩터가 있다면, 나는 이것이 훨씬 잘 작동한다는 것을 알았습니다.

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;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.