Tag 2 – Bilder malen

veröffentlicht am: 2. Dezember 2012

Willkommen zurück,

heute wollen wir unser „Spiel“ Bilder malen lassen. Dafür müssen wir zuerst feststellen, dass libgdx mit OpenGL rendert und somit die Bildgrößen beides Potenzen von 2 sein (damit die Graphikkarte das Bild schneller verarbeiten kann). Da es nun auf die Weihnachtszeit zugeht, habe ich mir ein freies Bild von einem Baum geholt (openclipart.org) und dieses in ein 512px * 1024px großes transparentes png gepackt.


Um das Bild jetzt zu laden, muss man es in den assets-Ordner speichern (der im testgame-android Projekt zu finden ist). Ich bevorzuge dort einen Unterordner graphics anzulegen (Name ist total egal).

Texture tree;
// [...] in der create()-Methode:
tree=new Texture(Gdx.files.internal("graphics/tree.png"));

Mit Gdx.files.internal() kann man Dateien aus dem assets lesen und weiterverarbeiten.

Wichtig ist noch, dass man alle Texturen, die man anlegt am Ende in dispose() wieder wegwerfen.

@Override
public void dispose() {
	tree.dispose();
}

Jetzt haben wir ein Objekt, in dem unsere Textur gespeichert wird, aber wir wollen sie auch noch malen. Dazu brauchen wir einen SpriteBatch. Vereinfacht gesagt ist ein SpriteBatch eine mysteriöse Box, die unsere Texturen sammelt, verarbeitet und zur Graphikkarte schickt. Wir erzeugen in der create()-Methode also ein neues SpriteBatch und werfen es in dispose() wieder weg.
Um die Textur jetzt zu malen, müssen wir folgendes schreiben:

batch.begin();
batch.draw(texture,0,0);
batch.end();

zwischen begin() und end() kann man dann alle Texturen malen, die man malen will.

Wie man sieht, ist ein großer Teil des Bildschirmes leer, da der Baum in der oberen linken Ecke des Bildes gemalt ist. Wir wollen also nur einen Teil des Bildes malen und dafür gibt es TextureRegions.

Beim erstellen der TextureRegion übergibt man die Textur (in unserem Fall tree) und den Ausschnit des Bildes, den diese Region enthalten soll (in unserem Fall startet sie bei 0,0 und hat eine Größe von 450*730).

TextureRegion christmasTree;
//[...]
christmasTree=new TextureRegion(tree, 0, 0, 450,730);
//[...]
batch.draw(christmasTree,0,0);

Aber welche Auflösung hat dieses Ding denn jetzt? also wo malen wir unseren Baum hin und wie packen wir ihn ganz ins Bild?

Standardmäßig nutzt libGDX die Auflösung, die das Fenster ursprünglich hat, als Auflösung für die sichtbare Fläche.
Wenn das Fenster dann vergrößert wird, bleibt die Auflösung der Zeichenfläche trotzdem noch die selbe und somit wird das Bild verzerrt.

Falls man die Auflösung der Zeichenfläche ändern will, nutzt man eine OrthographicCamera.

OrthographicCamera camera;
// [...] in create():
camera=new OrthographicCamera();
camera.setToOrtho(false, 800,480); // true, falls die y-Achse nach unten zeigen soll
// [...] in render():
batch.setProjectionMatrix(camera.combined); // sagt dem Batch, wo er zu malen hat
batch.begin();

Oder man setzt erst in der Methode resize()

camera.setToOrtho(false,width,height);

Wenn man jetzt das Fenster größer zieht, bekommt man mehr vom Bild zu sehen, da sich die Auflösung der Zeichenfläche automatisch anpasst, wenn das Fenster vergrößert wird.

Ich bin mir noch nicht sicher, welche der Methoden die bessere ist, aber für den Anfang werde ich die erste Methode nutzen, weil sie einfacher ist. Dabei werde ich 800×480 als feste Auflösung nutzen, da mein Smartphone diese Auflösung hat.
Die Standartgröße des Fensters der Desktop-version kann man einfach in der Main-Klasse ändern.

public class Main {
	public static void main(String[] args) {
		LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
		cfg.title = "bitowls adventcalendar game";
		cfg.useGL20 = false;
		cfg.width = 800;
		cfg.height = 480;
		
		new LwjglApplication(new TestGame(), cfg);
	}
}

Das Koordinatensystem, in das wir malen, fängt links unten an:

Ich hab nochmal einen Ball (openclipart.org) hinzugefügt und den Baum an die Bildschirmhöhe angepasst (momentan nutze ich dafür eine batch.draw() Methode, die auch zielbreite und zielhöhe akzeptiert, ich sollte aber später das Bild verkleinern).

Der volle Code:

public class TestGame implements ApplicationListener {
	
	TextureRegion christmasTree;
	Texture ball;
	SpriteBatch batch;
	OrthographicCamera camera;
	
	@Override
	public void create() {
		// load assets
		Texture tree=new Texture(Gdx.files.internal("graphics/tree.png"));
		christmasTree=new TextureRegion(tree, 0, 0, 450,730);
		ball=new Texture(Gdx.files.internal("graphics/ball.png"));
		
		batch=new SpriteBatch();
		
		// create viewport
		camera=new OrthographicCamera();
		camera.setToOrtho(false, 800,480);
	}

	@Override
	public void render() {		
		Gdx.gl.glClearColor(1,1,1, 1);
		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
		
		// render our images
		batch.setProjectionMatrix(camera.combined);
		batch.begin();
		batch.draw(christmasTree,0,0,450/(730.0f/480.0f),480);
		batch.draw(ball,60,170);
		batch.end();
	}

	@Override
	public void resize(int width, int height) {
	}

	@Override
	public void pause() {
	}

	@Override
	public void resume() {
	}
	
	@Override
	public void dispose() {
		// dispose all the trash :P
		christmasTree.getTexture().dispose();
		ball.dispose();
		batch.dispose();
	}
}

Falls es euch jetzt in den Fingern juckt zu programmieren, habe ich ein paar einfache Aufgaben:

  • den Baum mit der Kugel in die mitte des Bildschirms bringen
  • die Kugel öfters an verschiedenen Stellen des Baumes rendern
  • neue Texturen (Kerzen, anderer Schmuck) laden und auch diese an den Baum rendern

Morgen werden wir uns dann mit Userinput (Touch/Maus, Tastatur) beschäftigen.

Schreibe Vorschläge, Beschwerden, gefundene Fehler oder Fragen in die Kommentare!

geposted in libgdx

4 Antworten zu “Tag 2 – Bilder malen”

  1. Katoffel sagt:

    „batch.begin();
    bath.draw(texture,0,0);// ich glaub hir ist ein Fail… 😛
    batch.end();“

    Was is das für ein AWESOME hintergrund ? also dein desktop ?
    Und TextureRegions sind dazu da die PowerOf2 zu umgehen ?

    mfG Katoffel

    • bitowl sagt:

      Mein Hintergrundbild ist von http://www.blirk.net/space-wallpaper/39/.
      Und ja, mit TextureRegions kann man diese Beschränkung umgehen, da nur die Textur eine Größe von x² y² haben muss, mit TextureRegions aber quasi die Texturkoordinaten auf den zwei Dreiecken so geändert werden, dass man nur einen Teil des Bildes sieht.
      bitowl

  2. derSchotte sagt:

    Hallo bitowl,

    Du musst nicht alle Texturen mit Potenzen von 2 verwenden. Wenn du in der Desktop- sowie Android-Main cfg.useGL20 = true; setzt dann ist es egal welche breite, höhe die Texturen haben.

    Währe es nicht Sinnvoller eine Resources.java anzulegen in der alle Texturen gesetzt werden? Anstelle diese direkt in die einzelnen Dateien ein zu binden. Erleichtert später das ändern von einzelnen Texturen.

    Lerne derzeit selber Java und LibGDX.

    grüße derSchotte

    • bitowl sagt:

      Heyho,
      das mit cfg.useGL20 wusste ich noch nicht, allerdings ist das egal, wenn man später TextureAtlanten nutzt, um die Texturen speichert.
      Wenn man dann alle Daten mit dem AssetsManager schon vorher lädt, braucht man auch keine Resources.java mehr.

      grüße
      bitowl

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.