Храните камеру libgdx внутри границ при панорамировании и масштабировании

Я разрабатываю игру для Android с помощью LibGDX. Я добавил масштабирование и панорамирование. Моя проблема заключается в том, как избежать выхода из игровой площадки. Как бы то ни было, вы можете панорамировать вне игровой зоны в черноту. Когда я полностью вычеркнул, я знаю, как с этим бороться, я только что сказал:

if(camera.zoom == 1.0f) ;
else {

}

Но, если увеличить масштаб, как это сделать. Я знаю, что это не так сложно, я просто не могу понять. После создания я установил камеру в середину экрана. Я знаю, как кастрюлю, я использую camera.translate(-input.deltaX, -input.deltaY, 0), мне просто нужно проверить перед этим вызовом, чтобы увидеть, находится ли позиция вне игровой зоны. Когда я увеличен, как я могу проверить, находится ли я на краю экрана?

Ответ 1

Вы можете использовать один из

camera.frustum.boundsInFrustum(BoundingBox box) 
camera.frustum.pointInFrustum(Vector3 point)
camera.frustum.sphereInFrustum(Vector3 point, float radius)

чтобы проверить, находится ли точка/коробка/сфера в пределах вашей камеры. То, что я обычно делаю, - это определить 4 коробки вокруг моего мира, где игроку не разрешается видеть. Если камера перемещена и одно из ячеек находится в усеченном состоянии, я верну камеру назад в предыдущее положение.

Изменить: AAvering внедрил это в коде ниже.

Ответ 2

Здесь код, который я вызываю после положения камеры, обновляется из-за панорамирования или масштабирования в моей 2D-игре с использованием орфографической камеры. Он корректирует положение камеры, чтобы он не показывал ничего за пределами области воспроизведения.

float camX = camera.position.x;
float camY = camera.position.y;

Vector2 camMin = new Vector2(camera.viewportWidth, camera.viewportHeight);
camMin.scl(camera.zoom/2); //bring to center and scale by the zoom level
Vector2 camMax = new Vector2(borderWidth, borderHeight);
camMax.sub(camMin); //bring to center

//keep camera within borders
camX = Math.min(camMax.x, Math.max(camX, camMin.x));
camY = Math.min(camMax.y, Math.max(camY, camMin.y));

camera.position.set(camX, camY, camera.position.z);

camMin - самый низкий левый угол, который камера может не показывать ничего за пределами области воспроизведения, а также смещение от угла камеры к центру.

camMax - это самый высокий правый угол, в котором может находиться камера.

Ключевая часть, которую я предполагаю, что вам не хватает, - это масштабирование размера камеры с помощью уровня масштабирования.

Ответ 3

Кредит идет к Matsemann за идею, вот реализация, которую я использовал.

Создайте собственный класс MyCamera, расширяющий OrthographicCamera, и добавьте следующий код:

BoundingBox left, right, top, bottom = null;

public void setWorldBounds(int left, int bottom, int width, int height) {
    int top = bottom + height;
    int right = left + width;

    this.left = new BoundingBox(new Vector3(left - 2, 0, 0), new Vector3(left -1, top, 0));
    this.right = new BoundingBox(new Vector3(right + 1, 0, 0), new Vector3(right + 2, top, 0));
    this.top = new BoundingBox(new Vector3(0, top + 1, 0), new Vector3(right, top + 2, 0));
    this.bottom = new BoundingBox(new Vector3(0, bottom - 1, 0), new Vector3(right, bottom - 2, 0));
}

Vector3 lastPosition = new Vector3();
@Override
public void translate(float x, float y) {
    lastPosition.set(position.x, position.y, 0);
    super.translate(x, y);
}

public void translateSafe(float x, float y) {
    translate(x, y);
    update();
    ensureBounds();
    update();
}

public void ensureBounds() {
    if (frustum.boundsInFrustum(left) || frustum.boundsInFrustum(right) || frustum.boundsInFrustum(top) || frustum.boundsInFrustum(bottom)) {
        position.set(lastPosition);
    }
}

Теперь, в вашем обычном sceene или в каком бы режиме вы ни использовали (в моем случае это был пользовательский класс Board):

camera.setWorldBounds()

и в вашем методе GestureListener.pan вы можете вызвать

camera.translateSafe(x, y);

он должен держать вашу камеру в границах

Ответ 4

Здесь мое решение:

float minCameraX = camera.zoom * (camera.viewportWidth / 2);
float maxCameraX = worldSize.x - minCameraX;
float minCameraY = camera.zoom * (camera.viewportHeight / 2);
float maxCameraY = worldSize.y - minCameraY;
camera.position.set(Math.min(maxCameraX, Math.max(targetX, minCameraX)),
        Math.min(maxCameraY, Math.max(targetY, minCameraY)),
        0);

Где:

  • targetX и targetY - это мировые координаты, где находится ваша цель.
  • worldSize является Vector2 размера мира.

Ответ 5

У меня недостаточно репутации для написания комментариев, поэтому я укажу на некоторые предыдущие ответы.

Решение AAverin с ограничивающей рамкой, созданное с идеей Мацемана, не очень хорошо, потому что оно досадно замедляется, когда вы находитесь рядом с одним краем (границей) и пытаетесь перевести по диагонали, и в этом случае вы панорамируете в одну сторону за пределы и другие в правильном направлении.

Я настоятельно рекомендую вам попробовать решение из нижней части метода handleInput, представленного в

https://github.com/libgdx/libgdx/wiki/Orthographic-camera

Это работает плавно, и некоторые из предыдущих ответов выглядят так, но этот использует MathUtils.clamp wihch - прямолинейный и намного более чистый.

Ответ 6

Указанный класс CustomCamera работает не очень хорошо. Я использовал его, чтобы отобразить жест щепотки в zoomSafe, и камера будет постоянно отскакивать/мигать слева направо, когда она находится на краю границ. Камера также не работает должным образом с панорамированием. Если вы попытаетесь переместиться по краю границ, он не будет качаться в любом месте, как если бы края были "липкими". Это происходит потому, что он просто переводится в последнюю позицию вместо того, чтобы просто регулировать координату за пределами границ.

Ответ 7

Идеальный класс для этого (частично благодаря AAverin)

Этот класс не только прилипает к границам, он также привязывается к границам при масштабировании.

Вызовите их для настройки границ и перемещения камеры.

camera.setWorldBounds()
camera.translateSafe(x, y);

При масштабировании вызова

camera.attemptZoom();

И вот класс:

public class CustomCamera extends OrthographicCamera
{
    public CustomCamera() {}

    public CustomCamera(float viewportWidth, float viewportHeight)
    {
        super(viewportWidth, viewportHeight);
    }

    BoundingBox left, right, top, bottom = null;

    public void setWorldBounds(int left, int bottom, int width, int height) {
        int top = bottom + height;
        int right = left + width;

        this.left = new BoundingBox(new Vector3(left - 2, 0, 0), new Vector3(left -1, top, 0));
        this.right = new BoundingBox(new Vector3(right + 1, 0, 0), new Vector3(right + 2, top, 0));
        this.top = new BoundingBox(new Vector3(0, top + 1, 0), new Vector3(right, top + 2, 0));
        this.bottom = new BoundingBox(new Vector3(0, bottom - 1, 0), new Vector3(right, bottom - 2, 0));
    }

    Vector3 lastPosition;
    @Override
    public void translate(float x, float y) {
        lastPosition = new Vector3(position);
        super.translate(x, y);
    }

    public void translateSafe(float x, float y) {
        translate(x, y);
        update();
        ensureBounds();
        update();
    }

    public void ensureBounds()
    {
        if(isInsideBounds())
        {
            position.set(lastPosition);
        }
    }

    private boolean isInsideBounds()
    {
        if(frustum.boundsInFrustum(left) || frustum.boundsInFrustum(right) || frustum.boundsInFrustum(top) || frustum.boundsInFrustum(bottom))
        {
            return true;
        }
        return false;
    }

    public void attemptZoom(float newZoom)
    {
        this.zoom = newZoom;
        this.snapCameraInView();
    }

    private void snapCameraInView()
    {
        float halfOfCurrentViewportWidth = ((viewportWidth * zoom) / 2f);
        float halfOfCurrentViewportHeight = ((viewportHeight * zoom) / 2f);

        //Check the vertical camera.
        if(position.x - halfOfCurrentViewportWidth < 0f) //Is going off the left side.
        {
            //Snap back.
            float amountGoneOver = position.x - halfOfCurrentViewportWidth;
            position.x += Math.abs(amountGoneOver);
        }
        else if(position.x + halfOfCurrentViewportWidth > viewportWidth)
        {
            //Snap back.
            float amountGoneOver = (viewportWidth - (position.x + halfOfCurrentViewportWidth));
            position.x -= Math.abs(amountGoneOver);
        }

        //Check the horizontal camera.
        if(position.y + halfOfCurrentViewportHeight > viewportHeight)
        {
            float amountGoneOver = (position.y + halfOfCurrentViewportHeight) - viewportHeight;
            position.y -= Math.abs(amountGoneOver);
        }
        else if(position.y - halfOfCurrentViewportHeight < 0f)
        {
            float amountGoneOver = (position.y - halfOfCurrentViewportHeight);
            position.y += Math.abs(amountGoneOver);
        }
    }
}