Joshua Hoffman - Should the CTO be Coding? - Codemotion Amsterdam 2019
Avete avuto l'idea per il nuovo Flappy Birds? - Anelli
1. ROME 11-12 april 2014ROME 11-12 april 2014
Avete avuto l’idea per il nuovo Flappy Birds?
Introduzione a libGDX
E-Mail: matteo.anelli@nttdata.com
Twitter: @matteoanelli
Xbox Live, PSN, Steam: Ziggybee
Matteo Anelli (NTT DATA)
2. ROME 11-12 april 2014
Perché usare libgdx?
Oltre l’1.5% dei titoli Play Store la usa (è il primo framework per giochi)
Standardizza lo sviluppo e minimizza i costi di start-up indie
Permette una prototipazione molto rapida perché offre funzionalità di base per:
Grafica 2D e 3D
Audio
Astrazione degli input
Primitive matematiche (vettori, operatori 3D)
Networking (TCP, HTTP)
File System
User Interface
E’ ampiamente testata, con centinaia di app e giochi distribuiti con questa tecnologia
E’ Open Source!
Requisiti:
PC/Mac: JVM e supporto OpenGL
Android: Supporto OpenGL ES 2.x o superiore
iOS: RoboVM
BlackBerry: ci interessa davvero?
5. ROME 11-12 april 2014
One-Button Game
Tappando lo schermo il
passerotto si libra
Se tocca i tubi muore
I tubi scorrono a sinistra
Se il passerotto non batte le
ali la gravità lo fa schiantare
TAP!
Il prototipo
8. ROME 11-12 april 2014
public class FlappyGame extends Game {
@Override
public void create() {
AssetLoader.load();
setScreen(new GameScreen());
}
@Override
public void dispose() {
super.dispose();
AssetLoader.dispose();
}
}
La main class di un progetto libgdx
è la classe Game.
Possiede solo due metodi:
Create per l’inizializzazione
dell’app.
Dispose per la liberazione delle
risorse prima della chiusura
dell’app.
Nel nostro caso all’avvio vengono
caricati gli asset di gioco
(immagini, suoni, etc) e viene
definito il GameScreen di partenza
FlappyGame
9. ROME 11-12 april 2014
Uno Screen è qualcosa di molto
simile ad una classe Activity in
Android. Rappresenta uno stato
dell’App che svolge una
funzione. Per semplicità
l’esempio usa un singolo Screen.
Un gioco è come un film, ad ogni
ciclo se ne calcola un
fotogramma. Lo screen, tramite il
metodo render permette di
controllare il disegno di un
fotogramma.
public class GameScreen implements Screen {
private GameWorld world;
private GameRenderer renderer;
public GameScreen() {
float screenWidth = Gdx.graphics.getWidth();
float screenHeight = Gdx.graphics.getHeight();
float gameWidth = 136;
float gameHeight =
screenHeight / (screenWidth / gameWidth);
int midPointY = (int) (gameHeight / 2);
world = new GameWorld(midPointY);
renderer = new GameRenderer(world,
(int) gameHeight, midPointY);
Gdx.input.setInputProcessor(
new InputHandler(world));
}
@Override
public void render(float delta) {
runTime += delta;
world.update(delta);
renderer.render(runTime);
}
GameScreen
10. ROME 11-12 april 2014
La classe principale del gioco,
non è un oggetto libgdx ma
rappresenta il core del gioco, il
centro di controllo dell’intera
logica dell’applicazione
public void updateRunning(float delta) {
if (delta > .15f) {
delta = .15f;
}
bird.update(delta);
scroller.update(delta);
if (scroller.collides(bird) && bird.isAlive()) {
scroller.stop();
bird.die();
AssetLoader.dead.play();
}
if (Intersector.overlaps(
bird.getBoundingCircle(), ground)
&& currentState == GameState.RUNNING) {
scroller.stop();
bird.die();
bird.decelerate();
currentState = GameState.GAMEOVER;
if (score > AssetLoader.getHighScore()) {
AssetLoader.setHighScore(score);
currentState = GameState.HIGHSCORE;
}
}
}
GameWorld
11. ROME 11-12 april 2014
public void update(float delta){
frontGrass.update(delta);
backGrass.update(delta);
pipe1.update(delta);
pipe2.update(delta);
pipe3.update(delta);
if (pipe1.isScrolledLeft()){
pipe1.reset(pipe3.getTailX() + PIPE_GAP);
} else if (pipe2.isScrolledLeft()) {
pipe2.reset(pipe1.getTailX() + PIPE_GAP);
} else if (pipe3.isScrolledLeft()) {
pipe3.reset(pipe2.getTailX() + PIPE_GAP);
}
if (frontGrass.isScrolledLeft()) {
frontGrass.reset(backGrass.getTailX());
} else if (backGrass.isScrolledLeft())
{
backGrass.reset(frontGrass.getTailX());
}
}
Lo scrolling non fa altro che
«aggiornare» gli elementi del
nostro mondo.
Lo scroller controlla anche
che i tubi non siano usciti
fuori dallo schermo. In quel
caso li resetta, accodandoli
alla destra dell’ultimo tubo a
schermo.
ScrollHandler
12. ROME 11-12 april 2014
La fisica è molto semplice:
c’è un’accelerazione
costante che spinge l’uccello
verso il basso.
In realtà l’uccello si muove
solo in verticale, l’effetto
proiettile è dato dagli altri
elementi che scorrono!
public void update(float delta) {
velocity.add(acceleration.cpy().scl(delta));
if (velocity.y > 200) {
velocity.y = 200;
}
// CEILING CHECK
if (position.y < -13) {
position.y = -13;
velocity.y = 0;
}
position.add(velocity.cpy().scl(delta));
boundingCircle.set(position.x + 9, position.y
+ 6, 6.5f);
// Rotate counterclockwise
if (velocity.y < 0) {
rotation -= 600 * delta;
if (rotation < -20) {
rotation = -20;
}
}
// Rotate clockwise
if (isFalling() || !isAlive) {
rotation += 480 * delta;
if (rotation > 90) {
rotation = 90;
}
}
}
public void onClick() {
if (isAlive) {
AssetLoader.flap.play();
velocity.y = -140;
}
}
Fisica
13. ROME 11-12 april 2014
Il rendering avviene tramite il
wrapping delle funzioni
OpenGL ES con il
componente Gdx.gl
Nei giochi 2D ogni sprite è
una Texture. Per evitare di
sovraccaricare la GPU la
libreria effettua un buffering
delle operazioni.
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
shapeRenderer.begin(ShapeType.Filled);
// Draw Background color
shapeRenderer.setColor(30 / 255.0f, 125 / 255.0f, 205 /
255.0f, 1);
shapeRenderer.rect(0, 0, 136, midPointY + 66);
. . .
shapeRenderer.end();
batcher.begin();
batcher.disableBlending();
batcher.draw(bg, 0, midPointY + 23, 136, 43);
drawGrass();
drawPipes();
batcher.enableBlending();
if (bird.shouldntFlap()) {
batcher.draw(birdMid, bird.getX(), bird.getY(),
bird.getWidth() / 2.0f, bird.getHeight() / 2.0f,
bird.getWidth(), bird.getHeight(), 1, 1,
bird.getRotation());
} else {
batcher.draw(birdAnimation.getKeyFrame(runTime),
bird.getX(),
bird.getY(), bird.getWidth() / 2.0f,
bird.getHeight() / 2.0f, bird.getWidth(),
bird.getHeight(),
1, 1, bird.getRotation());
}
batcher.end();
Rendering