Wstęp do LibGDX


#1

Wstęp

Libgdx jest frameworkiem „Javowywm” do wieloplatformowego tworzenia gier i wizualizacji. W tej chwili wspiera takie platformy jak Windows, Linux, Mac OS X, Android(ver. 5+), iOS i HTML5. Jedynym wymaganiem na Windowsie, Linuxie i Mac OS X jest JRE(Java runtime).

Libgdx pozwala na napisanie kodu jeden raz i wypuszczanie na różne platformy bez modyfikacji. Zamiast czekać na kompilację i wysłanie do urządzenia Androidowego czy kompilacje na HTML5 możesz bezpośrednio debugować kod na swoim komputerze. Możesz używać wszystkich narzędzi z ekosystemu Javy, żeby być tak produktywnym jak tylko to możliwe.

Libgdx pozwala pisać tak niskopoziomowo jak tylko chcesz, dając bezpośredni dostęp do systemów plików, urządzeń wejścia, audio a nawet OpenGL z międzyplatformowym interfejsem OpenGL ES 1.x i 2.0.

Na podstawie tych niskopoziomowych udogodnień został stworzony zestaw API, które pomagają przy zadaniach takich jak:

  • Renderowanie sprajtów, tekstu, interfejsu.
  • Odtwarzanie efektów dźwiękowych i muzyki(strumieniowo).
  • Algebrze liniowej i trygonometrii, parsowaniu JSON-a i XML, itd.

Inne informacje

  • Wersja Desktopowa jest bazowana na LWJGL.
  • Wersja iOs-owa musi być kompilowana na macu.
  • Wersja HTML5 nie ma dostępu do plików na komputerze(to raczej oczywiste).

Konfiguracja

Potrzebne rzeczy
Ja w tym poradniku będę korzystał z Eclipse. Jeżeli preferujesz inne IDE to pomiń ten dział i odwiedź ten link. https://code.google.com/p/libgdx/wiki/ProjectSetup

Ci którzy mają problemy, chcą skompilować na iOSa i zainteresowanych, zapraszam do zapoznania się z https://code.google.com/p/libgdx/wiki/Prerequisits

Dla wszystkich platform:
JDK(Java Development Kit ~125mb) zestaw narzędzi między innymi do kompilowania javy.
http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html

Eclipse(~200mb) Zwykła wersja eclipse: http://eclipse.org/downloads/

Download dla innych platform jest na końcu.

Tworzenie projektu

Teraz potrzebujesz installatora Libgdx, pobierz go i uruchom dwuklikiem bądź w konsoli komendą „java -jar gdx-setup-ui.jar” Na tym etapie możesz mieć problem z brakiem „komendy” java. Problem można naprawić poprzez dodanie javy do path systemu. Odpowiedź łatwo można znaleźć w google. [url]http://libgdx.badlogicgames.com/nightlies/dist/gdx-setup-ui.jar[/url]

Gdx-setup-ui jest to plik instalacyjny/downloader libgdx, jest też zawarty w paczce którą nim pobierzesz.

Tutaj wybierz opcję Create i pierwsze co zrób to wciśnij przycisk (z N) Ten przycisk pobierze ci najnowszą wersję „nightly”(~40mb | coś jak beta tylko buildowana co „noc”). Dlaczego nightly? Ponieważ zawiera najnowszy kod i bugfixy. Kod wersji nightly znacząco różni się od stable(a przynajmniej w momencie kiedy to pisano).

Po lewej mamy menu konfiguracji.

  • Name : W nazwie możemy wpisać co chcemy.
  • Package : Tutaj musi być zachowany schemat. (com.firma.krotkanazwa, np. com.clockwise.dwarf , com.gmail.sonny.saeron)
  • Game class : To klasa główna programu. (Najlepiej użyć krótkiej nazwy)
  • Destination : Ścieżka w którym chcesz utworzyć pliki z projektami.
  • Generate : To generatory projektów do róznych platform.
  • Advanced settings : Tutaj edytujemy końcówki nazw folderów(jak widać na screenie).

Po środku mamy wybór narzędzi.

  • Physics Body Editor : To wsparcie dla edytora ciał do Box2D. Wczytuje pliki eksportowane z edytora o tej samej nazwie.
  • Universal Tween Engine : Zajmuje się interpolacją. Jest przydatnym narzędziem do poruszania, obracania, skalowania i wszystkiego innego co chcesz poruszyć gładko po ekranie i nie tylko.

Jak już na dole pobrała się paczka, to po prawej stronie w generacji, wciskamy guzik . Tam wciskamy launch i czekamy aż pojawi się napis „All done!”.

Importowanie projektu do Eclipse

Po lewej w Package Explorer wciśnij prawy przycisk myszy na białe pole i wybierz opcję import.

Tam wybieramy opcję General->Existing Projects into Workspace i dodajemy wcześniej utworzone foldery projektu. Tutaj możesz odznaczyć projekty którymi w tej chwili nie chcesz się zajmować.

Importujesz guzikiem Finish.

Uruchamianie

Po lewej zaznaczamy wersję natywną(-desktop) i odpalamy za pomocą F11 bądź zielonego znaczka play na górze. Tam wybieramy

Java application->Ok

Main->Ok

Jak wszystko zrobiłeś poprawnie to w tej chwili powinno pojawić ci się okienko aplikacji z logiem Libgdx.

Kod właściwy aplikacji edytuje się w folderze bez końcówki. W moim przypadku to
Instalacja->src->com.ghandhi.instalacja->Instalacja.java
Instalacja.java w tym przypadku to Game class który ustawiliśmy podczas tworzenia projektu.

Przebieg działania aplikacji

Jak można zauważyć w grafie po prawej stronie. Pierwszą rzeczą jaka jest wykonywana jest funkcja create(). Potem wykonuje się resize() która powtarza się za każdym razem jak zmieni się wielkość ekranu. Funkcja render() wykonywana jest co klatkę. Funkcja resume() wykonuje się tylko na androidzie, zaś pause() uruchamia się także przed zamknięciem aplikacji(dobre miejsce na automatyczny zapis). Ostatnią jaką się wykonuje jest dispose() która ma za zadanie pozbyć się zbędnych rzeczy z pamięci(spriteBatch, tekstury). Libgdx nie ma głównej pętli, jest bazowany na [„eventach”][6] głównie przez to jak działa Android i JavaScript. Ale funkcja render() może być tak traktowana.

Klasa startująca - Desktop

Do debugowania najszybszą możliwą drogą jest odpalenie gry na desktopie, tutaj pokażę tylko podstawowy zarys dla tej platformy. Resztę platform można znaleźć na końcu tego poradnika. Więcej informacji: https://code.google.com/p/libgdx/wiki/ApplicationConfiguration

Klasa startująca na desktopie jest oparta o lwjgl. A wygląda tak

package com.me.mygdxgame;

import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;

public class Main {
   public static void main(String[] args) {
      LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
      cfg.title = "my-gdx-game";
      cfg.useGL20 = false;
      cfg.width = 480;
      cfg.height = 320;
  
      new LwjglApplication(new MyGdxGame(), cfg);
   }
}

W tym kodzie tworzy się instancja LwjglApplicationConfiguration która służy do konfiguracji ustawień aplikacji takimi jak tytuł, szerokość, synchronizacja pionowa, fps czy używana wersja OpenGL. Po czym uruchamiana jest klasa główna aplikacji(w tym przypadku MyGdxGame).

Resztę klas startujących można zobaczyć na końcu poradnika.

SpriteBatch, Sprite i Texture

W libgdx renderowanie spritów zachodzi dzięki OpenGL i SpriteBatch-owi którym przypisywana jest tekstura wielkość, pozycja i inne translacje geometryczne. Wersja OpenGL 1.1 wymusza rozmiar tekstur do potęgi dwójki(np.16x16 64x64 128x256 1024x32). Łatwo to zmienić ustawiając cfg.useGL20 = true; w konfiguracji; Dzięki temu ustawi w aplikacji wersję 2.0. Aby wyrenderować sprite potrzebujemy instancji SpriteBatch i Sprite.

public class Instalacja implements ApplicationListener { private SpriteBatch batch; private Texture tex; private Sprite sprite; @Override public void create() { batch = new SpriteBatch(); tex = new Texture(Gdx.files.internal("image.png")); sprite = new Sprite(tex, 0, 0, 32, 32); } @Override public void resize(int width, int height) {} @Override public void render() { Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // Czyści ekran sprite.setPosition( sprite.getX()+2.5f*Gdx.graphics.getDeltaTime(), sprite.getY()+2.5f*Gdx.graphics.getDeltaTime()); batch.begin(); batch.draw(tex, 10, 10); // Rysuje texturę sprite.draw(batch); // Rysuje sprite batch.end(); } @Override public void pause() {} @Override public void resume() {} @Override public void dispose() {} }

W tym przykładzie stworzyłem dwa obiekty na ekranie, poruszający się sprite i nieruchomą teksturę.
Gotowiec można zobaczyć w „01_Instalacja”.

Przykład gry “Kółko i krzyżyk”

Tworzymy pliki projektu w kreatorze i importujemy go do Eclipse.

W TicTacToe-desktop/src/Main.java ustawiam następującą konfigurację.

[code]package com.gmail.sonny.saeron;

import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;

public class Main {
public static void main(String[] args) {
LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
cfg.title = “Tic Tac Toe”;
cfg.useGL20 = false;
cfg.width = 256;
cfg.height = 256;

            new LwjglApplication(new Game(), cfg);
    }

}[/code]

Skoro OpenGL 1.1(Niektóre karty graf. Intela nie wspierają 2.0) wymagane jest aby tekstury były potęgą dwójki(np. 256x1024) to zdecydowałem się na rozdzielczość 256x256, która z kolei wielkością pasuje do typowej minigry. Istnieje też TexturePacker który oficjalnie wspiera i jest wspierany przez LibGDX. Potrafi spakować wszystko do jednej tekstury której części można wczytać przez specjalny loader w LibGDX.
Więcej informacji: https://code.google.com/p/libgdx/wiki/TexturePacker

A tutaj tekstury:

W TicTacToe/src/Game.java wywalam wszystko i zostawiam tylko puste funkcje.

[code]package com.gmail.sonny.saeron;

import com.badlogic.gdx.ApplicationListener;

public class Game implements ApplicationListener {
@Override
public void create() {}
@Override
public void dispose() {}
@Override
public void render() {}
@Override
public void resize(int width, int height) {}
@Override
public void pause() {}
@Override
public void resume() {}
}[/code]

Do renderowania tekstur potrzebna będzie kamera, spritebatch no i one same.

private OrthographicCamera camera; private SpriteBatch batch; private Texture texX,texO,gui,win,lose,tie;

W funkcji create tworzę nowe obiekty spritebatcha i kamery. Wczytuje przy okazji tekstury, dodaję funkcję nowej gry która rozpocznie rozgrywkę i do metody resize dodaję zachowanie kamery.

[code]public void create() {
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
batch = new SpriteBatch();

    texX = new Texture(Gdx.files.internal("x.png"));
    texO = new Texture(Gdx.files.internal("o.png"));
    gui = new Texture(Gdx.files.internal("gui.png"));
    win = new Texture(Gdx.files.internal("win.png"));
    lose = new Texture(Gdx.files.internal("lose.png"));
    tie = new Texture(Gdx.files.internal("tie.png"));
    NowaGra();

}
public void resize(int width, int height) {
camera.setToOrtho(false, 256, 256);
}[/code]

Garbage collector javy sam nie wykryje że spritebatch i tekstury muszą zostać wydalone z systemu dlatego trzeba to zrobić za niego odpowiednia funkcją, więc w funkcji dispose pozbywam się ich wszystkich.

public void dispose() { batch.dispose(); texX.dispose(); texO.dispose(); gui.dispose(); win.dispose(); lose.dispose(); tie.dispose(); }

Potrzebne będzie coś do określania stanu gry i pola 3x3.

[code]private enum StanGry {
turaX,
turaO,
wygrana,
przegrana,
remis
}
private StanGry stanGry;

private enum Pole {
x,
o,
puste
}
private Pole pole[][] = new Pole3;[/code]

A także coś do czyszczenia tego pola.

public void WyczyscPole() { for(int x=0;x<3;x++) for(int y=0;y<3;y++) pole[x][y]=Pole.puste; }

No i funkcja nowej gry.

private void NowaGra() { WyczyscPole(); if(Math.round(Math.random())==1) stanGry=StanGry.turaO; else stanGry=StanGry.turaX; }

Przed renderingiem wypadało by zadbać o sprawdzenie stanu gry.

[code]private void Update() {
// Funkcja justTouched sprawdza czy pomiędzy klatkami coś kliknęło w ekran.
if(Gdx.input.justTouched() && (stanGry!=StanGry.turaX && stanGry!=StanGry.turaO))
NowaGra();
else if(Gdx.input.justTouched() && stanGry==StanGry.turaX)
{
float posX=-((float)Gdx.input.getX()/Gdx.graphics.getWidth()*3)+3;
float posY=(float)Gdx.input.getY()/Gdx.graphics.getHeight()*3;

            int fieldX=0,fieldY=0;
            if( posX>=0 && posX<=1 ) fieldX=0;
            if( posX>1 && posX<=2 ) fieldX=1;
            if( posX>2 && posX<=3 ) fieldX=2;
            
            if( posY>=0 && posY<=1 ) fieldY=0;
            if( posY>1 && posY<=2 ) fieldY=1;
            if( posY>2 && posY<=3 ) fieldY=2;

            if(pole[fieldX][fieldY]==Pole.puste) {
                    pole[fieldX][fieldY]=Pole.x;
                    stanGry=StanGry.turaO;
                    SprawdzStanGry();
            }
    }
    // AI przeciwnika
    if(stanGry==StanGry.turaO)
    {
            boolean found=false;
            do {
                    int fieldX=(int) Math.round(Math.random()*2);
                    int fieldY=(int) Math.round(Math.random()*2);
                    
                    if(pole[fieldX][fieldY]==Pole.puste)
                    {
                            found=true;
                            pole[fieldX][fieldY]=Pole.o;
                            stanGry=StanGry.turaX;
                            SprawdzStanGry();
                    }
            } while(found==false);
    }

}
private void SprawdzStanGry() {
// Sprawdzanie wygranej
if (SprawdzWygrana()) {
stanGry=StanGry.wygrana;
}
// Sprawdz przegrana
else if(SprawdzPrzegrana()) {
stanGry=StanGry.przegrana;
}
// Sprawdzanie remisu
else if(CountFull())
stanGry=StanGry.remis;
}[/code]

Coś by sprawdzić czy ktoś wygrał.

[code]private boolean SprawdzWygrana() {
// Pionowe
if( pole[0][0]==Pole.x &&
pole0==Pole.x &&
pole0==Pole.x)
return true;
if( pole[1][0]==Pole.x &&
pole1==Pole.x &&
pole1==Pole.x)
return true;
if( pole[2][0]==Pole.x &&
pole2==Pole.x &&
pole2==Pole.x)
return true;
// Poziome
if( pole[0][0]==Pole.x &&
pole[1][0]==Pole.x &&
pole[2][0]==Pole.x)
return true;
if( pole0==Pole.x &&
pole1==Pole.x &&
pole2==Pole.x)
return true;
if( pole0==Pole.x &&
pole1==Pole.x &&
pole2==Pole.x)
return true;
// Przekatne
if( pole[0][0]==Pole.x &&
pole1==Pole.x &&
pole2==Pole.x)
return true;
if( pole0==Pole.x &&
pole1==Pole.x &&
pole[2][0]==Pole.x)
return true;

    return false;

}

private boolean SprawdzPrzegrana() {
// Pionowe
if( pole[0][0]==Pole.o &&
pole0==Pole.o &&
pole0==Pole.o)
return true;
if( pole[1][0]==Pole.o &&
pole1==Pole.o &&
pole1==Pole.o)
return true;
if( pole[2][0]==Pole.o &&
pole2==Pole.o &&
pole2==Pole.o)
return true;

    // Poziome
    if( pole[0][0]==Pole.o &&
            pole[1][0]==Pole.o &&
            pole[2][0]==Pole.o)
            return true;
    if( pole[0][1]==Pole.o &&
            pole[1][1]==Pole.o &&
            pole[2][1]==Pole.o)
            return true;
    if( pole[0][2]==Pole.o &&
            pole[1][2]==Pole.o &&
            pole[2][2]==Pole.o)
            return true;
    
    // Przekatne
    if( pole[0][0]==Pole.o &&
            pole[1][1]==Pole.o &&
            pole[2][2]==Pole.o)
            return true;
    
    if( pole[0][2]==Pole.o &&
            pole[1][1]==Pole.o &&
            pole[2][0]==Pole.o)
            return true;
    
    return false;

}
// Sprawdza Remis
public boolean CountFull() {
int count=0;
for(int x=0;x<3;x++)
for(int y=0;y<3;y++) {
if(pole[x][y]!=Pole.puste)
count++;
}
if(count==9)
return true;
else
return false;
}[/code]

No i już mamy całą mechanikę, to przydało by się jeszcze całość wyrenderować.

[code]public void render() {
Update();
// Określa kolor którym ma czyścić ekran.
Gdx.gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// Czyści ekran
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

    batch.setProjectionMatrix(camera.combined);
    batch.begin();
    if(stanGry==StanGry.turaO||stanGry==StanGry.turaX)
    {
            batch.draw(gui,0,0);
            for(int x=0;x<3;x++)
            for(int y=0;y<3;y++)
            {
                    if(pole[x][y]==Pole.x)
                            batch.draw(texX,
                                            85*3-x*85-(texX.getWidth()/2-85/2)-85,
                                            85*3-y*85-(texX.getWidth()/2-85/2)-85);
                    if(pole[x][y]==Pole.o)
                            batch.draw(texO,
                                            85*3-x*85-(texX.getWidth()/2-85/2)-85,
                                            85*3-y*85-(texX.getWidth()/2-85/2)-85);
            }
    }
    else if(stanGry==StanGry.wygrana)
    {
            batch.draw(win,0,0);
    }
    else if(stanGry==StanGry.przegrana)
    {
            batch.draw(lose,0,0);
    }
    else if(stanGry==StanGry.remis)
    {
            batch.draw(tie,0,0);
    }
    batch.end();

}[/code]

I voila~! Gotowiec można zobaczyć w „02_TicTacToe”.

Podsumowanie

No to skończyliśmy pierwszy projekt w LibGDX. Ale… To raczej za mało nawet na minigrę. Pomocny w dopracowaniu tej gry może być kolejny rozdział. Na koniec mam jeszcze zadanie dla ciebie: * Dodaj do tej gry jakieś dźwięki a może nawet muzykę. * Stwórz menu i może jakieś GUI. * Dodaj możliwość grania z drugim graczem.

Klasa Gdx

Klasa główna biblioteki LibGDX która przechowuje w zmiennych statycznych, moduły odpowiedzialne za sterowanie aplikacja, grafiką, dźwiękiem, urządzeniami wejścia i plikami. Tutaj omówię niektóre funkcje każdej z nich.

  • Application(Gdx.app) – Moduł odpowiedzialny za kontrole nad aplikacją.
  • exit()
  • log & debug & error (String tag, String msg) – Wyświetla komunikat w konsoli
    tag : msg
  • setLogLevel(int level)
    0 – brak wiadomości, 1 – wyświetla tylko błędy, 2 – błędy+log, 3 - błędy+logi+debug
  • getType() - Zwraca typ aplikacji(ApplicationType)
    public enum ApplicationType {
    Android, Desktop, Applet, WebGL, iOS}
    • Graphics(Gdx.graphics)
    • getDeltaTime() - http://en.wikipedia.org/wiki/Delta_timing
    • getFramesPerSecond()
    • getHeight()
    • getWidth()
    • setVSync()
    • Audio(Gdx.audio) - Obsługuje OGG, MP3 i WAV.
    • newMusic(FileHandle) – zwraca nowa instancję Music. Jest streamowany bezpośrednio z pliku.
    • newSound(FileHandle) – zwraca nowa instancję Sound. Jest wczytywany z pamięci.
    • Input(Gdx.input)
    • getAccelerometerX() - Dostępne są 3 osie X,Y,Z. Zakres -10 do 10.
    • getX () & (int) – Dwie osie X,Y. Zwraca współrzędną ostatniego dotknięcia.
    • getDeltaX () & (int) – Podobnie jak na górze. Zwraca różnicę pomiędzy aktualną a ostatnią pozycją wskaźnika.
    • isTouched () & (int) – Zwraca bool.
    • setInputProcessor(InputProcessor) – Ustawia „przetwarzarkę” urządzeń wejścia (class Guziki extends InputProcessor)
    • Files(Gdx.files) - Pozwala na : zapisywanie, kopiowanie, przenoszenie, usuwanie, pobieranie listy i sprawdzanie czy plik bądź folder istnieje.
      FileHandle handle = Gdx.files.classpath(“plik.png”);
      FileHandle handle = Gdx.files.absolute(“c:/dane/plik.txt”);
    • Classpath – Pliki z kodem źródłowym. Tylko do odczytu. Działa tylko na desktopie i androidzie. Twórcy mówią żeby tego unikać.
    • Internal – Dostęp do zawartości aplikacji(np. folderu assets). Tylko do odczytu. Jeżeli nie może uzyskać dostępu do pliku to próbuje metodą Classpath. Ta metoda działa nawet z HTML5.
    • Local – Pliki lokalne aplikacji. Możliwy odczyt i zapis. (zapisy gry, cache)
    • External – Na androidzie jest to katalog główny karty SD, w desktopie leżą w katalogu użytkownika.
    • Absolute – Dostęp do wszystkich plików na urządzeniu. Unikać w miare możliwości.
      Przy Classpath i Internal wyrzuca GdxRuntimeException jeżeli plik nie został odnaleziony.
    • GLCommon(gl), GL10(gl10), GL11(gl11), GL20(gl20), GLU(glu)

Klasa startująca - Android

Z kodem startującym w androidzie jest podobnie jak z desktopową wersją, z tą różnicą iż nie używamy klasy Main tylko używamy androidowego Activity i konfiguracja w większości znajduje się w AndroidManifest.xml. W nim ustawiamy orientację ekranu i przywileje których będziemy potrzebować w aplikacji.
Przywileje w androidzie wyglądają mniej więcej tak:

<uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.WAKE_LOCK"/>

Libgdx można użyc do zrobienia ruchomej tapety:
https://code.google.com/p/libgdx/wiki/ApplicationConfiguration#Live_Wallpapers
Oraz „Daydream” który jest tłem wyświetlanym podczas odblokowywania:
https://code.google.com/p/libgdx/wiki/ApplicationConfiguration#Daydreams

Klasa startująca - iOS

Ta wersja opiera się na Xamarin's MonoDevelop IDE i Monotouch. Punktem wejściowym w Monotouch jest AppDelegate znajdujący się w Main.cs, zaś konfiguracja znajduje się w pliku Info.plist. Więcej informacji można znależć w tym linku: [url]https://code.google.com/p/libgdx/wiki/ApplicationConfiguration#iOS[/url]

Klasa startująca - HTML5

(gwt) Punktem wejściowym tutaj jest GwtApplication który znajduje się w GwtLauncher.java i jest złożony z dwóch metod getConfig() i getApplicationListener() która na podstawie configu tworzy instancję aplikacji. Więcej informacji tutaj: [url]https://code.google.com/p/libgdx/wiki/ApplicationConfiguration#HTML5/GWT[/url]

Download dla reszty platform:

Android: ADT Bundle(~400mb) zawiera sdk androida i downloader api oraz Eclipse 4.2(zwany także ADT) z pluginami pod deploy na androida. http://developer.android.com/sdk/index.html Przystosowywanie swojej wersji eclipse pod deploy na Androida znajdziesz pod tym linkiem [url]http://developer.android.com/sdk/installing/installing-adt.html[/url]

HTML5: GWT(Google Web Toolkit) wymagany jest do tworzenia wersji HTML5. Instrukcja do instalacji tego plugina w eclipse: https://developers.google.com/eclipse/docs/install-eclipse-4.2
Podstawy Libgdx by Daniel Debert is licensed under aCreative Commons Uznanie autorstwa-Użycie niekomercyjne-Bez utworów zależnych 3.0 Polska License.


#2

Uważam, że artykuł godny uwagi. Dużo ciekawych informacji, a co najważniejsze praktycznych. Jestem zwolennikiem nauka po przez przykłady i tutaj to widzę. Nie ma czystej teorii, ale i praktyka. Dla uzupełnienie informacji proponuję zajrzeć na tego bloga. Jest on przeznaczony głównie dla AndroidStudio, jeśli komuś Eclipse nie odpowiada.