Let’s make things Groovy

Jakiś czas temu przeczytałem artykuł (http://steve-yegge.blogspot.com/2007/12/codes-worst-enemy.html) w którym autor dość nieprzychylnie wyrażał się o Javie jako języku programowania. Mimo, że nie całkiem zgadzam się ze wszystkim co zostało tam napisane to jednak zacząłem zwracać uwagę na to ile kodu wymaga Java nawet do najprostszych rzeczy. Zerknijmy sobie na prosty przykład – bardzo standardowe „Hello World”:

public class Main {
  public static void main(String[] args) {
    System.out.println("Hello World!");
  }
}

Już patrząc na samą instrukcję System.out.println() odnosi się wrażenie, że to jest dużo pisania jak na tak prostą funkcję. Dla porównania możemy spojrzeć na kod Pythona:

print "Hello World"

Super! Krótko zwięźle i na temat. Problem w tym, że my znamy się dobrze na Javie i potrafimy używać przeróżnych bibliotek do tego języka, a w dodatku nie znosimy nazewnictwa metod w takiej_postaci() preferując raczej postaćTakową(). No dobra, może nie wszyscy mają takie uprzedzenia ale ja na pewno tak 🙂 (na pewno nie wydaje się wam, że Python ma za dużo __podkreśleń__?). A jeśli to nie jest problem to może brak standardu dla dokumentacji Pythonowej? Lub to, że część bibliotek dla Pythona dokumentacji nie ma wcale lub sprowadza się ona po prostu do listy wszystkich klas i metod, które czasem są opatrzone lapidarnym komentarzem? (mówię o czymś w stylu: get_none() – w dokumentacji przeczytamy returns None…)

Jeśli chcecie teraz zaproponować mi Jythona to dajcie sobie spokój. Byłem, sprawdziłem, nie podoba mi się 🙂 Najbardziej odrzuca mnie czas startu ale samo tempo pracy również nie powala. Możecie mi wierzyć, że bardzo chciałem sprawić by Python stał się moim językiem ojczystym do programowania, ale ostatecznie zawsze wracałem do Javy.

Potem odkryłem Groovy

O Groovy słyszałem już wcześniej. Nikt jednak nie wydawał się podekscytowany tym językiem i nikt nie zwracał na niego większej uwagi. W takim razie, pomyślałem, ja też nie będę. Ostatnio jednak w wyniku poszukiwania języka innego niż Java ale działającego na JVM (chodziło mi o kompatybilność z bibliotekami) postanowiłem zabrać Groovy na jazdę testową.

To była chyba najbardziej inspirująca zabawa z kodem odkąd zacząłem programowanie! W ciągu weekendu przeczytałem wszystkie interesujące mnie rzeczy dotyczące języka (głównie – co jest tu innego niż w Javie, wszelkie metaprogramowanie oraz ciekawostki na które się natknąłem jak np. DSL w Groovy). Najciekawsze jednak było to, że czułem się jakbym odkrył nowe możliwości w czymś co znam już od dawana. Pisałem po prostu w Javie ale wykorzystywałem dodatkowe możliwości, które zupełnie zmieniły styl kodu i wygodę programowania.

Co jest wartego uwagi w Groovy?

Przede wszystkim – jest to język dynamicznie typowany, choć trzeba zaznaczyć, że jest to nieco ciekawsze niż w przypadku Pythona gdyż możemy podawać typy analogicznie jak w Javie:

// Przykład 1
def greet(String who) {
  println "Hello $who!"
}

def greet2(who) {
  println "Hello $who!"
}

greet("Groovy")
greet2 "Groovy"
greet2 12345

W tym przykładzie widać kilka nietypowych dla Javy konstrukcji, ale zacznijmy od tematu omawianego wcześniej. Mamy zdefiniowane dwie funkcje. Pierwsza z nich posiada zastrzeżenie, że argument who musi być typu String. Próba wywołania takiej metody z argumentem innego typu skończy się wyrzuceniem odpowiedniego wyjątku (oczywiście w czasie wykonania programu). Druga funkcja nie posiada takiego ograniczenia i w tym wypadku możemy podawać właściwie dowolne obiekty. Oczywiście możemy również określić typ zwracany przez funkcję podając typ pomiędzy słowem kluczowym def a jej nazwą.

Dodatkowo w funkcjach i metodach nie musimy używać słowa return (zazwyczaj) gdyż ostatnia wartość jest tą zwracaną. To oznacza, że jeśli napiszemy funkcję, która kończy się linijką zawierającą po prostu liczbę: 25 to ta liczba będzie wartością całej funkcji (chyba, że wcześniej użyto słowa kluczowego return).

Kolejne niespodzianki czekają na nas już w linii numer 2! Zamiast przydługiego System.out.println mamy po prostu println. Mimo, że bardzo przypomina to trochę rozwiązanie zastosowane w języku Python wcale takie nie jest. Tak na prawdę wywoływany jest stary dobry System.out.println. Groovy przy starcie globalnie importuje statycznie wspomnianą metodę. Efekt jest dokładnie taki jakbyśmy napisali na początku każdej klasy Javy:

import static System.out.println;
// wówczas można tej metody używać tak:
println("Hello World");

Dodatkowo Groovy automatycznie importuje kilka przydatnych rzeczy do każdego kompilowanego pliku:

import java.lang.*;
import java.util.*;
import java.net.*;
import java.io.*;
import java.math.BigInteger;
import java.math.BigDecimal;
import groovy.lang.*;
import groovy.util.*;

Idąc dalej w drugiej linijce kodu zauważymy, że argument tej metody nie znajduje się w nawiasie. Jest to oczywiście uproszczenie wprowadzone w Groovy, żeby pozbyć się niepotrzebnych znaków z kodu przez co ma on stać się czytelniejszy. Oczywiście nic nie stoi na przeszkodzie, żeby nawiasów używać. Możemy zatem napisać: println(„Hello World”). Jedyny zgrzyt może pojawić się gdy chcemy wywołać funkcję lub metodę nie posiadającą argumentów. Wówczas kod wygląda identycznie jak dostęp do pola klasy, a jeśli Groovy go nie znajdzie to rzuci wyjątkiem informującym, że takiego pola nie ma. Ja preferuję pisanie nawiasów wszędzie (z wyjątkiem println [nie wiem czemu]) dlatego, że w innym wypadku moim zdaniem kod wygląda niespójnie.

Kontynuując naszą podróż po linijce numer 2 natrafimy na $who w środku stringa. Jest to kolejny cukierek od Groovy. Możemy w ten sposób wstawiać dowolne wartości do ciągu znaków. Co jest jeszcze ciekawsze? Oczywiście to, że dodając klamry możemy w ten sposób wykonać w międzyczasie dowolny kod Groovy:

println "Test: ${def b = 30; ++b}"
// Wyświetli: "Test: 31"

Docierając do końca 2 linijki możemy zauważyć brak średnika. Jest on oczywiście opcjonalny. Niektórym się to podoba, innym nie, ale to nie ma większego znaczenia – każdy pisze jak mu się podoba 🙂 Warto jednak pamiętać, że średnik rozdziela instrukcje i, tak jak w powyższym przykładzie, czasami może się przydać.

Coś więcej?

Hmm… znajdzie się więcej 🙂 Prawdziwą siłą Groovy są domknięcia (ang.: Closures – tak po polsku brzmi jakoś głupawo). Domknięcie jest, ujmując to najprościej, kawałkiem kodu, który nie ma nazwy i może być wywołany zupełnie jak funkcja. Deklaruje się je przy pomocy klamer {}. Do czego można je wykorzystać? Całkiem fajny przykład podał Andrew Montalenti w swoim poście i pozwolę sobie go przytoczyć:

["Rob", "Christopher", "Joe", "John"].findAll{ 
  it.size() < = 4 
}.each { 
  println it 
}
// ==> prints "Rob", "Joe" and "John"

Groovy dodaje kilka przydatnych metod do pracy na tablicach i listach (m. in. findAll i each). Być może niektórym z was przypomina to trochę jQuery – i dobrze 🙂 W tym przykładzie po prostu mówimy, że chcemy znaleźć wszystkie elementy tablicy, których długość jest mniejsza od 4, a potem każdy z nich wyświetlić. Tak na prawdę ten opis jest zupełnie zbędny bo nawet źrednio rozgarnięty programista nawet nie znający Groovy zrozumie o co tu może chodzić 🙂

Closures w Groovy są traktowane trochę specjalnie. Jeśli metoda jako ostatni argument posiada właśnie domknięcie to może ono być podane poza nawiasem tej metody. To znaczy, że mając:

// metoda wywoluje domknięcie z parametrem s
def metoda(String s, Closure c) {
	c(s);
}

Możemy użyć takiej metody na dwa sposoby:

metoda("hello", { println it })

metoda("hello") { println it }

Polecam jednak odwiedzić stronę Groovy i poczytać więcej. Jeśli ktoś zna już Javę to nauka Groovy jest stosunkowo bezbolesna. Właściwie jest to wręcz przyjemność bo możemy zamienić często długi kod javy na krótsze odpowiedniki w Groovy.

A wydajność?

Są pewne argumenty przeciw używaniu Groovy. Jak każdy język dynamiczny posiada on pewien narzut obliczeniowy, który powoduje, że jest on wolniejszy niż zwykła Java. Jak dotąd w celu zwiększenia wydajności pewnej częśći kodu można było użyć adnotacji @CompileStatic. Niestety użycie jej mogło spowodować, że kod zachowa się nieco inaczej niż byłoby to w Groovy.

Na nasze szczęście przyszła Java 7, a wraz z nią JVM uzyskał nową funkcję o nazwie InvokeDynamic. Powinna ona pozwolić Groovy na znaczne zbliżenie się do wydajności Javy. Na razie nie ma tego wiele, ale niektórzy twierdzą, że jego wykorzystanie faktycznie poprawiło znacząco predkość.

Nie obiecuję, że jeszcze napiszę coś o Groovy bo zawsze kiedy coś podobnego obiecuję to ostatecznie i tak tego nie robię 🙂 Dlatego po prostu miejmy nadzieję, że się uda 😉

Ostatnie prace

Dawno nie zajrzałem, ani nie napisałem 😉

Wszystko dlatego, że specjalnie nie miałem o czym pisać ponieważ zajmowałem się głównie rzeczami uczelnianymi, a kto chce słuchać o podstawach sieci neuronowych albo javowych technologiach? 😉

W międzyczasie przypatrywałem się grze Minecraft. Mimo tego, że na pierwszy rzut oka wygląda okropnie (w sumie to na każdy następny rzut oka wygląda równie słabo), a gra nie ma za specjalnie celu wielu ludzi nie może zrozumieć dlaczego, w ogóle taka gra istnieje i ma się dobrze… Początkowo też nie byłem przekonany ale po jakimś czasie okazało się, że gra jest niezwykle grywalna! Niemały wpływ na taki stan rzeczy ma fakt, że świat zbudowany z klocków można całkowicie przemodelować przy użyciu kilofa, łopaty i siekiery (które trzeba najpierw sobie zrobić).

Tak czy inaczej nie chciałem zachwalać Minecrafta bo czy ktoś go lubi czy nie to już kwestia indywidualna. Postanowiłem stworzyć coś podobnego do Minecrafta tylko idącego w nieco innym kierunku bardziej skupiając się na drugiej części nazwy – craftingu. Oczywiście zanim będzie można zabrać się w ogóle za tworzenie czegokolwiek potrzebujemy głównie świata, w którym będziemy pracować 😉 Bez tego ciężko pozyskać surowce.

W czasie świąt udało mi się zmusić silnik jMonkey Engine 3 do wyświetlania świata złożonego z bloczków. Efekt można podziwiać na poniższym screenie:

W chwili obecnej możliwe jest niszczenie i dostawianie bloków różnych typów. Dodatkowo ostatnim osiągnięciem jest oświetlenie per-blokowe 😉

Oświetlenie zrealizowane jest przy pomocy kolorów wierzchołków, które są mieszane z teksturą przy pomocy prostego pixel shadera. Brakuje tutaj porządnego algorytmu do zaciemniania miejsc zakrytych przed źródłem światła innymi blokami.

Ostatnimi dwiema rzeczami, które potrzebuję do osiągnięcia pełni szczęścia w tej chwili jest utworzenie generatora terenu, który potrafiłby „dogenerować” kawałki terenu jeśli byłby potrzebny oraz zaimplementowanie jakiejś prostej fizyki by móc się poruszać po świecie.

Do tej pory do fizyki używałem zintegrowanego z jME3 silnika fizyki jBullet, ale wydaje się to być trochę wyciąganiem armaty na muchę…. Pomijam już fakt, że przy zmienianiu geometrii świata trzeba też było generować na nowo CollisionShape dla danego kawałka terenu, a to zabiera kilka cennych kwantów czasu co jest niedopuszczalne! 😉

Tak więc… nie leniłem się ostatnio choć może tak to wyglądało kiedy nic nie pisałem :p Jeśli macie jakiś pomysł jak stworzyć generator terenu, który można tu wykorzystać to jestem otwarty na propozycje 🙂

Przypuszczam, że w przyszłości pojawi się jeszcze jakiś wpis na temat tworzonego przeze mnie… czegoś 😉

Jak wywołać z Javy konkretną funkcję skryptu JavaScript

Wczoraj opisałem trochę w jaki sposób można wykonać skrypt JS używając silnika Mozilla Rhino. Wczoraj byłem tak podekscytowany tym, że wreszcie mi się udąło, że zapomniałem napisać z czego właściwie korzystałem 😉

Tak czy inaczej samo wykonanie skryptu to jeszcze nie wszystko. Gdybyśmy chcieli mieć w grze wiele różnych obiektów wykonujących kilka zadań w zależności od potrzeb to musielibyśmy dla każdej funkcji pisać osobny plik skryptu. Ani to wygodne, ani eleganckie dlatego też warto było poświęcić wczoraj kilka chwil na zastanowienie się w jaki sposób wywoływać po nazwie funkcje JS z kodu Javy.

Aby to osiągnąć najpierw trzeba załadować skompilowany skrypt tak jak w ostatnim poście.

URL[] urls;
urls = new URL[] { new File('/home/morti/NetBeansProjects/RhinoJS/scripts').toURI().toURL() };
URLClassLoader loader = new URLClassLoader(urls);
Class cl = Class.forName('Base', true, loader);

Script scr = (Script) cl.newInstance();

Następnie należy utworzyć/pobrać kontekst wykonywania oraz przestrzeń:

Context cx = Context.enter();
Scriptable scope = cx.initStandardObjects();

Na stronie o silniku RhinoJS nie rozwodzili się przesadnie nad tym czym jest kontekst ale dość jasno zaznaczyli, że ma on występować jeden na wątek. Na szczęście funkcja enter() załatwia wszystko za nas. Jeżeli w tym wątku nie ma jeszcze kontekstu to zostanie utworzony a jeśli jest to zostanie pobrany i zwiększony jego licznik referencji. Skoro jest licznik referencji to pewnie trzeba też jakoś informować o oddaniu obiektu no i rzeczywiście: mamy metodę Context.exit().

A do czego te przestrzenie? No cóż. Służą one głównie do przechowywania środowiska wykonywania skryptu (np. zdefiniowanych zmiennych oraz ich wartości). Możemy mieć takich przestrzeni ile dusza zapragnie no i dobrze 🙂 Cała sztuczka polega na tym, że dla każdego obiektu w grze będziemy mieli jedną przestrzeń, która będzie przechowywała stan zmiennych oraz treści funkcji. Za chwilę postaram się wytłumaczyć dlaczego tyle tych scopów będziemy potrzebować. Najpierw jednak dam następny fragmencik kodu:

scr.exec(cx, scope);

Być może dla niektórych jest już jasne dlaczego potrzebujemy wiele przestrzeni. Napiszę jednak, aby wszyscy mogli wyciągnąć z tego tekstu wartościowe informacje 🙂 Jak widzimy powyżej należy wykonać załadowany skrypt. Trzeba wykonać ten krok aby zdefiniować w przestrzeni funkcje. Jeśli będziemy mieli wiele obiektów używających skryptów tej samej postaci (z tymi samymi nazwami funkcji) to gdybyśmy zdefinoiwali je wszystkie w jednej przestrzeni to tylko funkcje ostatniego skryptu byłyby wykonywane dla wzsystkich gdyż nadpisałyby one definicje poprzednich skryptów. Dlatego każdy obiekt w grze powinien mieć włąsną przestrzeń wykonywania skryptu.

Jak dotąd w sumie zrobiliśmy to samo co w ostatnim poście z tą różnicą, że dziś na tym nie skończymy 😉 No i oczywiście opisałem nieco znaczenie kilku wymaganych linijek. W całym kodzie trzeba oczywiście jeszcze umieścić odpowiednie bloki try{..} catch(…) ale nie używam tego tutaj ponieważ utrudniłoby to tylko czytanie przykładów.

Ostatnią rzeczą, którą trzeba wykonać jest pobranie obiektu funkcji z przestrzeni oraz wywołanie jej z podaniem parametrów:

Object fObj = scope.get('NazwaFunkcji', scope);
if(fObj instanceof Function) {
	Function f = (Function)fObj;

	result = f.call(cx, scope, scope, new Object[] {'Argument1', new Integer(44), arg3});
	System.out.println('Result: ' + result);
}

Powstaje naturalne pytanie dlaczego podajemy aż 2 razy scope skoro na zdrowy rozum możnaby go nie podawać wcale! Pobraliśmy przecież obiekt z tej przestrzeni więc o co tu chodzi. No cóż. chcąc być do końca szczerym to powiem, że nie zagłębiałem się w te przestrzenie tak mocno aby potrafić udzielić zadowalającej odpowiedzi. Mogę jedynie wkleić opis z dokumentacji:

java.lang.Object call(Context cx,
                      Scriptable scope,
                      Scriptable thisObj,
                      java.lang.Object[] args)

    Call the function. Note that the array of arguments is not
    guaranteed to have length greater than 0.

    Specified by:
        call in interface Callable

    Parameters:
        cx - the current Context for this thread
        scope - the scope to execute the function relative to. 
                This is set to the value returned by getParentScope() 
                except when the function is called from a closure.
        thisObj - the JavaScript this object
        args - the array of arguments 
    Returns:
        the result of the call

Być może komuś pryda się opisany tutaj sposób na wywołanie funkcji ze skompilowanych skryptów JavaScript. Może ktoś wie coś więcej i potrafi dodać coś do tematu? Ja z pewnością skorzystam z umiesczenia tutaj tego przykładu bo nie będę musiał go szukać w kodzie 😉

Slick

W jednym z poprzednich postów napisałem, że Slick udostępnia biblioteki natywne tylko dla Windowsa. Otóż dzisiaj przyjrzałem się temu dokładniej i okazało się, że po prostu Windowsowe nativy są w głównym katalogu natomiast pozostałe są popakowane w archiwa JAR i wrzucone do katalogu lib. Wcześniej ich po prostu nie zauważyłem.

W związku z poczynionym odkryciem uruchomiłem netbeansa na nowo zainstalowanym OpenSUSE i rozpocząłem poszukiwanie tutoriali w internecie. Tutaj niestety nie jest tak fajnie jak w GTGE. Dostępne są głównie przykładowe kody źródłowe. Udało mi się jednak znaleźć jeden tutorial ( http://thejavablog.wordpress.com/2008/06/08/using-slick-2d-to-write-a-game/ ), który pozwala załapać jak to mniejwięcej wygląda.

Prawdę mówiąc sam szkielet jest prawie taki sam jak to było w przypadku GTGE. Mamy metody init, update, render, których znaczenia i funkcji raczej nie trzeba tłumaczyć 😉

Slick jest pierwszym silnikiem 2D w Javie, w którym odkryłem możliwość dowolnego obracania obrazków. Dodatkowo przeglądając API zauważyłem, że posiada różne algorytmy wyszukiwania trasy, silnik cząsteczkowy (wraz z edytorem) oraz wsparcie dla tile-map rysowanych w programie Tiled.

Myślę że spędzę na tym silniku kilka ładnych godzin ponieważ zamierzam wziąć udział w tegorocznym WSOC! To chyba będzie pierwsza praca na WSOC napisana w Javie 😉

Refleksje w Javie

Poszukując nowych i ciekawych zastosowań Javy trafiłem na wzmiankę o mechanizmie pozwalającym ładować biblioteki (klasy) dynamicznie w trakcie działania programu. Właściwie to należało się spodziewać, że będzie to możliwe… Tak czy inaczej mechanizm ten nazwano Refleksjami.

Poszukałem trochę na temat tych refleksji i trafiłem na tutorial jak ich używać: http://www.programowanieobiektowe.pl/java_obiekty_refleksyjne.php.

Niestety to co się dzieje na tamtej stronie – w celu wykorzystania jednego malego obiektu bije na głowę nawet ładowanie zewnętrznych bibliotek w C… Dzieje się tak dlatego, że zakładamy, że dana bibioteko-klasa nie będzie miała nic wspólnego z naszym projektem. Widać zatem, że można załadować w ten sposób każdą klasę. Tylko po co skoro można to zrobić dużo, dużo prościej i przyjemniej. Czyniąc sobie pewne założenia na temat ładowanej klasy możemy znacznie uprościć używanie tej klasy zewnętrznej.

Jeśli mamy zamiar stworzyć sobie możliwość dodawania pluginów do własnego programu to prawdopodobnie stworzymy sobie interfejs Plugin. I tak na prawdę to tyle całkowicie wystarczy aby uprościć sprawę. Pokażę to „w kodzie”:

Plik Plugin.java:

interface Plugin {
    public String sayHello();
}

Plik MyPlugin.java który będzie ładowany dynamicznie:

class MyPlugin implements Plugin {
    public String sayHello() {
        return "Hello World!";
    }
}

No i teraz jeśli chcemy wykorzystać nasz plugin to wystarczy gdzieś wcisnąć coś takiego:

try {
    Plugin plugin = (Plugin) Class.forName("MyPlugin").newInstance();
    System.out.println(plugin.sayHello());
} catch(Exception e) {
    e.printStackTrace();
}

Jest to znacznie prostsze niż sprawdzanie każdej metody i jej argumentów jednak ma ograniczenie w postaci przymusu implementacji przez ładowaną klasę odpowiedniego interfejsu. No ale nie jest to chyba przeszkoda nie do przejścia 🙂

Golden T Game Engine

Postanowiłem wczoraj przetestować jeszcze jeden nieźle zapowiadający się silnik: Golden T Game Engine. Właściwie był to jedyny, który znalazłem (oprócz JGame) oferujący możliwość używania OpenGL jako backendu do renderowania.

Początkowo tutorial dostępny na stronie GTGE był dla mnie „nieprzyjazny” ponieważ ja tylko chciałem zobaczyć trochę kodu i pokombinować. Później jednak zmusiłem się aby go przeczytać i muszę przyznać, że jest to najlepszy tutorial wprowadzający do używania biblioteki jaki w życiu widziałem. Przyczyną tego stanu rzeczy jest niewielka ilość kodu obudowana w dość obszerną dawkę wiedzy na temat wewnętrznego działania konkrentych części silnika. Dzięki temu po przeczytaniu tutoriala znacznie łatwiej połapać się w dokumentacji.

Sama dokumentacja również zawiera przykłady użycia różnych klas. Dzięki temu nie trzeba czytać więcej tutoriali. Na dowód tego napisałem dziś (po raz kolejny) swoją wersję ponga o wiele mówiącej nazwie Jong. Niektórzy z was na pewno znają już to „boisko” 😉 Wykorzystałem trochę starej grafiki, ale stworzyłem nowe paletki oraz piłkę.

Podczas pisania nauczyłem się wielu rzeczy o tym silniku i muszę przyznać, że jest to najlepsze z badanych przeze mnie ostatnio wyjść. Najciekawsze jest to, że do rysowania można używać klasy java.awt.Graphics2D! Było to dla mnie miłe zaskoczenie po tym co widziałem w JGame…

Dodatkowo w silniku dostępne są klasy do obsługi różnego rodzaju obiektów tła. Domyślnie mamy dostępne klasy jak ColorBackground, ImageBackground, a nawet takie jak TileBackground, IsometricBackground oraz ParallaxBackground! A jest to tylko część silnika. Na prawdę zachęcam do obejrzenia i przetestowania tej biblioteki gdyż tworzenie gier z jej pomocą to prawdziwa przyjemność 🙂

Mała uwaga: Aby używać OpenGL należy ściągnąć ze strony jeszcze GTGE Add Ons. Można je znaleźć na TEJ stronie razem z kilkoma innymi dodatkami takimi jak system GUI dla GTGE.

Na koniec dam wreszcie link do tego Jonga. Nie jest to produkcja na miarę Obliviona czy nawet Mario Bros…. Tak czy inaczej… chodziło bardziej o przetestowanie niż zrobienie czegoś wartościowego 🙂 Grę można ściągnąć tu:

http://www.mediafire.com/?sharekey=cb92a7ab0b4a9246cb36451a58fbe7c44b232d45b6124d0e

U siebie sprawdziłem działanie na Ubuntu Linux 9.04 oraz Windows XP. Ta wersja jest „domyślną” i nie wykorzystuje OpenGL jednak radzi sobie świetnie w obu systemach.

JGame

Ostatni post napisałem na temat prób pracy z grafiką w czystej Javie bez dodatkowych bibliotek. Okazało się, że proste gry, w których grafiki zbyt wiele nie ma, dadzą się napisać nawet w taki sposób, jednak wszyscy wiemy, ze im szybciej tym lepiej. W poszukiwaniu prędkości rozpocząłem poszukiwania silników i frameworków do gier 2D w Javie. Natrafiłem na projekt SLICK, ale oblał egzamin zaraz po ściągnięciu na dysk – okazało się, że w paczce DLL-ki dla windowsa były, a SO dla Linuxa nie…  Zdegustowany ruszyłem w dalszą podróż…

Następna biblioteka na jaką trafiłem to JGame. Strona internetowa była dość mało zachęcająca, ale trzeba było dać jej chociaż szansę. Obejrzałem ubogą stronę z Tutorialami oraz aplety dla każdego z nich. Chwilę później zaczynałem już „testowanie”.

Już przy pierwszej „próbie” sił zostałem miło zaskoczony. Wrzuciłem na okienko 100 wykorzystywanych wcześniej obrazków PNG 40×40 z kanałem Alpha. FPS wyniósł 60. Nietrudno było się domyśleć, ze to wina v-sync, a nie prędkości. Postanowiłem więc zwiększyć nieco obciążenie dostawiając jeszcze jedno zero, czyli renderując 1000 tych obrazków. FPS wciąż był równy 60. Pomyślałem, że po dostawieniu jeszcze jednego zera efekt już powinien być widoczny. I był… Podczas renderowania 10 000 tych obrazków FPS spadł do ~10.

Kiedy już wiedziałem, ilu obrazków lepiej NIE rysować przyszedł czas aby znaleźć „optymalne” rozwiązanie… Okazało się, że przy 3000 obrazków prędkość umiejscowiła się około ~30 FPS, a to już wystarczy aby gra chodziła w miarę płynnie.

Dodam jeszcze, że wszystko sprawdzałem w rozdzielczości 640×480 oraz 800×600 i wyniki były niewiele gorsze w drugim przypadku.

Javę uruchamiam na moim laptopie z procesorem 2×1,8 GHz i kartą graficzną GeForce GO 7300 z użyciem systemu Ubuntu Linux 9.04. Jak widać da się osiągnąć całkiem zadowalające efekty na komputerach nie będących najnowszymi osiągnięciami techniki.

Pokombinuję jeszcze trochę z JGame bo ciekawie się zapowiada. Oprócz nienajgorszej wydajności oferuje jeszcze wiele „wspomagaczy” do tworzenia gier 2D. Polecam zapoznanie się z tym projektem.