bytes.cat

La wiki d'FP d'informàtica

Eines de l'usuari

Eines del lloc


Barra lateral

ASIX Administració de Sistemes Informàtics i Xarxes
Tots els mòduls del cicle
MP01 Implantació de sistemes operatius
Totes les UFs del modul
MP02 Gestió de bases de dades
Totes les UFs del modul
MP03 Programació bàsica
Totes les UFs del modul
MP04 Llenguatges de marques i sistemes de gestió d'informació
Totes les UFs del modul
MP05 Fonaments de maquinari
Totes les UFs del modul
MP06 Administració de sistemes operatius
Totes les UFs del modul
MP07 Planificació i administració de xarxes
Totes les UFs del modul
MP08 Serveis de xarxa i Internet
Totes les UFs del modul
MP09 Implantació d'aplicacions web
Totes les UFs del modul
MP10 Administració de sistemes gestors de bases de dades
Totes les UFs del modul
MP11 Seguretat i alta disponibilitat
Totes les UFs del modul
MP12 Formació i orientació laboral
Totes les UFs del modul
MP13 Empresa i iniciativa emprenedora
Totes les UFs del modul
MP14 Projecte
Totes les UFs del modul
DAM Desenvolupament d’aplicacions multiplataforma
Tots els mòduls del cicle
MP01 Sistemes informàtics
Totes les UFs del modul
MP02 Bases de dades
Totes les UFs del modul
MP03 Programació bàsica
Totes les UFs del modul
MP04 Llenguatges de marques i sistemes de gestió d'informació
Totes les UFs del modul
MP05 Entorns de desenvolupament
Totes les UFs del modul
MP06 Accés a dades
Totes les UFs del modul
MP07 Desenvolupament d’interfícies
Totes les UFs del modul
MP08 Programació multimèdia i dispositius mòbils
Totes les UFs del modul
MP09 Programació de serveis i processos
Totes les UFs del modul
MP10 Sistemes de gestió empresarial
Totes les UFs del modul
MP11 Formació i orientació laboral
Totes les UFs del modul
MP12 Empresa i iniciativa emprenedora
Totes les UFs del modul
MP13 Projecte de síntesi
Totes les UFs del modul
MPDual Mòdul Dual / Projecte
DAW Desenvolupament d’aplicacions web
Tots els mòduls del cicle
MP01 Sistemes informàtics
Totes les UFs del modul
MP02 Bases de dades
Totes les UFs del modul
MP03 Programació
Totes les UFs del modul
MP04 Llenguatge de marques i sistemes de gestió d’informació
Totes les UFs del modul
MP05 Entorns de desenvolupament
Totes les UFs del modul
MP06 Desenvolupament web en entorn client
Totes les UFs del modul
MP07 Desenvolupament web en entorn servidor
Totes les UFs del modul
MP08 Desplegament d'aplicacions web
Totes les UFs del modul
MP09 Disseny d'interfícies web
Totes les UFs del modul
MP10 Formació i Orientació Laboral
Totes les UFs del modul
MP11 Empresa i iniciativa emprenedora
Totes les UFs del modul
MP12 Projecte de síntesi
Totes les UFs del modul
SMX Sistemes Microinformàtics i Xarxes
Tots els mòduls del cicle
MP01 Muntatge i manteniment d’equips
Totes les UFs del modul
MP02 Sistemes Operatius Monolloc
Totes les UFs del modul
MP03 Aplicacions ofimàtiques
Totes les UFs del modul
MP04 Sistemes operatius en xarxa
Totes les UFs del modul
MP05 Xarxes locals
Totes les UFs del modul
MP06 Seguretat informàtica
Totes les UFs del modul
MP07 Serveis de xarxa
Totes les UFs del modul
MP08 Aplicacions Web
Totes les UFs del modul
MP09 Formació i Orientació Laboral
Totes les UFs del modul
MP10 Empresa i iniciativa emprenedora
Totes les UFs del modul
MP11 Anglès
Totes les UFs del modul
MP12 Síntesi
Totes les UFs del modul
CETI Ciberseguretat en Entorns de les Tecnologies de la Informació
Tots els mòduls del cicle
CiberOT Ciberseguretat en Entorns d'Operació
Tots els mòduls del cicle
jocs_libgdx

Desenvolupament de jocs amb libGDX

A l'article jocs_android pot veure's com treballar jocs utilitzant el framework estàndard d'Android (bàsicament modificant els valors del layout i els elements que hi conté).

En aquest article utilitzarem una llibreria específica, libGDX per realitzar el joc. Aquesta té molts avantatges, sobretot que permet compilar en diverses plataformes (Android, Desktop, iOS, HTML). A més, ens permetrà utilitzar recursos gràfics específics que ens facilitaran operacions complexes en 2D i 3D amb acceleració OpenGL.

Enllaços:

  1. libGDX Comunicacions en aquesta wiki.


Instal·lació

Necessites tenir instal·lat Android Studio.

Crea el projecte amb l'eina per iniciar projectes libGDX.

Troubleshooting

Si t'apareix l'error relacionat amb la llibreria ZIP tens 2 opcions:

  1. Eliminar la compilació de la plataforma iOS.
  2. Editar android/build.gradle i ajustar les llibreries:
    android/build.gradle
    buildToolsVersion "33.0.0"


Definicions

Objectes principals del framework:

  • Game: classe principal del joc, contenidor de pantalles.
  • Screen: hi encapsulem una pantalla (game over, presentació, etc.), nivell, minijoc. Cadascuna és independent de l'altra.
  • OrtographicCamera: objecte per traduir coordenades. Pot servir per adaptar tamanys de pantalla o per projectar en 2D una imatge 3D.
  • SpriteBatch: objecte per pintar sprites (imatges, animacions).
  • ShapeRenderer: objecte per pintar formes geomètriques (cercles, el·lipses, rectangles, triangles, etc.
  • BitmapFont: objecte per pintar lletres.
  • Texture: imatge.
  • TextureRegion: fragment d'una imatge.
  • Animation: objecte per gestionar les animacions i spritesheets.
  • Actor: objecte tipus widget, que pot ser alhora un element gràfic i una entrada de dades. Exemples: Button, Dialog, TextInputListener, etc.
  • Stage: objecte per renderitzar Actors.
  • Skin: conjunt de imatges i fonts per renderitzar Actors. Imprescindible si es vol emprar Actor i Stage.


Primer joc: Drops

drop-game.jpeg

  1. Crea un nou projecte amb aquesta eina. Puja'l a Github (i al Moodle).
  2. Segueix el tutorial per fer el joc Drop. El tutorial conté indicacions per implementar el joc en les 4 plataformes amb el mateix codi Java, però si vols , només cal que segueixis el tutorial per la part de Android i el core (la part comuna).
  3. Mostra el joc al professor quan tinguis el cubell funcionant. Fes un commit i push al repo.
  4. Realitza la segona part del tutorial de libGDX on s'expliquen els objectes Game i Screen. Acaba de transformar el teu projecte a la versió amb objectes tal i com es descriu al tutorial i que et permetrà fer una pantalla de presentació del joc.
  5. Recorda fer un nou commit i push.
  6. Mostra el joc al professor quan el tinguis acabat.
  7. Afegeix les següents ampliacions:
    1. Implementa un comptador de les gotes que es capturen amb el cubell. Mostra el resultat en una cantonada de la pantalla.
    2. Quan una gota arriba a terra el joc s'acaba. Afegeix un so adequat.
    3. Quan s'acaba la partida s'esborra la pantalla i s'atura el so. Es mostra la puntuació i un botó que permeti reiniciar la partida.
    4. Afegeix una imatge de fons adequada (que les gotes ressaltin i no dificulti el joc).
    5. Quan s'acaba la partida, la imatge de fons és diferent.
    6. Ajusta el hit test per tal que la gota només es consideri recollida si entra per la part de sobre del cubell (que no funcioni si venim de costat).
    7. Fes un comptador que indiqui el nombre de frames per segon (consultar llibreria Gdx.graphics).
    8. Afegeix alguna funcionalitat de collita pròpia.
  8. Recorda fer commit i push a Github.


libGDX és multiplataforma

El codi de la teva aplicació està al mòdul CORE.

Pots compilar en:

  1. Android: si obres el projecte amb Android Studio per defecte podràs executar en Android.
  2. Desktop (java app, Windows o Linux): afegeix una nova configuració
    RUN -> Edit configurations -> Add (+) -> Application
    1. Selecciona Module: myapp.desktop.main
    2. Selecciona Main Class: DesktopLauncher
  3. iOS: segueix aquestes instruccions. Requereix tenir un Mac, XCode i Android Studio amb RovoVM plugin
    1. Segons diu aquí, el simulador no s'activa, i cal descarregar la darrera versió de RoboVM d'aquí
    2. El darrer punt del tutorial diu q cal modificar ios.iml cada cop que executem. Aquí hi ha una solució automatitzada.


Sprites i animacions

Utilitzarem l'objecte 2D-Animation de libGDX.

L'objecte Animation ens facilita la gestió dels sprites a partir d'una spritesheet. Fes aquest tutorial per veure com fer l'animació. És relativament fàcil aconseguir-ho amb els objectes de la llibreria:

  1. Texture: ens ajuda a gestionar una imatge.
  2. TextureRegion: ens facilita retallar imatges grans i mostrar només una part (ideal per a agafar un fotograma d'un spritesheet).
  3. Sprite: descriu una TextureRegion amb la geometria i rotació per a ser renderitzat.
  4. Animation: un cop tenim triats els Texture o TextureRegion que ens cal per a l'animació, els podem posar en un array, i Animation ens facilitarà anar avançant els fotogrames un a un, segons el temps transcorregut.

Exercici

Busca una spritesheet que t'agradi en alguna de les següents webs amb recursos lliures de copyright i mira d'animar-la de forma similar a com es fa al tutorial de Animation.

Tingues en compte que és molt més fàcil animar una spritesheet amb els sprites ubicats en forma de matriu regular (com l'exemple de la documentació). Si són irregulars necessitarem un mapeig que es sol fer amb un Texture Atlas. Es recomana que inicialment feu servir un spritesheet simple.

Cada alumne ha de buscar un spritesheet diferent. Mostreu-lo al professor abans de realitzar l'animació.

  1. Imatges i sprites:
  2. Música i sons:

Eines interessants

Una eina interessant és TexturePacker, que ens permet construir una spritesheet a partir d'imatges individuals, i ens generarà també el TextureAtlas que facilitarà trobar després les coordenades de cada fotograma:

  1. TexturePacker doc. Hi ha unes apps suggerides pels creadors de libGDX:
  2. Texture Packer (sw propietari). Té funcions interessants però algunes son només en la versió Pro (de pagament).
  3. He trobat interessant aquest post de Stackoverflow que recomana l'ús d'aquesta eina, TexturePacker.


Animant un spritesheet irregular

Si volem animar un spritesheet irregular necessitarem fer un mapeig de les imatges (sprites) en un Texture Atlas. Per aconseguir-ho tenim diverses opcions:

  • Trossejar totes les imatges i fer servir TexturePacker. Obtindrem una spritesheet nova amb el seu atlas.
  • Fragmentar la spritesheet en un array de TextureRegion. Necessitarem saber la posició (x,y) d'origen i dimensions (amplada i alçada) de cada frame o sprite. Eines com GIMP poden facilitar-ho seleccionant cada sprite i copiant les seves coordenades i dimensons.

Per exemple, per aquesta spritesheet podem fer el caminar del Yeti de la següent manera:

A la definició de classe:

TextureRegion frames[] = new TextureRegion[4];
Animation<TextureRegion> yeti;

A create():

// per cada frame cal indicar x,y,amplada,alçada
frames[0] = new TextureRegion(sheet,9,190,107,112);
frames[1] = new TextureRegion(sheet,124,190,113,107);
frames[2] = new TextureRegion(sheet,248,190,101,110);
frames[3] = new TextureRegion(sheet,364,190,122,109);
yeti = new Animation<TextureRegion>(0.25f,frames);

A render():

stateTime += Gdx.graphics.getDeltaTime(); // Accumulate elapsed animation time
TextureRegion frame = yeti.getKeyFrame(stateTime,true);
 
batch.begin();
batch.draw(frame, 200, 100);
// si volem invertir el sentit, ho podem fer amb el paràmetre scaleX=-1
batch.draw(frame, 200, 100, 0, 0,
           frame.getRegionWidth(),frame.getRegionHeight(),-1,1,0);batch.end();

Invertir sprites

En moltes spritesheets els mateixos frames per caminar cap a l'esquerra serveixen per caminar cap a la dreta, ja que les eines de libGDX ens permeten invertir la imatge.

Per invertir un sprite es pot fer a través del mètode TextureRegion.flip, però el resultat (en aquest cas) no és molt bo ja que al haver diferents amplades del frame fa que el moviment faci salts irregulars.

És més pràctic invertir la imatge directament en el mètode SpriteBatch.draw ajustant la sccaleX=-1 , lo qual facilitarà un moviment més fluid (al menys a l'exemple) ja que l'origen es manté bé.


Mahoma o la muntanya

Per aconseguir una sensació de moviment hem fet servir l'objecte 2D-Animation de libGDX, juntament amb Texture i TextureRegion, tal i com explica el tutorial.

Ara, a part de l'animació, haurem de bellugar el personatge… o bé el fons de la pantalla. Tenim aquestes dues estratègies per fer la sensació de que avancem.

Moure el personatge

Per desplaçar el personatge per la pantalla, només caldrà modificar ON el pintem de la pantalla, just a les línies del codi de la funció render():

// al render()
// ...
spriteBatch.begin();
// Draw current frame at (50, 50)
spriteBatch.draw(currentFrame, 50, 50);
spriteBatch.end();

Podem desplaçar el personatge tenint una posx i posy, i utilitzant-les al render:

spriteBatch.draw(currentFrame, posx, posy);

A cada iteració del render només caldrà anar incrementant o decrementant adequadament aquestes posx i posy.

Moure el background

També podem optar per deixar el personatge al mig de la pantalla i bellugar el fons o background. O sigui que si Mahoma no va a la muntanya, serà la muntanya la que vindrà a Mahoma.

La idea serà tenir un mapa de fons, una imatge molt gran, de la qual retallarem només la part que volem mostrar a la pantalla del joc (el background).

L'objecte TextureRegion (veure TextureRegion a la documentació oficial) ens anirà perfecte per gestionar el fons de pantalla ja que està pensat per «retallar» una imatge gran i definir només una part d'ella (la que volem mostrar).

En particular, el mètode TextureRegion.setRegion(…) ens facilitarà que puguem desplaçar l'origen del fragment del background que volem mostrar, provocant un efecte de moviment (i que combinat amb l'animació queda exactament com es vol per a un joc).

Al create():

// bg
background = new Texture(Gdx.files.internal("background12.jpeg"));
background.setWrap(Texture.TextureWrap.MirroredRepeat, Texture.TextureWrap.MirroredRepeat);
bgRegion = new TextureRegion(background);
posx = 0;
posy = 0;

I al render() (acció del main loop del joc):

// (1) CALCULAR
//...calculem posx i posy del personatge...
 
// TextureRegion ens permet retallar un fragment de la Texture
// retallem el fragment de background des de la posició del personatge (posx, posy)
bgRegion.setRegion(posx,posy,game.SCR_WIDTH,game.SCR_HEIGHT);
 
// (2) PINTAR
game.batch.begin();
// primer pintem el background
game.batch.draw(bgRegion,0,0);
// ...després pintem altres coses...
 
// finalitzem main loop
game.batch.end();

A més, pots aconseguir fer un background q es vagi repetint fent efecte mirall quan arribes als límits, com és el cas d'aquest exemple del Ieti. Això ho aconseguim amb Texture.setWrap(…) (consulta la doc de Texture). El cas típic és fer-li wrap vertical i horitzontal:

background.setWrap( Texture.TextureWrap.MirroredRepeat,
                    Texture.TextureWrap.MirroredRepeat);


Objectes libGDX

La llibreria libGDX ens dona una sèrie d'objectes per facilitar els càlculs i l'encapsulament de variables.

De la llibreria Math destaquem:

  • Vector2 ens permetrà representar un vector, que té una component X i una component Y. Val la pena parar atenció als mètodes per calcular l'angle angleDeg() i angleRad().
  • Rectangle compost per posició (X i Y) i amplada i alçada. Al tutorial Drop s'utilitza també per veure les col·lisions (entre el cubell i les gotes) amb el mètode intersects(Rectangle). Té altres mètodes útils com contains per saber si un objecte està dins d'un altre.
  • Circle ens anirà molt bé per a les pilotes, ja que té posició (X i Y) i radi. També ens permetrà calcular col·lisions amb altres objectes amb els mètodes intersect i contains.

I a la llibreria Graphics destaquem:

  • Batch és l'objecte que pinta imatges (Textures) amb el mètode draw (sobrecarregat amb diverses versions, mira la documentació).
    • El batch només pinta Textures (imatges) sobre la pantalla. Si volem pintar formes de colors necessitarem l'objecte Pixmap.
  • Pixmap a mode de canvas o «lienzo», ens permet crear imatges a partir de formes geomètriques (línia, cercle, rectangle, píxel, etc.)

Dibuixant formes

Podem dibuixar formes geomètriques utilitzant Batch i l'objecte Pixmap.

Aquest és un exemple utilitzat per a un tret làser de color vermell (rectangle):

Pixmap tretPixmap = new Pixmap( TRET_WIDTH, TRET_HEIGHT, Pixmap.Format.RGB888);
tretPixmap.setColor( Color.RED );
tretPixmap.fill();
Texture tretTexture = new Texture(tretPixmap);

Per fer una pilota bàsica (cercle) amb transparència = 0.3, (on 0.0==transparent i 1.0==opac) de color blanc ho faríem així:

Pixmap pilotaPixmap = new Pixmap(100,100,Pixmap.Format.RGBA8888);
pilotaPixmap.setColor(new Color(1.0f,1.0f,1.0f,0.3f));
pilotaPixmap.fillCircle(50,50,50);
Texture pilotaTexture = new Texture(pilotaPixmap);

Aquests objectes es crearien al constructor del Game. Després pintaríem el «tret» o la «pilota» en el mètode render segons les coordenades que haguem calculat (utilitzant Vector, Rectangle, etc.).


Pilotes rebotant

Molts jocs es basen en fer rebotar pilotes per la pantalla. Us passo algunes estratègies típiques per aconseguir-ho.

Ull! Segons aquesta imatge l'origen de coordenades està a dalt a l'esquerra. Això sol ser així en la majoria de llibreries gràfiques. libGDX, en canvi, té l'origen a baix a l'esquerra, que resulta més intuïtiu ja que està més d'acord amb els clàssics eixos de coordenades cartesians.

Com pots veure a la imatge, rebotar una pilota és fàcil si tens les coordenades (pos_x i pos_y) de la posició, i la trajectòria la emmagatzemes com una velocitat descomposada en component X i component Y (vel_x i vel_y , per exemple).

Quan la pilota arriba al límit inferior o superior, només cal invertir el signe de la velocitat Y si la trajectòria semblarà exactament un rebot.

Exercici

Fes una aplicació de demostració amb els següents nivells (Screen).

Quan es cliqui sobre la pantalla es passa al següent nivell.

Procura utilitzar Vector2

  1. Pilota (busca una icona que t'agradi) que reboti a tots els límits de la pantalla.
  2. Pilota amb gravetat. Rebota a terra, però no al «sostre».
    • Pista: es tracta de que la velocitat Y varii al llarg del temps, amb un increment relacionat amb g (la gravetat).
    • Vigila el comportament que té la pilota al llarg del temps. Hauria de mantenir-se a la mateixa alçada màxima. Si no és així, revisa perquè passa.
  3. Pilota amb gravetat i fricció. Quan rebota a terra, perd algo d'energia i acaba atenuant-se


Controls Touchscreen

Els controls d'entrada al joc poden ser molts:

  • Teclat (només per versions Desktop)
  • Pantalla
  • Acceleròmetre
  • Brúixola/Giròscop

Convé llegir la documentació dels controls en libGDX.

Joystick virtual

Una estratègia adequada sol ser definir unes regions de la pantalla que, quan es premin, permetin avançar el personatge. Definirem aquestes regions amb rectangles d'1/3 de la pantalla:

A l'objecte:

Rectangle up, down, left, right, fire;
final int IDLE=0, UP=1, DOWN=2, LEFT=3, RIGHT=4;

A create():

// facilities per calcular el "touch"
up = new Rectangle(0, game.SCR_HEIGHT*2/3, game.SCR_WIDTH, game.SCR_HEIGHT/3);
down = new Rectangle(0, 0, game.SCR_WIDTH, game.SCR_HEIGHT/3);
left = new Rectangle(0, 0, game.SCR_WIDTH/3, game.SCR_HEIGHT);
right = new Rectangle(game.SCR_WIDTH*2/3, 0, game.SCR_WIDTH/3, game.SCR_HEIGHT);

A render() podem cridar la funció virtual_joystick_control() que ens retornarà la direcció que detectem de les regions definides:

    protected int virtual_joystick_control() {
        // iterar per multitouch
        // cada "i" és un possible "touch" d'un dit a la pantalla
        for(int i=0;i<10;i++)
        if (Gdx.input.isTouched(i)) {
            Vector3 touchPos = new Vector3();
            touchPos.set(Gdx.input.getX(i), Gdx.input.getY(i), 0);
            // traducció de coordenades reals (depen del dispositiu) a 800x480
            game.camera.unproject(touchPos);
            if (up.contains(touchPos.x, touchPos.y)) {
                return UP;
            } else if (down.contains(touchPos.x, touchPos.y)) {
                return DOWN;
            } else if (left.contains(touchPos.x, touchPos.y)) {
                return LEFT;
            } else if (right.contains(touchPos.x, touchPos.y)) {
                return RIGHT;
            }
        }
        return IDLE;
    }

Exercici

Implementa el control de moviment mitjançant touch input al teu personatge amb animació. Pots optar moure el personatge per la pantalla, o bé moure el background, tal i com s'explica a l'apartat anterior.


Comunicacions

Vés a l'article libGDX Comunicacions on podràs aprendre a utilitzar:

  • Crides HTTP/S estàndard
  • Ús WebSockets per comunicacions a temps real.


Ortographic Camera

Tal i com explica el tutorial del Drop Game, la OrtographicCamera ens facilitarà la traducció entre les coordenades que hem definit pel joc (o «coordenades virtuals») i les coordenades reals del dispositiu (device), que poden tenir dimensions diferents. A més, al treballar amb una llibreria multiplataforma, voldrem fer un sol joc a la carpeta core/, i el codi de les carpetes de les plataformes (android/, ios/, html/) haurà de ser fix (no hem de repetir el joc a cada plataforma).

Les operacions que realitzarà la càmera seran:

Coordenades virtuals -> project -> Coordenades reals (//device//)
Coordenades virtuals <- unproject <- Coordenades reals (//device//)

Posant que volem una pantalla de 800×480

DesktopLauncher.java
public class DesktopLauncher {
    public static void main (String[] arg) {
        Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
        config.setWindowedMode(480, 800);

A l'arxiu de Game o Screen:

public final int GAME_WIDTH = 800;
public final int GAME_HEIGHT = 480;
 
public void create() {
        camera = new OrthographicCamera();
        camera.setToOrtho(false, GAME_WIDTH, GAME_HEIGHT);
 
        //...
}

Per fer que el SpritBatch tradueixi automàticament en totes les accions de dibuix, el configurarem a l'inici del render():

public void render() {
        camera.update();
        spriteBatch.setProjectionMatrix(camera.combined);
 
        //...
}

Si estem capturant entrades de la pantalla, caldrà la operació contrària: unproject()

protected int virtual_joystick_control() {
    // iterar per multitouch
    // cada "i" és un possible "touch" d'un dit a la pantalla
    for(int i=0;i<10;i++)
    if (Gdx.input.isTouched(i)) {
        Vector3 touchPos = new Vector3();
        touchPos.set(Gdx.input.getX(i), Gdx.input.getY(i), 0);
        // traducció de coordenades reals (depen del dispositiu) a 800x480
        game.camera.unproject(touchPos);
        // les dades convertides s'enregistren a la mateixa variable touchPos
        //...


Actors, Scenes i Skins

Per fer controls avançats com Buttons, Dialogs, etc. hem de tenir en compte que son elements tant de renderització com de entrada de dades, i es tracten de forma especial. En caldrà emprar els objectes Stage i Skin:

Algunes referències:

IMPORTANT: perquè funcioni el skin cal descarregar tots els arxius a la carepta assets.


jocs_libgdx.txt · Darrera modificació: 2024/04/12 11:52 per enric_mieza_sanchez