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.
LibGDX’ita (esialgu)¶
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.
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.