Ganymede – co nowego?

Pobawiłem się już chwilę najnowszą edycją Eclipse, wg mnie najważniejsze nowości to:

Po zerowe: ładniejszy splash:

Free Image Hosting at www.ImageShack.us vs Free Image Hosting at www.ImageShack.us

Po pierwsze – jest szybszy! Ładowanie projektu trwa krócej i całe środowisko daje subiektywne wrażenie szybszego działania 🙂

Po drugie – breadcrumbs, czyli rewelacyjne uproszczenie w nawigacji:

Free Image Hosting at www.ImageShack.us

Po trzecie: nie udało mi się do tej pory ani razu wywrócić nowego eclipse’a, mimo że wypchałem go już moimi ulubionymi pluginami. Super!

Po czwarte: ‘ktoś’ wreszcie wymyślił, że update’y były skopane i stworzono zupełnie nowy ekran:

Free Image Hosting at www.ImageShack.us

Java Monkey Engine with Eclipse Ganymede

25 czerwca wydana została nowa wersja Eclipse o kosmicznej nazwie Ganymede. Mimo, że na co dzień używam czegoś zupełnie innego, muszę zobaczyć co u konkurencji słychać 😀 Dodatkowo z okazji wydania Ganymede fundacja Eclipse zorganizowała Ganymede Around the World Contest. Ja z kolei od jakiegoś czasu bawię się Java Monkey Engine, wiec postanowiłem coś o nim napisać.

Na początek… co to jest JME?

To pełnoprawny (czyt. w pełni wypasiony :D) silnik do tworzenia gier, wspierający grafikę przestrzenną i dzwięk 3d. Ale jak to, w Javie? A tak! Oczywiście zgodnie z filozofią platformy, nie obeszło sie bez natywnych bibliotek, ale coś za coś, a tutaj ‘cały komplet’ cosiów jest dostarczony wraz z jme.

Zaczynamy: z repozytorium sciągamy aktualną wersję JME2, kompilujemy (ant dist-all) i dodajemy do naszego projektu biblioteki. Istnieje też repozytorium jme na java.net, ale zawiera kod w starszej (1.x) wersji.

Ponieważ będzie to pierwsza aplikacja, nie będzie robiła niczego skomplikowanego – ot, zainicjalizujemy sobie wyświetlacz i pokażemy kilka obiektów.

Glówna klasa naszego programu oparta będzie o klasę com.jme.app.SimpleGame – dzięki temu nie będziemy musieli (tym razem!) wykonywać wielu czynności ręcznie – implementacji dostarczy nam właśnie klasa SimpleGame. Pozostaje ja tylko rozszerzyć 🙂


1
public class Demo extends com.jme.app.SimpleGame {<br /><br />   public static void main(String[] args) {<br />       Demo demo = new Demo();<br />       demo.start();<br /><br />       System.out.println("Done with the game");<br />   }<br /><br />   @Override<br />   protected void simpleInitGame() {<br />       // TODO Auto-generated method stub<br />   }<br />}

Z ciekawości i niecierpliwości spróbujemy uruchomić już teraz i… Niestety, nie działa.


1
<br />   Jul 16, 2008 12:35:47 AM com.jme.app.BaseGame start<br />   INFO: Application started.<br />   Jul 16, 2008 12:35:47 AM com.jme.system.PropertiesGameSettings &lt;init&gt;<br />   INFO: PropertiesGameSettings created<br />   Jul 16, 2008 12:35:47 AM com.jme.system.PropertiesGameSettings load<br />   WARNING: Could not load properties. Creating a new one.<br />   Jul 16, 2008 12:35:47 AM com.jme.app.BaseSimpleGame initSystem<br />   INFO: jME version 2.0 dev build 1<br />   Jul 16, 2008 12:35:47 AM com.jme.input.joystick.DummyJoystickInput &lt;init&gt;<br />   INFO: Joystick support is disabled<br />   Jul 16, 2008 12:35:47 AM com.jme.system.lwjgl.LWJGLDisplaySystem &lt;init&gt;<br />   INFO: LWJGL Display System created.<br />   Jul 16, 2008 12:35:47 AM class pl.ags.jme.Demo start()<br />   SEVERE: Exception in game loop<br />   java.lang.UnsatisfiedLinkError: no lwjgl in java.library.path<br />      at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1682)<br />      at java.lang.Runtime.loadLibrary0(Runtime.java:823)<br />      at java.lang.System.loadLibrary(System.java:1030)<br />      at org.lwjgl.Sys$1.run(Sys.java:72)<br />      at java.security.AccessController.doPrivileged(Native Method)<br />      at org.lwjgl.Sys.doLoadLibrary(Sys.java:65)<br />      at org.lwjgl.Sys.loadLibrary(Sys.java:81)<br />      at org.lwjgl.Sys.&lt;clinit&gt;(Sys.java:98)<br />      at org.lwjgl.opengl.Display.&lt;clinit&gt;(Display.java:128)<br />      at com.jme.system.lwjgl.LWJGLDisplaySystem.getValidDisplayMode(Unknown Source)<br />      at com.jme.system.lwjgl.LWJGLDisplaySystem.selectMode(Unknown Source)<br />      at com.jme.system.lwjgl.LWJGLDisplaySystem.initDisplay(Unknown Source)<br />      at com.jme.system.lwjgl.LWJGLDisplaySystem.createWindow(Unknown Source)<br />      at com.jme.app.BaseSimpleGame.initSystem(Unknown Source)<br />      at com.jme.app.BaseGame.start(Unknown Source)<br />      at pl.ags.jme.Demo.main(Demo.java:10)<br />   Jul 16, 2008 12:35:47 AM com.jme.app.BaseSimpleGame cleanup<br />   INFO: Cleaning up resources.<br />   Jul 16, 2008 12:35:47 AM com.jme.app.BaseGame start<br />   INFO: Application ending.<br />   Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class org.lwjgl.opengl.Display<br />      at com.jme.system.lwjgl.LWJGLDisplaySystem.close(Unknown Source)<br />      at com.jme.app.BaseGame.quit(Unknown Source)<br />      at com.jme.app.BaseSimpleGame.quit(Unknown Source)<br />      at com.jme.app.BaseGame.start(Unknown Source)<br />      at pl.ags.jme.Demo.main(Demo.java:10)<br />

Wpadliśmy, w dodatku przez te ‘nieszczęsne’ bindingi. 🙂 Na szczęście rozwiązanie problemu jest banalnie proste, wystarczy zadeklarować w parametrze java.library.path podać lokalizację bibliotek odpowiednich dla naszego systemu.

Np: Free Image Hosting at www.ImageShack.us
-Djava.library.path=c:/workspace/ganymede/jmeDemo/lib/lwjgl/native/win32

Spróbujmy jeszcze raz: coś sie włącza, (dzwięku nie ruszaliśmy, więc nie buczy), mruga – Hurra! Great success! Ale: co się dzieje, black screen?! Chociaz nie, w prawym górnym rogu jest napisane “F4 – toggle stats”. Czyli jednak działa 🙂 Czas więc najwyższy dodać cokolwiek do naszej gry, zeby nie wyglądala jak tor dla pcheł. Zrobimy sobie bardzo biedny model jakiegoś układu słonecznego. Układ składał się będzie z planety i gwiazdy.

new Sphere(“planet”, 10, 10, 10);

tworzy nową kulę o nazwie “planet” w polożeniu (10, 10, 10). Żeby nie było statycznie (nudno), planeta będzie kręcić się wokół własnej osi, a gwiazda rosnąć 🙂 Umiesćmy więc w metodzie simpleUpdate wywołania rotateSphere() i growSphere().


1
protected void simpleUpdate() {<br />   rotateSphere(planet);<br />   growSphere(star);<br />}<br /><br />private void rotateSphere(Sphere sphere) {<br />   angle = angle + (tpf * 1);<br />   if (angle &amp;gt; 360)<br />      angle = 0;<br /><br />   rotationQuat.fromAngleAxis(angle, axis);<br />   sphere.setLocalRotation(rotationQuat);<br />}<br /><br />private void growSphere(Sphere sphere) {<br />   Vector3f scale = sphere.getLocalScale();<br />   scale = scale.mult(1.0001f);<br />   sphere.setLocalScale(scale);<br />}<br />

1) www.ImageShack.us

1
demo.setConfigShowMode(AbstractGame.ConfigShowMode.AlwaysShow);


Metoda setCofnigShowMode ustala w jakich okolicznościach wyświetlone będzie okno z ustawieniami (wybiera sie rozdzielczość, głębię kolorów, tryb pełnoekranowy, renderer). My chcemy, zeby bylo widoczne za kazdym razem.

2) metoda simpleUpdate() jest wykonywana przy każdym przebiegu glównej pętli gry.
www.ImageShack.us

A tutaj cały kod naszej klasy (nie tej).


1
public class Demo extends com.jme.app.SimpleGame {<br />   private Quaternion rotationQuat = new Quaternion();<br />   private float angle = 0;<br />   private Vector3f axis = new Vector3f(1, 1, 0);<br /><br />   private Sphere planet;<br />   private Sphere star;<br /><br />   public static void main(String[] args)<br />   {<br />      Demo demo = new Demo();<br /><br />      demo.setConfigShowMode(AbstractGame.ConfigShowMode.AlwaysShow);<br />      demo.start();<br />   }<br /><br />   protected void simpleUpdate()<br />   {<br />      rotateSphere(planet);<br />      growSphere(star);<br />   }<br /><br />   private void rotateSphere(Sphere sphere)<br />   {<br />      angle = angle + (tpf * 1);<br />      if (angle &gt; 360)<br />      {<br />         angle = 0;<br />      }<br /><br />      rotationQuat.fromAngleAxis(angle, axis);<br />      sphere.setLocalRotation(rotationQuat);<br />   }<br /><br />   private void growSphere(Sphere sphere)<br />   {<br />      Vector3f scale = sphere.getLocalScale();<br />      scale = scale.mult(1.0001f);<br />      sphere.setLocalScale(scale);<br />   }<br /><br />   @Override<br />   protected void simpleInitGame()<br />   {<br />      display.setTitle(&quot;java malpa silnik&quot;);<br /><br />      planet = createEarth();<br />      star = createSun();<br />      rootNode.attachChild(star);<br />      rootNode.attachChild(planet);<br /><br />      try<br />      {<br />         MultiFormatResourceLocator loc2 = new MultiFormatResourceLocator(<br />            new File(&quot;c:/workspace/ganymede/jmeDemo/model&quot;).toURI(),<br />            &quot;.jpg&quot;, &quot;.png&quot;, &quot;.tga&quot;);<br />         ResourceLocatorTool.addResourceLocator(<br />            ResourceLocatorTool.TYPE_TEXTURE, loc2);<br />      }<br />      catch (Exception e)<br />      {<br />         e.printStackTrace();<br />      }<br /><br />      URL grass = ResourceLocatorTool.locateResource(<br />         ResourceLocatorTool.TYPE_TEXTURE, &quot;model/grass.jpg&quot;);<br />      URL sun = ResourceLocatorTool.locateResource(<br />         ResourceLocatorTool.TYPE_TEXTURE, &quot;model/sun.jpg&quot;);<br /><br />      TextureState grassTexture = loadTexture(grass);<br />      TextureState sunTexture = loadTexture(sun);<br /><br />      planet.setRenderState(grassTexture);<br />      star.setRenderState(sunTexture);<br />   }<br /><br />   private Sphere createEarth()<br />   {<br />      Sphere s = new Sphere(&quot;Earth&quot;, 5, 10, 15);<br />      s.setLocalTranslation(new Vector3f(0, 0, -40));<br />      s.setModelBound(new BoundingSphere());<br />      s.updateModelBound();<br /><br />      return s;<br />   }<br /><br />   private Sphere createSun()<br />   {<br />      Sphere s = new Sphere(&quot;Sun&quot;, 100, 100, 100);<br />      s.setLocalTranslation(new Vector3f(0, 0, -500));<br />      s.setModelBound(new BoundingSphere());<br />      s.setLocalScale(2);<br />      s.updateModelBound();<br /><br />      return s;<br />   }<br /><br />   private TextureState loadTexture(URL u)<br />   {<br />      TextureState ts = display.getRenderer().createTextureState();<br />      ts.setEnabled(true);<br />      ts.setTexture(TextureManager.loadTexture(u,<br />                                               Texture.MinificationFilter.BilinearNearestMipMap,<br />                                               Texture.MagnificationFilter.Bilinear));<br />      return ts;<br />   }<br />}<br />