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


81

我使用libGDX实现了一些屏幕,这些屏幕显然将使用ScreenlibGDX框架提供的类。但是,这些屏幕的实现仅适用于预定义的屏幕尺寸。例如,如果子图形用于640 x 480尺寸的屏幕(长宽比为4:3),则它在其他屏幕尺寸上将无法正常工作,因为子图形会与屏幕边界相称并且不会缩放到屏幕尺寸完全没有 而且,如果libGDX将提供简单的缩放比例,那么我所面临的问题仍然存在,因为这将导致游戏屏幕的宽高比发生变化。

在研究互联网之后,我遇到了讨论相同问题的博客/论坛。我已经实现了,到目前为止,它运行良好。但我想确认这是否是实现此目标的最佳选择,还是有更好的选择。下面的代码显示了我如何处理此合法问题。

论坛链接: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)。现在,我已经使用舞台类实现了屏幕,通过它可以很好地保持宽高比。但是,在调整大小或使用不同的屏幕尺寸时,生成的黑色区域始终出现在屏幕右侧。那就是屏幕没有居中,如果黑色区域很大,这会很丑陋。

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


您可以链接到存在相同问题的博客或论坛吗?
史蒂夫·布莱克韦尔

@SteveBlackwell,这里是链接:java-gaming.org/index.php?topic=25685.new
Rafay 2012年

@SteveBlackwell请查看更新后的问题,看看是否可以帮助您。
拉斐

1
我对舞台不是很了解,但是在这里看,似乎应该已经解决了。这些文档在居中方面并没有太多帮助。也许您可以将相机稍微移动一点或调整视口。
史蒂夫·布莱克韦尔

1
好吧,我的问题实际上得到了解决。我将其发布为答案。
拉斐

Answers:


51

如今该怎么做:

由于这是关于libgdx的最著名的问题之一,因此我将对其进行一些更新

引入LibGDX v1.0Viewport来处理此问题。它使用起来容易得多,并且缩放策略是可插入的,这意味着一行可以更改行为,您可以使用它来玩,看看哪一条最适合您的游戏。

您需要了解的所有信息都可以在这里找到。


我已经尝试过,但仍然是TextureRegionStreches。如果保留该reszie(int width, int height)功能为空。在TextureRegion不strectch。
Muhammad Babar 2014年

但是即使使用视口,我们也必须对不同的宽高比使用单独的纹理,不是吗?是否有简化该方法的逻辑?还是应该通过使图形设计独立于分辨率来完成图形设计?
WeirdElfB0y

有关使用视口的良好教程:gamefromscratch.com/post/2014/12/09/…–
ubzack

添加此答案并回答注释中的问题,您可以绘制16:9的世界,并为纹理提供更高的高度,以便能够以4:3的速度加载。然后,在实例化视口时,您可以检查屏幕的实际纵横比,并在保持宽度标准的同时,为视口设置一个高度,使其适合您正在运行的纵横比。缺点是您将在4:3分辨率上有很多“空”空间,但是如果您的纹理可以处理,则所有内容都将显示为已绘制且不会拉伸
Klitos G.

24

编辑: libGDX已经发展。最好的答案是现在没人管这个。确保检查Viewport文档的链接

当SteveBlack发布在舞台课程中报告的问题的链接时,我去那儿只是发现问题(实际上不是我的问题)已在最新的夜间会议中得到解决。

在互联网上到处研究了我遇到的问题后,我找不到任何解决方案,因此决定直接与报告该错误的人员联系。之后,他在libgdx论坛上回答了我,我对他帮助我的工作表示感谢。链接在这里

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

在resize()方法中:

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

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

解决我的问题的原始人的个人资料链接


6
在libGDX 0.9.6中,stage.setViewport(640,480,false)就足够了,因为它已经包括对stage.getCamera()。position.set(640/2,480/2,0)的调用。
亚历克西斯(Alexis Pautrot)2012年

1
setViewport3个参数现在不可用!
穆罕默德·巴巴尔

正如Steve Blackwell指出的那样,我已将接受的答案更改为没有人发布的答案,因为这似乎是最新且正确的答案。
拉斐2014年

7

左/右或上/下的黑条看起来比扭曲整个场景以适合屏幕更好。如果您将宽高比设为可能范围的中间值(4:3可能是低端,16:9可能是高端),则对于大多数设备来说,条形应该保持较小。这也使您即使在较大的屏幕上也可以使用大部分屏幕,并且您已经有了该家伙的代码,因此非常简单。如果这只是libgdx中内置的选项,那就更好了。

但是,我认为最好的方法是使用整个屏幕。我还没有做到这一点,但这将是我下一个项目所采用的方法。这个想法是,如果某人的屏幕更宽,那么他们应该在侧面看到更多。克里斯·普鲁特(Chris Pruett)在他的一场演讲中谈到了如何做到这一点(链接到演讲中的链接-实际上,整个过程实际上还不错)。想法是按比例缩放高度,然后将视口设置得足够宽以适合屏幕。OpenGL应该注意显示其余部分。他的方式就是在这里

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


“无论如何,将应用程序作为可调整大小的本机窗口运行是测试多个屏幕尺寸的方法,比创建一堆AVD更加容易。” 说得好。实际上,我正在尝试解决这种现象,以确保最大的兼容性,而不是针对具有不同纹理尺寸的不同屏幕尺寸。
拉斐



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

在触地/向上/拖动中使用它,您可以检查游戏中的触地。


这似乎与相机的unproject方法类似。它做同样的事情吗?
milosmns 2014年

@milosmns这是我在做类似1年前的样子,好,我发现了unproject事情..是好当你的相机总是在同样的位置..
Boldijar保罗

0

这段代码适用于我的最新更新:* OrthographicCamera是cam,不会进行裁剪,只更改视口,因此宽度仍然比实际窗口/设备大“很多”倍

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);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.