Viewportid¶
Enne viewport'idest rääkimist oleks hea peatuda korraks kaameratel, sest viewport lahendab probleeme, mis kaasnevad kaamera pildi ekraanile kuvamisega.
Kaamera¶
Mida tähendab kaamera LibGDX-s?¶
Kaamerast võib mõelda kui aknast mängu maailma. Selle kaudu saab paika panna, millist osa mängust mängija näeb (ja 3D mängude puhul ka millise nurga alt ta seda näeb).
Näide¶
Ütleme, et meil on defineeritud selline maailm:

Me asetame maailma kaamera:

Mängija näeb sellist pilti:

Milliseid probleeme kaamera lahendab?¶
Kaamera lubab eristada mängu maailma suurusi ekraani suurusest. Kuna mängu võidakse mängida eri seadmetel, millel võivad olla eri resolutsioonid, oleks tülikas mõelda mängus kasutatavatest suurustest pikslites, sest eri seadmed kuvavad eri arvu piksleid ning pikslid võivad olla eri mõõtudes.
Mugavam on defineerida mingisugused mängus kasutatavad ühikud, mis ei sõltu ekraani suurusest, vaid on seotud mängu enda loogikaga. Need võivad olla näiteks ruudud (tiles) - mängija liigub klahvi vajutamisel ühe ruudu võrra edasi (ja mitte 30 pikslit ühe seadme puhul, 46 pikslit teise ja 73 pikslit kolmanda puhul) - või meetrid, eriti kui kasutada mängu arendamisel füüsikamootoreid, mis toimetavad meetrites.
Kaamera lihtsustab ka näiteks scroll'imist, zoom'imist jms. Samuti saab kaamera abil mängu jooksutamist optimiseerida, sest pole vaja kuvada tervet mängu maailma, vaid ainult seda osa, mida mängija näeb.
Millised probleemid kaameratega kaasnevad?¶
Võtame uuesti algse näite: meil on defineeritud mingisugune maailm ning me asetame sellesse maailma kaamera. Kui nüüd resolutisooni (illustreerimise mõttes üsna ekstreemselt) muuta, siis näeb mängija sellist pilti:

Seega kui laiuse-kõrguse suhe on esialgsest erinev, kuvatakse mängijale pilt venitatuna. Selle parandamiseks on LibGDX-s olemas viewport'id.
Viewport¶
Viewport'ist võib mõelda kui lülist kaamera pildi ja ekraani vahel. See defineerib, kuidas kaamerast saadud pilt mängijale esitatakse - sisendiks on teatud mõõtmetega pilt ning viewport otsustab, kuidas seda eri resolutsioonidega seadmetele esitada. Ilma viewport'ideta peaks mängu arendaja selle sammu ise implementeerima. Viewport kuvab mängus kasutatavad ühikud ekraanile sellisel moel, et mängus toimuv näeb mängijale samasugune välja olenemata tema seadme resolutsioonist.
Milliseid probleeme viewport lahendab?¶
1. Eri laiuse-kõrguse suhete (aspect ratio) ebakõlad¶
Eri seadmete laiuse-kõrguse suhted on erinevad. Viewport tagab, et mängu maailm skaleeritakse mängijatele ilma moonutusteta.
2. Ekraani suuruse muutmine¶
Kui mängija otsustab mängu akna suurust muuta, asetab viewport automaatselt kaamera nii ümber, et mäng oleks endiselt mängitav ja ei tekiks visuaalseid ebakõlasid.
3. Mängu loogiliste ühikute piksliteks teisendamine¶
Viewport teisendab mängu loogilised ühikud automaatselt piksliteks ümber ja kuvab need korrektselt ekraanile
Ilma viewport'ideta peaks mängu arendaja ise kaamera omaduste ümber arvutamise, skaleerimise jms implementeerima. Ja seda igasuguste erinevate resolutsioonidega seadmete jaoks.
Viewport'i implementatsioonid LibGDX-s¶
LibGDX pakub erinevaid viewport'ide implementatsioone, mis töötavad erinevalt ja mida saab kasutada vastavalt mängu vajadusele. Ühes mängus võib kasutada ka mitmeid eri implementatsioone (näiteks menüü jaoks ühte ja mängu enda jaoks teist).
1. FitViewport¶
FitViewport hoiab alati laiuse-kõrguse suhte samana. Kui ekraani laiuse-kõrguse suhe ei vasta viewport'i laiuse-kõrguse suhtele, lisatakse ekraani äärtesse mustad ribad. FitViewport'i kasutatakse siis kui on ilmtingimata vajalik säilitada laiuse-kõrguse suhe.
Liiga kõrge ekraan:

Liiga lai ekraan:

FitViewporti parameetriteks on viewport'i laius ja kõrgus ning kaamera, millele seda rakendatakse:
Viewport viewport = new FitViewport(64, 32, camera);
2. FillViewport¶
FillViewport säilitab laiuse-kõrguse suhte, kuid erinevalt FitViewportist täidab ta alati terve ekraani. See tähendab seda, et osa viewport'ist endast võidakse mängija ekraanilt välja jätta.
Kõrge ekraan:

Lai ekraan:

FillViewporti parameetriteks on viewport'i laius ja kõrgus ning kaamera, millele seda rakendatakse:
Viewport viewport = new FillViewport(64, 32, camera);
3. StretchViewport¶
StretchViewport venitab mängu maailma selliselt, et see täidaks terve ekraani. Laiuse-kõrguse suhet ei säilitata ning pilt moonutub teatud erinevate resolutsioonide puhul.Kasutatakse sel juhul, kui on kindlasti vaja et pilt täidaks terve ekraani ning ei hoolita moonutustest, mis venitamisega kaasnevad.
Liiga kõrge ekraan:

Liiga lai ekraan:

StretchViewporti parameetriteks on viewport'i laius ja kõrgus ning kaamera, millele seda rakendatakse:
Viewport viewport = new StretchViewport(64, 32, camera);
4. ExtendViewport¶
ExtendViewport säilitab laiuse-kõrguse suhte ning lisab ekraani äärtesse mängu maailma juurde. Mängijad, kellel on laiem ekraan, näevad rohkem mängu maailma kui teised mängijad. Kasutatakse sel juhul kui tahetakse säilitada laiuse-kõrguse suhe ning ei soovita moonutusi või tühja ruumi ekraani äärtes.
Kõrge ekraan:

Lai ekraan:

ExtendViewporti parameetriteks on viewport'i laius ja kõrgus ning kaamera, millele seda rakendatakse:
Viewport viewport = new ExtendViewport(64, 32, camera);
5. ScreenViewport¶
ScreenViewport teisendab kaamera koordinaadid üks-ühele ekraani koordinaatideks ümber ilma mingisuguse skaleerimise või laiuse-kõrguse muutmiseta. Kasutatakse näiteks UI renderdamiseks või kui on vaja üks-ühele teisendust mängu ühikute ja pikslite vahel.
Kuna mängu maailma laius ja kõrgus on antud juhul defineeritud mängu ühikutena, aga ScreenViewport teisendab ühikuid pikslitena, ilmub maailm väga väiksena ekraani servas:

ScreenViewporti ainsaks parameetriks on kaamera, millele seda rakendatakse:
Viewport viewport = new ScreenViewport(camera);
6. ScalingViewport¶
ScalingViewport lubab spetsiifilist viewport'i kohandamist. Kasutatakse mängude puhul, kus eelnevad meetodid ei tööta.
ScalingViewporti parameetriteks on skaleerimis-strateegia, viewport'i laius ja kõrgus ning kaamera, millele seda rakendatakse:
Viewport viewport = new ScalingViewport(Scaling.fit, 64, 32, camera);
Siin saab katsetada, kuidas erinevad viewport'i implementatsioonid välja näevad:¶
Viewport'ide kasutamine LibGDX-s¶
Tavaliselt luuakse esiteks kaamera ning seejärel viewport, millele antakse kaasa vajalikud parameetrid - enamasti viewport'i laius ja kõrgus ning kaamera, millele seda rakendatakse.
Viewport'i tuleb kindlasti resize meetodis uuendada.
1. Loo kaamera ja viewport¶
OrthographicCamera camera = new OrthographicCamera();
Viewport viewport = new FitViewport(64, 32, camera);
2. Kirjuta üle resize meetod¶
@Override
public void resize(int width, int height) {
viewport.update(width, height);
}
Lihtne näide:¶
package io.github.camera_and_viewports;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.utils.viewport.ExtendViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
public class Main extends ApplicationAdapter {
private OrthographicCamera camera;
private SpriteBatch batch;
private Sprite theWorld;
private Texture image;
private Viewport viewport;
private float WORLD_WIDTH = 64;
private float WORLD_HEIGHT = 36;
/**
* Creates a sprite batch and a sprite with the given background image which
* it positions in the bottom left corner of the screen.
* Creates camera and a viewport and applies the viewport to the camera.
* Positions the camera in the middle of the screen.
*/
@Override
public void create () {
batch = new SpriteBatch();
image = new Texture(Gdx.files.internal("background.jpg"));
theWorld = new Sprite(image);
theWorld.setPosition(0, 0);
theWorld.setSize(WORLD_WIDTH, WORLD_HEIGHT);
camera = new OrthographicCamera();
viewport = new ExtendViewport(WORLD_WIDTH / 2f, WORLD_HEIGHT / 2f, camera);
viewport.apply();
camera.position.set(WORLD_WIDTH / 2f, WORLD_HEIGHT / 2f, 0);
}
/**
* Clears the screen.
* Updates the camera and draws the sprite that holds
* the background image.
*/
@Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.update();
batch.setProjectionMatrix(camera.combined);
batch.begin();
theWorld.draw(batch);
batch.end();
}
/**
* Updates the viewport on screen resizing.
*/
@Override
public void resize (int width, int height) {
viewport.update(width, height);
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
}
/**
* Disposes unnecessary assets.
*/
@Override
public void dispose () {
batch.dispose();
image.dispose();
}
}