如何在libGDX中处理不同的宽高比?

时间:2012-02-08 18:04:32

标签: android opengl-es libgdx aspect-ratio

我已经使用libGDX实现了一些屏幕,这显然会使用libGDX框架提供的Screen类。但是,这些屏幕的实现仅适用于预定义的屏幕尺寸。例如,如果精灵用于640 x 480大小的屏幕(4:3宽高比),它将无法按预期在其他屏幕尺寸上工作,因为精灵与屏幕边界相同并且不会缩放到屏幕尺寸一点都不此外,如果libGDX提供了简单的缩放,我面临的问题仍然存在,因为这会导致游戏画面的宽高比发生变化。

在互联网上进行研究后,我遇到了一个讨论同一问题的blog/forum。我已经实现了它,到目前为止它工作正常。但我想确认这是否是实现这一目标的最佳选择,还是有更好的选择。下面的代码展示了我如何处理这个合法的问题。

FORUM LINK:http://www.java-gaming.org/index.php?topic=25685.new

public class SplashScreen implements Screen {

    // Aspect Ratio maintenance
    private static final int VIRTUAL_WIDTH = 640;
    private static final int VIRTUAL_HEIGHT = 480;
    private static final float ASPECT_RATIO = (float) VIRTUAL_WIDTH / (float) VIRTUAL_HEIGHT;

    private Camera camera;
    private Rectangle viewport;
    // ------end------

    MainGame TempMainGame;

    public Texture splashScreen;
    public TextureRegion splashScreenRegion;
    public SpriteBatch splashScreenSprite;

    public SplashScreen(MainGame maingame) {
        TempMainGame = maingame;
    }

    @Override
    public void dispose() {
        splashScreenSprite.dispose();
        splashScreen.dispose();
    }

    @Override
    public void render(float arg0) {
        //----Aspect Ratio maintenance

        // update camera
        camera.update();
        camera.apply(Gdx.gl10);

        // set viewport
        Gdx.gl.glViewport((int) viewport.x, (int) viewport.y,
        (int) viewport.width, (int) viewport.height);

        // clear previous frame
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

        // DRAW EVERYTHING
        //--maintenance end--

        splashScreenSprite.begin();
        splashScreenSprite.disableBlending();
        splashScreenSprite.draw(splashScreenRegion, 0, 0);
        splashScreenSprite.end();
    }

    @Override
    public void resize(int width, int height) {
        //--Aspect Ratio Maintenance--
        // calculate new viewport
        float aspectRatio = (float)width/(float)height;
        float scale = 1f;
        Vector2 crop = new Vector2(0f, 0f);

        if(aspectRatio > ASPECT_RATIO) {
            scale = (float) height / (float) VIRTUAL_HEIGHT;
            crop.x = (width - VIRTUAL_WIDTH * scale) / 2f;
        } else if(aspectRatio < ASPECT_RATIO) {
            scale = (float) width / (float) VIRTUAL_WIDTH;
            crop.y = (height - VIRTUAL_HEIGHT * scale) / 2f;
        } else {
            scale = (float) width / (float) VIRTUAL_WIDTH;
        }

        float w = (float) VIRTUAL_WIDTH * scale;
        float h = (float) VIRTUAL_HEIGHT * scale;
        viewport = new Rectangle(crop.x, crop.y, w, h);
        //Maintenance ends here--
    }

    @Override
    public void show() {
        camera = new OrthographicCamera(VIRTUAL_WIDTH, VIRTUAL_HEIGHT); //Aspect Ratio Maintenance

        splashScreen = new Texture(Gdx.files.internal("images/splashScreen.png"));
        splashScreenRegion = new TextureRegion(splashScreen, 0, 0, 640, 480);
        splashScreenSprite = new SpriteBatch();

        if(Assets.load()) {
            this.dispose();
            TempMainGame.setScreen(TempMainGame.mainmenu);
        }
    }
}

更新:我最近才知道libGDX有一些自己的功能来维护宽高比,我想在这里讨论。在互联网上搜索宽高比问题时,我遇到了一些论坛/开发人员,他们遇到了“如何在不同屏幕尺寸上保持宽高比?”的问题。其中一个真正适用于我的解决方案已在上面发布。

稍后,当我继续为屏幕实现touchDown()方法时,我发现由于调整大小调整,我实施touchDown()的坐标会发生很大变化。在使用一些代码来根据屏幕调整大小来转换坐标之后,我在很大程度上减少了这个数量但是我没有成功地用针点精度来维护它们。例如,如果我在纹理上实现了touchDown(),则调整屏幕大小会使纹理区域上的touchListener向右或向左移动一些像素,具体取决于调整大小,这显然是不受欢迎的。

后来我才知道舞台类有自己的原生功能来维持宽高比(boolean stretch = false)。现在我已经使用舞台类实现了我的屏幕,因此它可以很好地保持宽高比。但是,在调整大小或不同屏幕尺寸时,生成的黑色区域始终显示在屏幕的右侧;也就是说,如果黑色区域非常大,则屏幕不居中,这使得它非常难看。

任何社区成员都可以帮我解决这个问题吗?

7 个答案:

答案 0 :(得分:50)

现在该怎么做:

由于这是关于libgdx最着名的问题之一,我会给它一些更新

LibGDX v1.0引入了Viewport来解决这个问题。它更容易使用,缩放策略是可插拔的,这意味着单行可以改变行为,你可以玩它,看看哪一个最适合你的游戏。

可以找到您需要了解的所有内容here

答案 1 :(得分:24)

编辑: libGDX已经发展。现在最好的答案是用户this one。请务必查看Viewport documentation.

的链接

正如史蒂夫布莱克发布了舞台课上报道的问题的链接,我去那里发现问题(实际上不是我的问题)已在最新的夜魇中得到解决。

在互联网上搜索我遇到的问题之后,我找不到任何解决方案,所以决定直接联系报告错误的人。在那之后,他在libgdx论坛上回复了我,我很感激他帮助我。 Here is the link

这是一行代码,你所要做的就是:

在resize()方法中:

stage.setViewport(640, 480, false);
stage.getCamera().position.set(640/2, 480/2, 0);

其中640 X 480是TextureRegion的分辨率,用于描述预期的宽高比。如果您的TextureRegion大小为320 X 240,则应将两个参数更改为新分辨率以执行此操作。

Profile link of the original person who actually resolved my issue

答案 2 :(得分:7)

左/右或上/下的黑条看起来比仅仅扭曲整个场景以适合屏幕更好。如果您将宽高比定位在可能范围的中间(4:3可能是低端,16:9可能是高端),那么对于大多数设备,条形应保持较小。这也让你在大屏幕上使用大多数屏幕,而你已经有了这个人的代码,所以这很容易。如果这只是libgdx内置的选项,那就更好了。

但是,我认为最好的方法是使用整个屏幕。我还没有这样做,但这将是我为下一个项目采取的方法。这个想法是,如果有人有更宽的屏幕,那么他们应该看到更多的侧面。克里斯普鲁特谈到如何在他的一次谈话中做到这一点(link to spot in talk - 实际上,整个事情实际上非常好)。我们的想法是缩放高度,然后将视口设置得足够宽以适合屏幕。 OpenGL应该负责显示其余部分。他这样做的方式是here

对于libgdx,也许有一种简单的方法可以通过在舞台上移动相机来实现这一点,但是我从未真正使用过scene2d。无论如何,将应用程序作为本机可调整大小的窗口运行是一种比创建一堆AVD更容易测试多种屏幕尺寸的方法。

答案 3 :(得分:5)

请参阅官方文档中Viewport部分的最后一部分: https://code.google.com/p/libgdx/wiki/scene2d#Viewport

答案 4 :(得分:3)

ResolutionFileResolver类允许您将文件名解析为最佳分辨率。我相信,只要您为这些宽高比创建精灵,它也可以使用不同的宽高比。 AssetManagerTest中有一个示例用法。

答案 5 :(得分:0)

我正在http://blog.acamara.es/2012/02/05/keep-screen-aspect-ratio-with-different-resolutions-using-libgdx/

使用该方法

我做了这个功能,返回触摸屏上的点,缩小到你想要的游戏位置。

public Vector2 getScaledPos(float x, float y) {
    float yR = viewport.height / (y - viewport.y);
    y = CAMERA_HEIGHT / yR;

    float xR = viewport.width / (x - viewport.x);
    x = CAMERA_WIDTH / xR;

    return new Vector2(x, CAMERA_HEIGHT - y);
}

在触地/向上/拖动中使用此功能,您可以检查游戏中的触控效果。

答案 6 :(得分:0)

此代码适用于我的最新更新: * OrthographicCamera是凸轮,这不会裁剪,只需更改视口,使宽度仍然比实际窗口/设备大“那么多”倍数

public void resize(int width, int height) {
    int newW = width, newH = height;
    if (cam.viewportWidth > width) {
        float scale = (float) cam.viewportWidth / (float) width;
        newW *= scale;
        newH *= scale;
    }

    // true here to flip the Y-axis
    cam.setToOrtho(true, newW, newH);
}