web-dev-qa-db-fra.com

Comment traiter les différents formats d'aspect dans libGDX?

J'ai implémenté des écrans en utilisant libGDX qui utiliseraient évidemment la classe Screen fournie par le framework libGDX. Cependant, la mise en œuvre de ces écrans ne fonctionne qu'avec des tailles d'écran prédéfinies. Par exemple, si le Sprite était destiné à un écran de 640 x 480 (format d'image 4: 3), il ne fonctionnerait pas comme prévu pour d'autres tailles d'écran, car les images-objets respectent les limites de l'écran et ne sont pas redimensionnées. du tout. De plus, si libGDX avait fourni une mise à l'échelle simple, le problème auquel je ferais face aurait toujours existé, car le rapport de format de l'écran de jeu serait modifié.

Après des recherches sur Internet, je suis tombé sur un blog/forum qui avait discuté du même problème. Je l'ai mis en œuvre et jusqu'à présent, il fonctionne bien. Mais je veux confirmer si c'est la meilleure option pour y parvenir ou s'il existe de meilleures alternatives. Le code ci-dessous montre comment je gère ce problème légitime.

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

UPDATE: J'ai récemment appris que libGDX dispose de certaines fonctionnalités propres à la gestion des rapports d'aspect, ce que j'aimerais discuter ici. En cherchant la question du rapport de format sur Internet, je suis tombé sur plusieurs forums/développeurs qui avaient ce problème de "Comment maintenir le rapport de format sur différentes tailles d'écran?" L'une des solutions qui a vraiment fonctionné pour moi a été publiée ci-dessus.

Plus tard, lorsque j’ai poursuivi l’implémentation des méthodes touchDown() pour l’écran, j’ai constaté qu’en raison de la mise à l’échelle lors du redimensionnement, les coordonnées sur lesquelles j’avais implémenté touchDown() changeraient considérablement. montant. Après avoir travaillé avec du code pour traduire les coordonnées conformément au redimensionnement de l'écran, j'ai réduit ce montant dans une large mesure, mais je n'ai pas réussi à les maintenir avec une précision optimale. Par exemple, si j'avais implémenté touchDown() sur une texture, le redimensionnement de l'écran déplacerait le touchListener de la région de la texture de quelques pixels vers la droite ou la gauche, en fonction du redimensionnement, ce qui était évidemment indésirable.

Plus tard, j'ai appris que la classe de scène avait sa propre fonctionnalité native pour conserver les proportions (boolean stretch = false). Maintenant que j'ai implémenté mon écran en utilisant la classe de scène, le rapport de format est bien maintenu. Toutefois, en cas de redimensionnement ou de tailles d’écran différentes, la zone noire générée apparaît toujours à droite de l’écran; c'est-à-dire que l'écran n'est pas centré, ce qui le rend assez moche si la zone noire est très grande.

Un membre de la communauté peut-il m'aider à résoudre ce problème?

79
Rafay

Comment le faire aujourd'hui:

Comme il s’agit là d’une des questions les plus connues sur libgdx, je vais vous donner une petite mise à jour :

LibGDX v1.0 a introduit Viewport pour gérer ce problème. Il est beaucoup plus facile à utiliser et la stratégie de redimensionnement est connectable, ce qui signifie qu'une seule ligne peut modifier le comportement. Vous pouvez y jouer et voir laquelle correspond le mieux à votre jeu.

Tout ce que vous devez savoir à ce sujet peut être trouvé ici .

50
noone

EDIT: libGDX a évolué. La meilleure réponse est maintenant celui-ci par l'utilisateur noone. Assurez-vous de vérifier le lien vers la documentation Viewport. .

Lorsque SteveBlack a posté le lien du problème signalé en classe, je suis allé là-bas pour découvrir que le problème (qui n'était pas vraiment le mien) a été résolu dans les derniers nightlies.

Après avoir cherché ici et là sur Internet le problème que je rencontrais, je n’ai trouvé aucune solution, j’ai donc décidé de contacter directement la personne qui a signalé le bogue. Après cela, il m'a répondu sur les forums de libgdx et je lui suis redevable de l'avoir aidé. Voici le lien

C'était une seule ligne de code et tout ce que vous avez à faire est:

Dans le resize () methond:

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

Où 640 X 480 est la résolution de votre TextureRegion qui décrit le rapport de format souhaité. Si votre taille de TextureRegion était de 320 x 240, alors les deux arguments devraient être remplacés par la nouvelle résolution pour que le tour soit joué.

Lien de profil de la personne d'origine qui a réellement résolu mon problème

24
Rafay

Les barres noires à gauche/à droite ou en haut/en bas sont plus esthétiques que la simple distorsion de toute la scène pour l'adapter à l'écran. Si vous ciblez un format d'image qui se situe au milieu des plages possibles (4: 3 est probablement le bas de gamme, 16: 9 est probablement le haut de gamme), les barres doivent rester petites pour la plupart des appareils. Cela vous permet également d’utiliser la plupart de l’écran, même sur des écrans plus grands, et que vous avez déjà le code de ce type, c’est donc assez facile. Ce serait encore plus agréable s'il ne s'agissait que d'une option intégrée à libgdx.

Mais, je pense que la meilleure approche consiste à utiliser tout l'écran. Je ne l'ai pas encore fait, mais ce sera l'approche que je vais adopter pour mon prochain projet. L'idée est que si quelqu'un a un écran plus large, il devrait voir plus sur les côtés. Chris Pruett explique comment procéder dans le cadre de l’un de ses entretiens ( lien pour le localiser - en fait, le tout est en fait plutôt bon). L'idée est de redimensionner la hauteur, puis de définir une fenêtre suffisamment large pour qu'elle s'adapte à l'écran. OpenGL devrait s'occuper d'afficher le reste. La façon dont il le fait est ici .

Pour libgdx, il y a peut-être un moyen facile de faire cela avec scene2d en déplaçant la caméra sur la scène, mais je n'ai jamais vraiment travaillé avec scene2d. Dans tous les cas, exécuter l'application en tant que fenêtre redimensionnable native est un moyen beaucoup plus facile de tester plusieurs tailles d'écran plutôt que de créer un ensemble de fichiers AVD.

7
Steve Blackwell

Voir la dernière partie de la section Viewport dans la documentation officielle: https://code.google.com/p/libgdx/wiki/scene2d#Viewport

5
NateS

La classe ResolutionFileResolver vous permet de résoudre les noms de fichiers avec la meilleure résolution. Je pense que cela fonctionnera également avec différents formats, à condition que vous ayez créé des sprites pour ces formats. Il existe un exemple d'utilisation dans AssetManagerTest .

3
Doran

J'utilise cette méthode de http://blog.acamara.es/2012/02/05/keep-screen-aspect-ratio-with-different-resolutions-using-libgdx/

J'ai créé cette fonction qui renvoie le point de l'écran touché, réduit à la position de jeu souhaitée.

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

Utilisez ceci dans les touchdown/up/drag et vous pouvez vérifier la présence de contact dans votre jeu.

0
Boldijar Paul

Ce code fonctionne pour moi avec la dernière mise à jour: * OrthographicCamera est cam, cela ne fait aucun recadrage, change simplement la fenêtre d'affichage afin que la largeur soit toujours "à" beaucoup plus grande que la fenêtre/le périphérique réel

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