¿Cómo lidiar con diferentes relaciones de aspecto en libGDX?

81

He implementado algunas pantallas usando libGDX que obviamente usarían la Screenclase proporcionada por el marco libGDX. Sin embargo, la implementación de estas pantallas solo funciona con tamaños de pantalla predefinidos. Por ejemplo, si el sprite estaba destinado a una pantalla de tamaño 640 x 480 (relación de aspecto 4: 3), no funcionará como se esperaba en otros tamaños de pantalla porque los sprites se ajustan a los límites de la pantalla y no se ajustan al tamaño de la pantalla. en absoluto. Además, si libGDX hubiera proporcionado un escalado simple, el problema al que me enfrento aún habría estado ahí porque eso haría que la relación de aspecto de la pantalla del juego cambiara.

Después de investigar en Internet, me encontré con un blog / foro que había discutido el mismo tema. Lo he implementado y hasta ahora está funcionando bien. Pero quiero confirmar si esta es la mejor opción para lograrlo o si existen mejores alternativas. A continuación se muestra el código para mostrar cómo estoy lidiando con este problema legítimo.

ENLACE DEL FORO: 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);
        }
    }
}

ACTUALIZACIÓN: Hace poco me enteré de que libGDX tiene algunas de sus propias funciones para mantener relaciones de aspecto que me gustaría discutir aquí. Mientras buscaba el problema de la relación de aspecto en Internet, encontré varios foros / desarrolladores que tenían este problema de "¿Cómo mantener la relación de aspecto en diferentes tamaños de pantalla?" Una de las soluciones que realmente funcionó para mí se publicó arriba.

Más tarde, cuando procedí a implementar los touchDown()métodos para la pantalla, descubrí que debido a la escala al cambiar el tamaño, las coordenadas en las que había implementado touchDown()cambiarían en gran medida. Después de trabajar con un código para traducir las coordenadas de acuerdo con el cambio de tamaño de la pantalla, reduje esta cantidad en gran medida, pero no pude mantenerlas con una precisión milimétrica. Por ejemplo, si lo hubiera implementado touchDown()en una textura, cambiar el tamaño de la pantalla desplazaría el touchListener en la región de la textura algunos píxeles hacia la derecha o hacia la izquierda, dependiendo del cambio de tamaño y esto obviamente no era deseado.

Más tarde supe que la clase de escenario tiene su propia funcionalidad nativa para mantener la relación de aspecto ( boolean stretch = false). Ahora que he implementado mi pantalla usando la clase de escenario, la relación de aspecto se mantiene bien. Sin embargo, al cambiar el tamaño o diferentes tamaños de pantalla, el área negra que se genera siempre aparece en el lado derecho de la pantalla; es decir, la pantalla no está centrada, lo que lo hace bastante feo si el área negra es sustancialmente grande.

¿Algún miembro de la comunidad puede ayudarme a resolver este problema?

Rafay
fuente
¿Podría vincular al blog o foro que tuviera el mismo problema?
Steve Blackwell
@SteveBlackwell aquí está el enlace: java-gaming.org/index.php?topic=25685.new
Rafay
@SteveBlackwell Consulte la pregunta actualizada y vea si puede ayudar con esto.
Rafay
1
No sé mucho sobre el escenario, pero mirando aquí , parece que esto debería haberse solucionado. Los documentos no son de mucha ayuda para centrar. Tal vez puedas mover la cámara un poco o ajustar la ventana gráfica.
Steve Blackwell
1
Bueno, mi problema realmente se resolvió. Lo estoy publicando como respuesta.
Rafay

Respuestas:

51

Cómo hacerlo hoy en día:

Dado que esta es una de las preguntas más famosas sobre libgdx aquí, le daré una pequeña actualización :

LibGDX v1.0 introducido Viewportpara manejar este problema. Es mucho más fácil de usar y la estrategia de escala es conectable, lo que significa que una sola línea puede cambiar el comportamiento y puedes jugar con ella y ver cuál se adapta mejor a tu juego.

Todo lo que necesita saber al respecto se puede encontrar aquí .

Ninguno
fuente
Lo he probado pero todavía mis TextureRegionStreches. Si deja la reszie(int width, int height)función vacía. LA TextureRegionno strectch.
Muhammad Babar
Pero incluso cuando usamos Viewports, tenemos que usar texturas separadas para diferentes relaciones de aspecto, ¿no es así? ¿Existe una lógica para simplificar ese método? ¿O debería hacerse el diseño gráfico manteniéndolo independiente de las resoluciones?
WeirdElfB0y
Buen tutorial sobre el uso de Viewports: gamefromscratch.com/post/2014/12/09/…
ubzack
Agregando a esta respuesta y respondiendo a las preguntas en los comentarios, podrías dibujar tu mundo para 16: 9 y darle más altura a tu textura para poder cargar en 4: 3. Luego, al crear una instancia de su ventana gráfica, puede verificar la relación de aspecto real de la pantalla y, mientras mantiene su ancho estándar, puede darle a su ventana gráfica una altura que se ajuste a la relación de aspecto que está ejecutando. Un inconveniente es que tendrá mucho espacio "vacío" en una resolución de 4: 3, pero si su textura puede manejar eso, todo se mostrará como dibujado y no estirado
Klitos G.
24

EDITAR: libGDX ha evolucionado. La mejor respuesta es ahora esta del usuario nadie. Asegúrese de consultar el enlace a la Viewportdocumentación. .

Cuando SteveBlack publicó el enlace del problema que se informó en la clase de escenario, fui allí solo para descubrir que el problema (que en realidad no era mi problema) se había resuelto en las últimas noches.

Después de investigar aquí y allá en Internet para el problema que estaba teniendo, no pude encontrar ninguna solución, así que decidí contactar a la persona que informó el error directamente. Después de eso, me respondió en los foros de libgdx y estoy en deuda con él por ayudarme. Aqui esta el link

Era una sola línea de código y todo lo que tienes que hacer es:

En el método resize ():

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

Donde 640 X 480 es la resolución de su TextureRegion que describe la relación de aspecto deseada. Si el tamaño de TextureRegion era 320 X 240, entonces ambos argumentos deben cambiarse a la nueva resolución para hacer el truco.

Enlace de perfil de la persona original que realmente resolvió mi problema

Rafay
fuente
6
En libGDX 0.9.6, stage.setViewport (640, 480, false) es suficiente porque ya incluye una llamada a stage.getCamera () .position.set (640/2, 480/2, 0);
Alexis Pautrot
1
setViewportcon 3 parámetros en no disponible ahora!
Muhammad Babar
He cambiado la respuesta aceptada a la publicada por el usuario nadie, como señaló Steve Blackwell, porque parece ser la última y correcta.
Rafay
7

Las barras negras a la izquierda / derecha o arriba / abajo se ven mejor que simplemente distorsionar toda la escena para que se ajuste a la pantalla. Si apunta a una relación de aspecto que se encuentra en el medio de los rangos posibles (4: 3 es probablemente el extremo bajo, 16: 9 es probablemente el extremo superior), entonces las barras deberían permanecer pequeñas para la mayoría de los dispositivos. Esto también te permite usar la mayor parte de la pantalla incluso en pantallas más grandes, y ya tienes el código de ese tipo, por lo que es bastante fácil. Sería aún mejor si esta fuera solo una opción integrada en libgdx.

Pero creo que el mejor enfoque es usar toda la pantalla. Aún no he hecho esto, pero será el enfoque que tomaré para mi próximo proyecto. La idea es que si alguien tiene una pantalla más ancha, debería ver más en los lados. Chris Pruett habla sobre cómo hacer esto en una de sus charlas ( enlace a spot in talk ; de hecho, todo es bastante bueno). La idea es escalar la altura y luego configurar la ventana de visualización lo suficientemente amplia para adaptarse a la pantalla. OpenGL debería encargarse de mostrar el resto. La forma en que lo hace es aquí .

Para libgdx, tal vez haya una manera fácil de hacer esto con scene2d moviendo la cámara sobre el escenario, pero nunca he trabajado con scene2d. En cualquier caso, ejecutar la aplicación como una ventana nativa de tamaño variable es una forma mucho más fácil de probar varios tamaños de pantalla que crear un montón de AVD.

Steve Blackwell
fuente
"En cualquier caso, ejecutar la aplicación como una ventana nativa de tamaño variable es una forma mucho más fácil de probar varios tamaños de pantalla que crear un montón de AVD". Bien dicho. De hecho, estoy intentando trabajar en este fenómeno para garantizar la máxima compatibilidad en lugar de apuntar a diferentes tamaños de pantalla con diferentes tamaños de texturas.
Rafay
3

La clase ResolutionFileResolver le permite resolver nombres de archivos con la mejor resolución. Creo que también funcionará con diferentes relaciones de aspecto, siempre que haya creado sprites para esas relaciones de aspecto. Hay un ejemplo de uso en AssetManagerTest .

Doran
fuente
0

Estoy usando ese método de http://blog.acamara.es/2012/02/05/keep-screen-aspect-ratio-with-different-resolutions-using-libgdx/

Hice esta función que devuelve el punto en la pantalla tocado, reducido a la posición de juego que desea.

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

Use esto en el touchdown / up / drag y puede verificar el toque en su juego.

Pablo Boldijar
fuente
Esto parece similar al método de desproyección de la cámara. ¿Hace lo mismo?
milosmns
@milosmns esta es la forma en que estaba haciendo como hace 1 año, bueno, descubrí lo de desproyectar ... es bueno cuando tu cámara está siempre en la misma posición ..
Boldijar Paul
0

Este código me funciona con la última actualización: * OrthographicCamera es una cámara, esto no hace recortes, solo cambia la ventana gráfica para que el ancho sea todavía "muchas" veces más grande que la ventana / dispositivo real

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