Mängu näide¶
Teeme läbi mõned lihtsad näited. See materjal on loodud inglise keelsete juhendite baasil.
Loome uue projekti libGDX setup tööriistaga. Kui soovid järgnevaid koodijuppe muutmata kopeerida, siis veendu, et paketi nimi ja mänguklass oleksid samasugused nagu pildil:

Projekti loomine¶
Intellij’s projekti avamiseks:
File -> Open -> leia kataloog, mille seadsid just destination’iks -> vali seest build.gradle fail -> OK -> Open as project.
Mänguaknale kuvamine¶
Ava projektis
\core\src\ee.taltech.iti0301.libgdxdemo\libgdxDemo.java
klass.
Kopeeri sellesse järgnev kood ja käivita klassis
desktop\src\ee\taltech\iti0301\libgdxdemo\DesktopLauncher.java
asuv
main
meetod, et joonistada ring:
package ee.taltech.iti0301.libgdxdemo;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
public class libgdxDemo extends ApplicationAdapter {
ShapeRenderer shapeRenderer;
@Override
public void create () {
shapeRenderer = new ShapeRenderer();
}
@Override
public void render () {
Gdx.gl.glClearColor(.25f, .25f, .25f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
shapeRenderer.setColor(0, 1, 0, 1);
shapeRenderer.circle(200, 100, 75);
shapeRenderer.end();
}
@Override
public void dispose () {
shapeRenderer.dispose();
}
}
Create()
kutsutakse välja vaid ühe korra, kui mäng käima pannakse.
Render()
kutsutakse välja mitu korda sekundis, et muuta, mida
mänguaknale kuvatakse.
Dispose()
kutsutakse välja, kui mängust lahkutakse.
ShapeRenderer
klassi kasutame, et saada ligipääs lihtsatele
joonistamisfunktsioonidele.
Nähtavasti määrame tausta värvuse, kujundi tüübi (seest täidetud ehk
Filled
), kujundi värvuse, kujundi liigi (ring) koos selle
koordinaatide ja raadiusega.
Pane tähele, et (0; 0) koordinaat asub libGDX puhul ekraani (mänguakna) alumises vasakus nurgas!
Animatsioonid¶
Hoiustame stseeni kirjeldavaid olekuid muutujates
Kasutame neid iga kaadri puhul stseeni uuesti joonistamiseks
Uuendame muutujate väärtusi ajas, et viia stseenil läbi muudatusi
Siinpuhul circleX
ja circleY
hoiavad endas ringi koordinaate
ning xSpeed
ja ySpeed
hoiavad ringi horisontaalset/vertikaalset
kiirust. Kui ringi keskpunkt jõuab mänguakna serva, siis muudetakse
vastav kiirus vastassuunaliseks, et ring alati mänguakna sisse jääks.
Täiendatud kood:
package ee.taltech.iti0301.libgdxdemo;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
public class libgdxDemo extends ApplicationAdapter {
ShapeRenderer shapeRenderer;
float circleX = 200;
float circleY = 100;
float xSpeed = 2;
float ySpeed = 1;
@Override
public void create () {
shapeRenderer = new ShapeRenderer();
}
@Override
public void render () {
circleX += xSpeed;
circleY += ySpeed;
if (circleX < 0 || circleX > Gdx.graphics.getWidth()) {
xSpeed *= -1;
}
if (circleY < 0 || circleY > Gdx.graphics.getHeight()) {
ySpeed *= -1;
}
Gdx.gl.glClearColor(.25f, .25f, .25f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
shapeRenderer.setColor(0, 1, 0, 1);
shapeRenderer.circle(circleX, circleY, 75);
shapeRenderer.end();
}
@Override
public void dispose () {
shapeRenderer.dispose();
}
}
Delta Time¶
Koodil on üks puudus - me ei saa määrata, kui tihti render()
meetodit välja kutsutakse. Nii töötaks mäng aeglasemalt või kiiremini
olenevalt seadmest, mille peal seda jooksutatakse, ehk täpsemalt selle
seadme kaadrisagedusest (frame rate). Mäng peaks aga töötama samal
kiirusel olenemata seadmest.
Selleks peab ringi liikumine sõltuma ajast, mis on möödunud viimasest kaadrist. Kui on möödunud väga vähe aega, peaks ka ring olema liikunud väga vähe. Kui aga on möödunud rohkem aega, peaks ring olema liikunud kaugemale.
LibGDX pakub lahendusena Gdx.graphics.getDeltaTime()
meetodit, mis
tagastab viimasest kaadrist möödunud aja. Tähistab protsenti ühest
sekundist, nt 0.5 on pool sekundit.
Paneme xSpeed
ja ySpeed
tähistama vahemaad (pikslites), mis
liigutakse sekundis:
float xSpeed = 120;
float ySpeed = 60;
Nüüd peaks ring liikuma horisontaalselt 120 pikslit sekundis, vertikaalselt 60 pikslit sekundis.
Korrutame need väärtused aja muuduga, et saada läbitav vahemaa praeguse kaadri jaoks:
circleX += xSpeed * Gdx.graphics.getDeltaTime();
circleY += ySpeed * Gdx.graphics.getDeltaTime();
Nüüd liigub ring sama kiirusega, olenemata seadme kaadrisagedusest.
Pollimine¶
Kasutaja sisendit saab kõige lihtsamini kätte sisendseadmete pollimisega. Mõned olulisemad meetodid:
Gdx.input.isTouched() tagastab true kui kasutaja klõpsab hiirega aknale. Töötab ka puutetundlike ekraanide puhul
Gdx.input.getX() tagastab hiireklõpsu või ekraanipuudutuse x-koordinaadi
Gdx.input.getY() tagastab hiireklõpsu või ekraanipuudutuse y-koordinaadi. NB! OpenGL puhul on y-koordinaadi väärtus 0 hoopis ekraani alumises servas, mistõttu tuleb korrektseks asukoha määramiseks lahutada hiireklõpsu y-koordinaat akna kõrgusest:
Gdx.graphics.getHeight() - Gdx.input.getY()
Gdx.input.isKeyPressed() tagastab, kas etteantud klaviatuuri klahvi vajutatakse. Näiteks
Gdx.input.isKeyPressed(Input.Keys.W)
kontrollib, kas W klahvi hoitakse praegu all.
Asenda kood järgnevaga ja proovi nii hiire klõpsu kui ka WASD klahve:
package ee.taltech.iti0301.libgdxdemo;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
public class libgdxDemo extends ApplicationAdapter {
ShapeRenderer shapeRenderer;
float circleX = 200;
float circleY = 100;
@Override
public void create() {
shapeRenderer = new ShapeRenderer();
}
@Override
public void render() {
if (Gdx.input.isTouched()) {
circleX = Gdx.input.getX();
circleY = Gdx.graphics.getHeight() - Gdx.input.getY();
}
if(Gdx.input.isKeyPressed(Input.Keys.W)){
circleY++;
}
else if(Gdx.input.isKeyPressed(Input.Keys.S)){
circleY--;
}
if(Gdx.input.isKeyPressed(Input.Keys.A)){
circleX--;
}
else if(Gdx.input.isKeyPressed(Input.Keys.D)){
circleX++;
}
Gdx.gl.glClearColor(.25f, .25f, .25f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
shapeRenderer.setColor(0, 1, 0, 1);
shapeRenderer.circle(circleX, circleY, 75);
shapeRenderer.end();
}
@Override
public void dispose() {
shapeRenderer.dispose();
}
}
Input Events¶
Pollimine töötab püsiva sisendi puhul hästi (nt hiirekursori asukoha jälitamine), kuid võib olla tülikas kasutaja sisendile reageerimise puhul (klõpsamine, klahvivajutus, ekraanipuudutus). Nendes olukordades tuleks kasutada sisendsündmuste töötlejat (input event handler).
Proovi näidet:
package ee.taltech.iti0301.libgdxdemo;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.MathUtils;
public class libgdxDemo extends ApplicationAdapter {
ShapeRenderer shapeRenderer;
float r = MathUtils.random();
float g = MathUtils.random();
float b = MathUtils.random();
@Override
public void create () {
shapeRenderer = new ShapeRenderer();
Gdx.input.setInputProcessor(new InputAdapter() {
@Override
public boolean keyTyped (char key) {
r = MathUtils.random();
g = MathUtils.random();
b = MathUtils.random();
return true;
}
@Override
public boolean touchDown (int x, int y, int pointer, int button) {
r = MathUtils.random();
g = MathUtils.random();
b = MathUtils.random();
return true;
}
});
}
@Override
public void render () {
Gdx.gl.glClearColor(r, g, b, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
}
@Override
public void dispose () {
shapeRenderer.dispose();
}
}
Lõime InputAdapter
’i, milles keyTyped()
ja touchDown()
meetodid, mis seavad muutujad r
, g
ja b
suvalisteks
väärtusteks. KeyTyped
on klaviatuuri klahvide ning touchDown
hiirenuppude jaoks. Nii muudab render()
meetod nende muutujate
põhjal tausta värvust, kui tuvastatakse hiireklõps või klahvivajutus.
NB!
Ehk märkasite, et mõned klaviatuuri klahvid (nt nooleklahvid) ei
tee midagi. Seda seetõttu, et keyTyped
toimib ainult siis, kui see
klahv genereerib Unicode sümbolit. Kui asendate koodis
keyTyped (char key)
ära keyDown (int keycode)
-ga, siis avaldavad
mõju ka teised klahvid.

Sisendsündmused¶
Terviklikul mängul on üldjuhul olemas: - tiitellehe vaade (nt mängujuhiste ja play nupuga); - sätete/valikute vaade (nt heli vaigistamise valikuga); - mängu mängimise vaade; - mängu lõppemise vaade (informeerib võidust/kaotusest ja laseb uuesti alustada).
Vaateid võib olla rohkemgi.
Proovime esmalt ilma libGDX’ita¶
Järgnevat kehva struktuuriga lahendust ei tohiks keerukamate mängude puhul kasutada, kuid see annab parema arusaama libGDX abil sama asja tegemisest.
Proovi koodi:
package ee.taltech.iti0301.libgdxdemo;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Vector2;
public class libgdxDemo extends ApplicationAdapter {
enum Screen{
TITLE, MAIN_GAME, GAME_OVER
}
Screen currentScreen = Screen.TITLE;
SpriteBatch batch;
ShapeRenderer shapeRenderer;
BitmapFont font;
float circleX = 300;
float circleY = 150;
float circleRadius = 50;
float xSpeed = 4;
float ySpeed = 3;
@Override
public void create () {
batch = new SpriteBatch();
shapeRenderer = new ShapeRenderer();
font = new BitmapFont();
Gdx.input.setInputProcessor(new InputAdapter() {
@Override
public boolean keyDown (int keyCode) {
if(currentScreen == Screen.TITLE && keyCode == Input.Keys.SPACE){
currentScreen = Screen.MAIN_GAME;
}
else if(currentScreen == Screen.GAME_OVER && keyCode == Input.Keys.ENTER){
currentScreen = Screen.TITLE;
}
return true;
}
@Override
public boolean touchDown (int x, int y, int pointer, int button) {
if(currentScreen == Screen.MAIN_GAME){
int renderY = Gdx.graphics.getHeight() - y;
if(Vector2.dst(circleX, circleY, x, renderY) < circleRadius){
currentScreen = Screen.GAME_OVER;
}
}
return true;
}
});
}
@Override
public void render () {
if(currentScreen == Screen.TITLE){
Gdx.gl.glClearColor(0, .25f, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
font.draw(batch, "Title Screen!", Gdx.graphics.getWidth()*.25f, Gdx.graphics.getHeight() * .75f);
font.draw(batch, "Click the circle to win.", Gdx.graphics.getWidth()*.25f, Gdx.graphics.getHeight() * .5f);
font.draw(batch, "Press space to play.", Gdx.graphics.getWidth()*.25f, Gdx.graphics.getHeight() * .25f);
batch.end();
}
else if(currentScreen == Screen.MAIN_GAME) {
circleX += xSpeed;
circleY += ySpeed;
if (circleX < 0 || circleX > Gdx.graphics.getWidth()) {
xSpeed *= -1;
}
if (circleY < 0 || circleY > Gdx.graphics.getHeight()) {
ySpeed *= -1;
}
Gdx.gl.glClearColor(0, 0, .25f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
shapeRenderer.setColor(0, 1, 0, 1);
shapeRenderer.circle(circleX, circleY, 75);
shapeRenderer.end();
}
else if(currentScreen == Screen.GAME_OVER){
Gdx.gl.glClearColor(.25f, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
font.draw(batch, "You win!", Gdx.graphics.getWidth()*.25f, Gdx.graphics.getHeight() * .75f);
font.draw(batch, "Press enter to restart.", Gdx.graphics.getWidth()*.25f, Gdx.graphics.getHeight() * .25f);
batch.end();
}
}
@Override
public void dispose () {
shapeRenderer.dispose();
}
}
Lõime tiitellehe
, mängu vaate
ja mängu lõpu vaatega
mängu.
Kasutame selles enum
’it, et kirjeldada kolme võimalikku vaadet.
Kontrollime, missugune vaade on parasjagu aktiivne ja otsustame selle
põhjal, mis vaade teha aktiivseks klahvide või hiirenupu vajutuste järel
(vt create()
meetodit) kui ka seda, mida täpsemalt ekraanile kuvada
(vt render()
meetodit).

Kolme vaatega mäng¶
Kuna keerulisema mängu loomiseks pole see adekvaatne lahendus, siis teeme nüüd elu lihtsamaks libGDX abil.
Teeme LibGDX’iga¶
LibGDX aitab hoida vaateid (screens) eraldi klassides ja ressursse nende
vahel jagada Game
klassi ja Screen
liidese abil.
Üldjuhul sisaldab libGDX’i projekt ühte klassi, mis laiendab
Game
’i, ning mitut klassi, mis implementeerivad Screen
’i.
Igale mängus eksisteerivale vaatele vastab klass.

Struktuur¶
Asenda eelmine näide järgnevaga:
package ee.taltech.iti0301.libgdxdemo;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
public class libgdxDemo extends Game {
SpriteBatch batch;
ShapeRenderer shapeRenderer;
BitmapFont font;
@Override
public void create () {
batch = new SpriteBatch();
shapeRenderer = new ShapeRenderer();
font = new BitmapFont();
setScreen(new TitleScreen(this));
}
@Override
public void dispose () {
batch.dispose();
shapeRenderer.dispose();
font.dispose();
}
}
Pane tähele, et nüüd meie libgdxDemo
laiendab Game
’i ja ei
sisalda mänguloogikat ega render()
osa.
Meile huvipakkuv osa koodist on
setScreen(new TitleScreen(this));
kus setScreen()
päritakse Game
klassist ja võimaldab meil
vahetada erinevate vaadete vahel. Luuakse TitleScreen
ja edastatakse
libgdxDemo
’i viide konstruktorisse this
võtmesõna abil.
Loo libgdxDemo
’ga samasse kataloogi TitleScreen
klass järgneva
sisuga:
package ee.taltech.iti0301.libgdxdemo;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.GL20;
public class TitleScreen extends ScreenAdapter{
libgdxDemo game;
public TitleScreen(libgdxDemo game) {
this.game = game;
}
@Override
public void show() {
Gdx.input.setInputProcessor(new InputAdapter() {
@Override
public boolean keyDown(int keyCode) {
if (keyCode == Input.Keys.SPACE) {
game.setScreen(new GameScreen(game));
}
return true;
}
});
}
@Override
public void render(float delta) {
Gdx.gl.glClearColor(0, .25f, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
game.batch.begin();
game.font.draw(game.batch, "Title Screen!", Gdx.graphics.getWidth() * .25f, Gdx.graphics.getHeight() * .75f);
game.font.draw(game.batch, "Click the circle to win.", Gdx.graphics.getWidth() * .25f, Gdx.graphics.getHeight() * .5f);
game.font.draw(game.batch, "Press space to play.", Gdx.graphics.getWidth() * .25f, Gdx.graphics.getHeight() * .25f);
game.batch.end();
}
@Override
public void hide() {
Gdx.input.setInputProcessor(null);
}
}
See klass laiendab ScreenAdapter
’it (ehk implementeerib
Screen
’i, ilma et peaks defineerima igat võimalikku meetodit) ja
sisaldab konstruktorit, mis hoiustab libgdxDemo
objekti, mille äsja
edastasime.
Show()
meetod kutsutakse automaatselt välja, kui antud vaade ehk
tiitelleht (TitleScreen
) saab praegu kuvatavaks vaateks. Selles
kontrollitakse tühiku ehk space klahvi vajutamist. Seda vajutades saab
vaateks GameScreen
vaade.
Render()
meetodit kutsutakse korduvalt välja (tavaliselt 60 kaadrit
sekundis) nii kaua, kuni antud vaade on praegu kuvatav vaade. See
kuvabki tiitellehe elemendid. Seejuures kasutame libgdxDemo
klassis
loodud batch
’i ja font
’i, et need saaks loodud vaid korra
ühes klassis ehk ei peaks korduma mitme vaate puhul.
Hide()
meetod kutsutakse välja, kui mängus hakatakse kuvama teist
vaadet. See eemaldab show()
meetodis loodud InputProcessor
’i,
et ei võetaks vastu uusi sisendeid selle vaate mõjutamiseks.
Lisa nüüd ka GameScreen
klass, mille sisuks:
package ee.taltech.iti0301.libgdxdemo;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Vector2;
public class GameScreen extends ScreenAdapter {
libgdxDemo game;
float circleX = 300;
float circleY = 150;
float circleRadius = 50;
float xSpeed = 4;
float ySpeed = 3;
public GameScreen(libgdxDemo game) {
this.game = game;
}
@Override
public void show() {
Gdx.input.setInputProcessor(new InputAdapter() {
@Override
public boolean touchDown(int x, int y, int pointer, int button) {
int renderY = Gdx.graphics.getHeight() - y;
if (Vector2.dst(circleX, circleY, x, renderY) < circleRadius) {
game.setScreen(new EndScreen(game));
}
return true;
}
});
}
@Override
public void render(float delta) {
circleX += xSpeed;
circleY += ySpeed;
if (circleX < 0 || circleX > Gdx.graphics.getWidth()) {
xSpeed *= -1;
}
if (circleY < 0 || circleY > Gdx.graphics.getHeight()) {
ySpeed *= -1;
}
Gdx.gl.glClearColor(0, 0, .25f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
game.shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
game.shapeRenderer.setColor(0, 1, 0, 1);
game.shapeRenderer.circle(circleX, circleY, 75);
game.shapeRenderer.end();
}
@Override
public void hide() {
Gdx.input.setInputProcessor(null);
}
}
Siin sisaldub juba eelnevast tuttav mänguloogika, mis paneb ringi mööda
mänguakent ringi liikuma. Muu on analoogiline TitleScreen
klassiga.
Kui kasutaja vajutab ringile, siis muudame me aktiivseks vaateks
EndScreen
’i.
Loo nüüd viimane ehk EndScreen
klass järgneva sisuga:
package ee.taltech.iti0301.libgdxdemo;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.GL20;
public class EndScreen extends ScreenAdapter {
libgdxDemo game;
public EndScreen(libgdxDemo game) {
this.game = game;
}
@Override
public void show() {
Gdx.input.setInputProcessor(new InputAdapter() {
@Override
public boolean keyDown(int keyCode) {
if (keyCode == Input.Keys.ENTER) {
game.setScreen(new TitleScreen(game));
}
return true;
}
});
}
@Override
public void render(float delta) {
Gdx.gl.glClearColor(.25f, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
game.batch.begin();
game.font.draw(game.batch, "You win!", Gdx.graphics.getWidth() * .25f, Gdx.graphics.getHeight() * .75f);
game.font.draw(game.batch, "Press enter to restart.", Gdx.graphics.getWidth() * .25f, Gdx.graphics.getHeight() * .25f);
game.batch.end();
}
@Override
public void hide() {
Gdx.input.setInputProcessor(null);
}
}
Saadud tulemus on identne eelmise näite tulemusega, mille lõime ilma libGDX’ita, kuid pakub mõistlikumat klasside struktuuri ning keerukamate mängude puhul ka lühemat ja lihtsamini mõistetavat koodi.
Atlas-fail¶
Atlas-fail on failiformaat, mis võimaldab koondada erinevaid pildifaile (PNG, JPEG, BMP, GIF, TIFF, WEBP) üheks suureks Texture
’iks. Atlas-faili eri formaatidega faile pannes muudetakse kõik need staatilisteks (PNG/JPEG). Tasub küll tähele panna, et GIF-failid salvestatakse ainult ühe kaadrina.
Texture
sisaldab pildi metaandmeid ning üht Texture
'it laaditakse arvutimällu ainult ühe korra, initsialiseerimise ajal. Kui kõigil failidel on oma Texture
, tuleb iga Texture
vahetamisel (näiteks animatsioonides või ühte pilti teisega asendades) siduda Texture
CPU-ga ehk bind
'ida.
Lisaks kasutatakse Texture
’i joonistamiseks kas SpriteBatch
'i (mitme pildi korraga joonistamine) või Sprite
’i (üksiku pildi joonistamine), mis ka vajavad igal joonistamisel bind
'imist.
Atlas-fail võimaldab efektiivsemat mälukasutust ning teeb ka failihalduse lihtsamaks.
Mitmest pildifailist Atlas-faili loomine¶
Aseta kõik failid, mida soovid atlas-faili koondada, ühte kausta (näites
powerupFly
kaust).

Leia
build.gradle
faillwjgl3
projektist ning lisatasks.register
meetodite juurde järgmised read:

tasks.register("runTexturePacker", JavaExec) {
main = "tools.TexturePackerTool"
classpath = sourceSets.main.runtimeClasspath
args = []
}
PS! Vanem JDK versioon eelistab
task runTexturePacker(type: JavaExec) {
main = "tools.TexturePackerTool"
classpath = sourceSets.main.runtimeClasspath
args = []
}
3. Loo tools
package ning klass TexturePackerTool
ja kopeeri sinna järgnev kood, kus asenda soovi korral:
- assets/powerupFly
oma pildikausta nimega.
- assets/packed
soovitud pildikausta nimega.
- packFileName
soovitud atlas-faili nimega.
package tools;
import com.badlogic.gdx.tools.texturepacker.TexturePacker;
public class TexturePackerTool {
public static void main(String[] args) throws Exception {
String inputDir = "assets/powerupFly";
String outputDir = "assets/packed";
String packFileName = "powerupFly";
TexturePacker.process(inputDir, outputDir, packFileName);
}
}
Jooksuta
TexturePackerTool
klassi.Sulle tekib
packed
kaust (kus on.atlas
ja.png
laiendusega failid). PNG-faili on koondatud kõik pildid ning atlas-failis on info, mis koordinaatidel eri PNG-failid asuvad.

![]() |
![]() |
Järgnev kood näitab, kuidas otsida atlas-faili abil kindel piirkond (näites ämbri PNG ehk bucket
) ning kuidas (näites tingimuslikult) muuta bucketSprite
’i pilt.
Loome enne konstruktorit kaks uut muutujat:
boolean canFly = false;
float canFlyTimer = 0;
canFly
väärtuse muudame true
ks kui bucket
välgunoolega kokku puutub.
input
meetodis määrame kolmanda vajaliku muutuja ning see on ka meetod, kus määrame, mis klaviatuuri klahvidega on võimalik bucket
-it liigutada.
float delta = Gdx.graphics.getDeltaTime();
logic
meetodis paneme canFly
muutuja sõltuma canFlyTimer
-ist ning canFlyTimer
-i sõltuma delta
muutujast. Siin meetodis anname bucket
muutujale võime ka liikuda vertikaalselt ehk input
meetodis saab mängija klaviatuuril kahte nuppu lisaks vajutada. Me ei anna seda võimet aga igaveseks vaid kaheks sekundiks ning canFlyTimer
peab järge aja möödumise üle. Kui canFlyTimer
nulli jõuab, siis määrame canFly
oleku jälle false
-iks. delta
muutuja tagastab meile kui palju aega möödus viimase kaadri renderdamisest.
public class Main implements ApplicationListener {
private SpriteBatch spriteBatch;
private TextureAtlas textureFlyAtlas;
private Rectangle bucketRectangle;
private Sprite bucketSprite;
private TextureAtlas.AtlasRegion bucketRegion;
private float canFlyTimer = 0;
private boolean canFly = false;
@Override
public void create() {
textureFlyAtlas = new TextureAtlas("assets/packed/powerupFly.atlas");
bucketRegion = textureFlyAtlas.findRegion("bucket");
bucketSprite = new Sprite(bucketRegion);
bucketSprite.setSize(1, 1);
bucketSprite.setPosition(42, 0);
bucketRectangle = new Rectangle();
}
private void logic() {
if (canFly) {
canFlyTimer -= delta;
if (canFlyTimer <= 0) {
bucketSprite.setRegion(bucketRegion);
}
}
}
@Override
public void dispose() {
textureFlyAtlas.dispose();
spriteBatch.dispose();
}
}
Mitmest pildifailist animatsiooni loomine TextureAtlasega¶
Aseta kõik failid, mida soovid atlas-failiga animatsiooniks teha, ühte kausta.
Leia
build.gradle
faillwjgl3
projektist ning lisatasks.register
meetodite juurde järgmised read:

tasks.register(runTexturePacker, JavaExec) {
main = tools.TexturePackerTool
classpath = sourceSets.main.runtimeClasspath
args = []
}
3.Uuenda TexturePackerTool
klassi, asendades:
assets/buckets
oma pildikausta nimega.assets/packedAnimations
soovitud pildikausta nimega.packFileName
soovitud atlas-faili nimega.

Jooksuta
TexturePackerTool
klassi.Sulle tekib
packedAnimations
kaust (kus on.atlas
ja.png
laiendusega failid). PNG-faili on koondatud kõik animatsiooni kaadrid ning atlas-failis on info, mis koordinaatidel eri PNG-failid asuvad.
Järgnev kood näitab, kuidas otsida atlas-faili abil kindel piirkond (animatsiooni kaadrid) ning kuidas seda rakendada bucketSprite
’il (näites tingimusel, kui ämber on alla kukkumas).
public class Main implements ApplicationListener {
private TextureAtlas.AtlasRegion bucketRegion;
private TextureAtlas textureAnimationAtlas;
private Array<TextureAtlas.AtlasRegion> bucketAnimationFrames;
private Rectangle bucketRectangle;
private Sprite bucketSprite;
private float animationTimer;
private int currentFrameIndex;
boolean isFalling = false;
float fallSpeed = 0;
float fallEndY = 0;
@Override
public void create() {
textureAnimationAtlas = new TextureAtlas("assets/packedAnimation/animation.atlas");
bucketAnimationFrames = new Array<>();
for (int i = 1; i <= 8; i++) {
String frameName = "bucket" + i;
bucketAnimationFrames.add(textureAnimationAtlas.findRegion(frameName));
bucketSprite = new Sprite(bucketRegion);
bucketSprite.setSize(1, 1);
bucketSprite.setPosition(42, 0);
bucketRectangle = new Rectangle();
}
@Override
public void render() {
input();
logic();
draw();
}
private void logic() {
if (isFalling) {
animationTimer += delta;
if (animationTimer >= 0.1f) {
currentFrameIndex++;
if (currentFrameIndex >= bucketAnimationFrames.size) {
currentFrameIndex = 0;
}
animationTimer = 0;
bucketSprite.setRegion(bucketAnimationFrames.get(currentFrameIndex));
}
bucketSprite.translateY(-fallSpeed * delta);
if (bucketSprite.getY() <= fallEndY) {
bucketSprite.setY(fallEndY);
isFalling = false;
bucketSprite.setRegion(bucketRegion);
}
}
}
@Override
public void dispose() {
textureFlyAtlas.dispose();
spriteBatch.dispose();
}
}