Wie gehe ich mit verschiedenen Seitenverhältnissen in libGDX um?

81

Ich habe einige Bildschirme mit libGDX implementiert, die offensichtlich die Screenvom libGDX-Framework bereitgestellte Klasse verwenden würden . Die Implementierung für diese Bildschirme funktioniert jedoch nur mit vordefinierten Bildschirmgrößen. Wenn das Sprite beispielsweise für einen Bildschirm mit einer Größe von 640 x 480 (Seitenverhältnis 4: 3) gedacht war, funktioniert es bei anderen Bildschirmgrößen nicht wie vorgesehen, da die Sprites entlang der Bildschirmgrenzen verlaufen und nicht auf die Bildschirmgröße skaliert sind überhaupt. Wenn die libGDX eine einfache Skalierung bereitgestellt hätte, wäre das Problem, mit dem ich konfrontiert bin, immer noch vorhanden, da sich dadurch das Seitenverhältnis des Spielbildschirms ändern würde.

Nachdem ich im Internet recherchiert hatte, stieß ich auf einen Blog / ein Forum , in dem das gleiche Thema diskutiert wurde. Ich habe es implementiert und bis jetzt funktioniert es gut. Ich möchte jedoch bestätigen, ob dies die beste Option ist, um dies zu erreichen, oder ob es bessere Alternativen gibt. Unten finden Sie den Code, der zeigt, wie ich mit diesem legitimen Problem umgehe.

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: Ich habe kürzlich erfahren, dass libGDX einige seiner eigenen Funktionen zur Aufrechterhaltung von Seitenverhältnissen hat, die ich hier diskutieren möchte. Bei der Suche nach dem Problem mit dem Seitenverhältnis im Internet bin ich auf mehrere Foren / Entwickler gestoßen, bei denen das Problem aufgetreten ist: "Wie kann das Seitenverhältnis auf verschiedenen Bildschirmgrößen beibehalten werden?" Eine der Lösungen, die wirklich für mich funktioniert haben, wurde oben veröffentlicht.

Als ich später mit der Implementierung der touchDown()Methoden für den Bildschirm fortfuhr , stellte ich fest, dass sich die von mir implementierten Koordinaten aufgrund der Skalierung beim Ändern der Größe erheblich ändern touchDown()würden. Nachdem ich mit einem Code gearbeitet hatte, um die Koordinaten gemäß der Bildschirmgröße zu übersetzen, reduzierte ich diesen Betrag erheblich, aber es gelang mir nicht, sie punktgenau zu pflegen. Wenn ich beispielsweise touchDown()eine Textur implementiert hätte, würde eine Größenänderung des Bildschirms den touchListener im Texturbereich je nach Größenänderung um einige Pixel nach rechts oder links verschieben, was offensichtlich unerwünscht war.

Später erfuhr ich, dass die Stage-Klasse über eine eigene native Funktionalität verfügt, um das Seitenverhältnis beizubehalten ( boolean stretch = false). Nachdem ich meinen Bildschirm mithilfe der Stage-Klasse implementiert habe, wird das Seitenverhältnis dadurch gut beibehalten. Bei Größenänderung oder unterschiedlichen Bildschirmgrößen wird der erzeugte schwarze Bereich jedoch immer auf der rechten Seite des Bildschirms angezeigt. Das heißt, der Bildschirm ist nicht zentriert, was es ziemlich hässlich macht, wenn der schwarze Bereich wesentlich groß ist.

Kann mir ein Community-Mitglied helfen, dieses Problem zu lösen?

Rafay
quelle
Könnten Sie einen Link zu dem Blog oder Forum erstellen, in dem das gleiche Problem aufgetreten ist?
Steve Blackwell
@SteveBlackwell hier ist der Link: java-gaming.org/index.php?topic=25685.new
Rafay
@SteveBlackwell Bitte lesen Sie die aktualisierte Frage und prüfen Sie, ob Sie dabei helfen können.
Rafay
1
Ich weiß nicht viel über die Bühne, aber hier scheint es, als hätte dies behoben werden müssen. Die Dokumente sind keine allzu große Hilfe für die Zentrierung. Vielleicht können Sie die Kamera ein wenig bewegen oder das Ansichtsfenster anpassen.
Steve Blackwell
1
Nun, mein Problem wurde tatsächlich gelöst. Ich poste es als Antwort.
Rafay

Antworten:

51

Wie es heutzutage geht:

Da dies hier eine der bekanntesten Fragen zu libgdx ist, werde ich ein kleines Update geben :

LibGDX v1.0 wurde eingeführt Viewport, um dieses Problem zu beheben. Es ist viel einfacher zu bedienen und die Skalierungsstrategie ist steckbar, was bedeutet, dass eine einzelne Zeile das Verhalten ändern kann und Sie damit spielen und sehen können, welche am besten zu Ihrem Spiel passt.

Alles, was Sie darüber wissen müssen, finden Sie hier .

Niemand
quelle
Ich habe es versucht, aber immer noch meine TextureRegionStreches. Wenn Sie die reszie(int width, int height)Funktion leer lassen. Das strekt TextureRegionnicht.
Muhammad Babar
Aber selbst wenn wir Ansichtsfenster verwenden, müssen wir separate Texturen für unterschiedliche Seitenverhältnisse verwenden, nicht wahr? Gibt es eine Logik, um diese Methode zu vereinfachen? Oder sollte das Grafikdesign durchgeführt werden, indem es unabhängig von Auflösungen gehalten wird?
WeirdElfB0y
Gutes Tutorial zur Verwendung von Ansichtsfenstern: gamefromscratch.com/post/2014/12/09/…
ubzack
Wenn Sie diese Antwort ergänzen und die Fragen in den Kommentaren beantworten, können Sie Ihre Welt für 16: 9 zeichnen und Ihrer Textur mehr Höhe verleihen, um in 4: 3 laden zu können. Wenn Sie dann Ihr Ansichtsfenster instanziieren, können Sie das tatsächliche Seitenverhältnis des Bildschirms überprüfen und unter Beibehaltung Ihres Breitenstandards Ihrem Ansichtsfenster eine Höhe geben, die dem von Ihnen ausgeführten Seitenverhältnis entspricht. Ein Nachteil ist, dass Sie bei einer Auflösung von 4: 3 viel "leeren" Platz haben, aber wenn Ihre Textur damit umgehen kann, wird alles als gezeichnet und nicht gedehnt
angezeigt
24

EDIT: libGDX hat sich weiterentwickelt. Die beste Antwort ist jetzt diese von niemandem. Überprüfen Sie unbedingt den Link zur ViewportDokumentation. .

Als SteveBlack den Link zu dem Problem veröffentlichte, das in der Bühnenklasse gemeldet wurde, ging ich dorthin, um festzustellen, dass das Problem (das eigentlich nicht mein Problem war) in den letzten Nightlies behoben wurde.

Nachdem ich hier und da im Internet nach dem Problem gesucht hatte, das ich hatte, konnte ich keine Lösung finden. Deshalb entschied ich mich, die Person zu kontaktieren, die den Fehler direkt gemeldet hatte. Danach antwortete er mir in libgdx-Foren und ich bin ihm zu Dank verpflichtet, dass er mir geholfen hat. Hier ist der Link

Es war eine einzelne Codezeile und alles, was Sie tun müssen, ist:

In der Methode resize ():

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

Wobei 640 x 480 die Auflösung Ihrer TextureRegion ist, die das beabsichtigte Seitenverhältnis beschreibt. Wenn Ihre TextureRegion-Größe 320 x 240 war, sollten beide Argumente in die neue Auflösung geändert werden, um den Trick auszuführen.

Profillink der ursprünglichen Person, die mein Problem tatsächlich gelöst hat

Rafay
quelle
6
In libGDX 0.9.6 reicht stage.setViewport (640, 480, false) aus, da es bereits einen Aufruf von stage.getCamera () .position.set (640/2, 480/2, 0) enthält.
Alexis Pautrot
1
setViewportmit 3 Parametern ist jetzt nicht verfügbar!
Muhammad Babar
Ich habe die akzeptierte Antwort in die Antwort geändert, die von Benutzer noone gepostet wurde, wie Steve Blackwell betonte, da es die neueste und richtige zu sein scheint.
Rafay
7

Schwarze Balken links / rechts oder oben / unten sehen besser aus, als nur die gesamte Szene zu verzerren, um sie an den Bildschirm anzupassen. Wenn Sie ein Seitenverhältnis anstreben, das in der Mitte des möglichen Bereichs liegt (4: 3 ist wahrscheinlich ein niedriges Ende, 16: 9 ist wahrscheinlich ein hohes Ende), sollten die Balken für die meisten Geräte klein bleiben. Auf diese Weise können Sie den größten Teil des Bildschirms auch auf größeren Bildschirmen verwenden, und Sie haben bereits den Code dieses Typen, sodass es ziemlich einfach ist. Es wäre noch schöner, wenn dies nur eine in libgdx integrierte Option wäre.

Ich denke jedoch, dass der beste Ansatz darin besteht, den gesamten Bildschirm zu verwenden. Ich habe das noch nicht getan, aber es wird der Ansatz sein, den ich für mein nächstes Projekt verfolge. Die Idee ist, dass jemand, der einen breiteren Bildschirm hat, mehr an den Seiten sehen sollte. Chris Pruett spricht in einem seiner Vorträge darüber, wie man das macht ( Link zum Spot im Gespräch - eigentlich ist das Ganze eigentlich ziemlich gut). Die Idee ist, die Höhe zu skalieren und dann das Ansichtsfenster so weit einzustellen, dass es auf den Bildschirm passt. OpenGL sollte sich um die Anzeige des Restes kümmern. So wie er es macht, ist hier .

Für libgdx gibt es vielleicht eine einfache Möglichkeit, dies mit scene2d zu tun, indem Sie die Kamera über die Bühne bewegen, aber ich habe noch nie mit scene2d gearbeitet. In jedem Fall ist das Ausführen der App als natives Fenster mit veränderbarer Größe eine viel einfachere Möglichkeit, mehrere Bildschirmgrößen zu testen, als eine Reihe von AVDs zu erstellen.

Steve Blackwell
quelle
"In jedem Fall ist das Ausführen der App als natives Fenster mit veränderbarer Größe eine viel einfachere Möglichkeit, mehrere Bildschirmgrößen zu testen, als eine Reihe von AVDs zu erstellen." Gut gesagt. Eigentlich versuche ich, an diesem Phänomen zu arbeiten, um maximale Kompatibilität zu gewährleisten, anstatt unterschiedliche Bildschirmgrößen mit unterschiedlichen Texturgrößen zu erreichen.
Rafay
3

Mit der ResolutionFileResolver- Klasse können Sie Dateinamen in der besten Auflösung auflösen. Ich glaube, dass dies auch mit verschiedenen Seitenverhältnissen funktioniert, vorausgesetzt, Sie haben Sprites für diese Seitenverhältnisse erstellt. Es gibt ein Beispiel für die Verwendung im AssetManagerTest .

Doran
quelle
0

Ich verwende diese Methode von http://blog.acamara.es/2012/02/05/keep-screen-aspect-ratio-with-different-resolutions-using-libgdx/

Ich habe diese Funktion erstellt, die den berührten Punkt auf dem Bildschirm zurückgibt und auf die gewünschte Spielposition verkleinert.

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

Verwenden Sie dies beim Aufsetzen / Hoch / Ziehen und Sie können in Ihrem Spiel nach Berührungen suchen.

Boldijar Paul
quelle
Dies scheint der unprojektierten Methode der Kamera ähnlich zu sein. Tut es das Gleiche?
Milosmns
@milosmns das ist die Art, wie ich es vor 1 Jahr gemacht habe, gut, ich habe das Unprojekt herausgefunden. Es ist gut, wenn deine Kamera immer in der gleichen Position ist.
Boldijar Paul
0

Dieser Code funktioniert für mich mit dem neuesten Update: * OrthographicCamera ist eine Kamera, dies macht kein Zuschneiden, ändert nur das Ansichtsfenster, sodass die Breite immer noch "so oft" größer ist als das tatsächliche Fenster / Gerät

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
quelle