当平移和缩放时,保持libgdx相机在边界内

13

我正在使用LibGDX为Android开发游戏。我已经添加了捏合缩放和平移功能。我的问题是如何防止越过游戏区域的边界。目前,你可以将屏幕拖动到黑色区域之外。当完全缩小时,我知道如何处理这个问题,我只需要做:

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

}

但是,如果放大视图,我该怎么做呢?我知道这并不复杂,只是似乎想不出方法。在创建对象时,我将相机设置在屏幕中央。我知道如何平移,我正在使用camera.translate(-input.deltaX, -input.deltaY, 0),我只需要在此调用之前进行测试,以查看位置是否超出了游戏区域。当我放大视图时,如何测试自己是否位于屏幕边缘?

7个回答

8
感谢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);
    }
}

现在,在您的自定义场景或任何您使用的地方(在我的情况下,它是一个自定义Board类),调用以下内容:
camera.setWorldBounds()

在您的GestureListener.pan方法中,您可以调用以下内容。
camera.translateSafe(x, y);

应该将您的相机限制在范围内


8

您可以使用以下之一:

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

检查一个点/盒子/球是否在相机的视野内。

我通常会定义4个围绕我的世界的盒子,玩家不应该看到这些盒子。如果相机移动并且其中一个盒子在视锥体中,我将相机移回到上一个位置。

编辑:AAvering已经在代码下面实现了此功能。


谢谢,那应该可以。所以你在“游戏区域”周围定义了四个矩形,并为每个框架设置了一个if语句?这样,如果其中一个返回true,你就知道要返回哪个方向了? - weaverx9x9
这是一个很好的想法,但我在寻找如何实现它并实例化BoundingBox方面遇到了困难。假设您有一个游戏区域,例如(0,0)到(800,480),那么您是否需要沿着每个边创建4个BoundingBoxes? - Jammo

6

这是我在我的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 是相机可以到达的相反的最高右侧位置。

我猜你可能缺少的关键部分是按缩放级别缩放相机大小。


5
这是我的解决方案:
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);

含义:

  • targetXtargetY 是你的目标所在的世界坐标。
  • worldSize 是世界的大小,它是一个 Vector2 类型的变量。

1
谢谢你的代码片段!这正是我所想的,但是我懒得自己写它 ;) - Anubian Noob

4

我没有足够的声望来写评论,所以我会指向一些先前的答案。

AAverin使用Matsemann的想法制作的边界框解决方案不好,因为当您靠近一个边缘(边界)并尝试对角线移动时,它会令人恼火地减慢速度,在这种情况下,您正在向一侧移动超出范围,而另一侧则是正确的方向。

我强烈建议您尝试handleInput方法底部呈现的解决方案。

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

那个程序运行顺畅,与之前的答案有些相似,但这个使用了MathUtils.clamp,更加直观简洁。

2

对于这个问题,非常适合使用这个类(部分感谢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);
        }
    }
}

1
给出的CustomCamera类效果不太好。我使用它将捏合手势映射到zoomSafe,当在边界上时,相机会不断地从左到右反弹/闪烁。相机在平移方面也无法正常工作。如果您尝试沿着边界平移,它不会平移到任何地方,就像边缘是“粘性”的一样。这是因为它只是将其转换回到上一个位置,而不是仅调整超出边界的坐标。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接