如何处理 libGDX 中的不同纵横比?
Posted
技术标签:
【中文标题】如何处理 libGDX 中的不同纵横比?【英文标题】:How to deal with different aspect ratios in libGDX? 【发布时间】:2012-03-01 05:16:01 【问题描述】:我已经使用 libGDX 实现了一些屏幕,这些屏幕显然会使用 libGDX 框架提供的Screen
类。但是,这些屏幕的实现仅适用于预定义的屏幕尺寸。例如,如果 sprite 用于 640 x 480 尺寸的屏幕(4:3 宽高比),它不会在其他屏幕尺寸上按预期工作,因为 sprite 符合屏幕边界并且未缩放到屏幕尺寸一点也不。此外,如果 libGDX 提供了简单的缩放,那么我面临的问题仍然存在,因为这会导致游戏屏幕的纵横比发生变化。
在互联网上进行研究后,我遇到了一个讨论相同问题的blog/forum。我已经实现了它,到目前为止它运行良好。但我想确认这是否是实现这一目标的最佳选择,或者是否有更好的选择。下面是显示我如何处理这个合法问题的代码。
论坛链接: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()
方法时,我发现由于resize 的缩放,我实现touchDown()
的坐标会发生很大变化。在使用一些代码根据屏幕调整大小转换坐标之后,我在很大程度上减少了这个数量,但我没有成功地保持它们的精确度。例如,如果我在纹理上实现了touchDown()
,调整屏幕大小会使纹理区域上的 touchListener 向右或向左移动一些像素,具体取决于调整大小,这显然是不希望的。
后来我才知道舞台类有自己的原生功能来保持纵横比(boolean stretch = false
)。现在我已经使用舞台类实现了我的屏幕,它很好地保持了纵横比。但是在调整大小或不同的屏幕尺寸时,生成的黑色区域总是出现在屏幕的右侧;那就是屏幕没有居中,如果黑色区域很大,这会很丑。
任何社区成员都可以帮我解决这个问题吗?
【问题讨论】:
你能链接到有同样问题的博客或论坛吗? @SteveBlackwell 这里是链接:java-gaming.org/index.php?topic=25685.new @SteveBlackwell 请查看更新后的问题,看看您是否可以提供帮助。 我对Stage不太了解,但是看here,好像应该已经修复了。 docs 对居中没有太大帮助。也许您可以将相机移动一点或调整视口。 其实我的问题已经解决了。我将其发布为答案。 【参考方案1】:左侧/右侧或顶部/底部的黑条看起来比仅仅扭曲整个场景以适应屏幕更好。如果您的目标纵横比在可能范围的中间(4:3 可能是低端,16:9 可能是高端),那么对于大多数设备来说,条应该保持较小。即使在更大的屏幕上,这也可以让您使用大部分屏幕,并且您已经拥有该人的代码,因此非常容易。如果这只是 libgdx 中内置的一个选项,那就更好了。
但是,我认为最好的方法是使用整个屏幕。我还没有这样做,但这将是我下一个项目所采用的方法。这个想法是,如果有人有更宽的屏幕,那么他们应该在侧面看到更多。 Chris Pruett 在他的一次演讲中谈到了如何做到这一点(link to spot in talk--实际上,整个事情实际上非常好)。这个想法是缩放高度,然后将视口设置为足够宽以适合屏幕。 OpenGL 应该负责显示其余部分。他这样做的方式是here。
对于 libgdx,也许有一种简单的方法可以通过将相机移动到舞台上来使用 scene2d,但我从未真正使用过 scene2d。无论如何,将应用程序作为原生可调整大小的窗口运行比创建一堆 AVD 更容易测试多种屏幕尺寸。
【讨论】:
“无论如何,将应用程序作为原生可调整大小的窗口运行比创建一堆 AVD 更容易测试多种屏幕尺寸。”说得好。实际上,我正在尝试解决这种现象,以确保最大的兼容性,而不是针对具有不同尺寸纹理的不同屏幕尺寸。【参考方案2】:ResolutionFileResolver 类允许您将文件名解析为最佳分辨率。我相信这也适用于不同的纵横比,前提是您已经为这些纵横比创建了精灵。 AssetManagerTest中有一个使用示例。
【讨论】:
【参考方案3】:编辑: libGDX 已经进化。最佳答案现在是用户 noone 的 this one。请务必查看Viewport
documentation. 的链接。
当 SteveBlack 发布了在舞台课上报告的问题的链接时,我去那里只是为了发现问题(这实际上不是我的问题)已在最新的夜间报告中得到解决。
在互联网上到处搜索我遇到的问题后,我找不到任何解决方案,因此决定直接联系报告错误的人。之后,他在 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
【讨论】:
在 libGDX 0.9.6 中, stage.setViewport ( 640, 480, false ) 就足够了,因为它已经包含了对 stage.getCamera ().position.set ( 640 / 2, 480 / 2 的调用, 0 );setViewport
有 3 个参数现在不可用!
我已将接受的答案更改为用户 noone 发布的答案,正如 Steve Blackwell 指出的那样,因为它似乎是最新且正确的答案。【参考方案4】:
查看官方文档中 Viewport 部分的最后一部分: https://code.google.com/p/libgdx/wiki/scene2d#Viewport
【讨论】:
【参考方案5】:我正在使用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 这是我一年前的做法,很好,我发现了 unproject 的事情。当你的相机总是在同一个位置时很好。。【参考方案6】:现在怎么做:
由于这是 libgdx 上最著名的问题之一,我将对其进行一点更新:
LibGDX v1.0 引入了Viewport
来处理这个问题。它更易于使用,并且扩展策略是可插拔的,这意味着单行可以改变行为,您可以使用它来查看哪一个最适合您的游戏。
你需要知道的一切都可以在here找到。
【讨论】:
我已经尝试过了,但仍然是我的TextureRegion
Strechs。如果将reszie(int width, int height)
函数留空。 TextureRegion
不会拉伸。
但即使在使用视口时,我们也必须为不同的纵横比使用单独的纹理,不是吗?是否有简化该方法的逻辑?还是应该通过保持独立于分辨率来完成图形设计?
使用视口的好教程:gamefromscratch.com/post/2014/12/09/…
添加到这个答案并回答 cmets 中的问题,您可以绘制 16:9 的世界,并为您的纹理提供更多高度,以便能够以 4:3 加载。然后,在实例化您的视口时,您可以检查屏幕的实际纵横比,同时保持您的宽度标准,您可以为您的视口提供一个适合您正在运行的纵横比的高度。一个缺点是在 4:3 分辨率下您将有很多“空白”空间,但如果您的纹理可以处理,所有内容都将显示为绘制而不是拉伸【参考方案7】:
此代码适用于我的最新更新: * 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);
【讨论】:
以上是关于如何处理 libGDX 中的不同纵横比?的主要内容,如果未能解决你的问题,请参考以下文章
如何在平方 SurfaceView(如 Instagram)中将相机预览大小设置为平方纵横比
如何在不改变原始纵横比的情况下组合两个不同大小的 UIImage?