Java Monkey Engine with Eclipse Ganymede
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 <init><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 <init><br /> INFO: Joystick support is disabled<br /> Jul 16, 2008 12:35:47 AM com.jme.system.lwjgl.LWJGLDisplaySystem <init><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.<clinit>(Sys.java:98)<br /> at org.lwjgl.opengl.Display.<clinit>(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:
-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 &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 | 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.
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 > 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("java malpa silnik");<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("c:/workspace/ganymede/jmeDemo/model").toURI(),<br /> ".jpg", ".png", ".tga");<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, "model/grass.jpg");<br /> URL sun = ResourceLocatorTool.locateResource(<br /> ResourceLocatorTool.TYPE_TEXTURE, "model/sun.jpg");<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("Earth", 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("Sun", 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 />