Our research group has just released a simple game to help us investigate some recent theoretical results. Thanks to the awesome work by the JME developers, we’ve been able to quickly put together a simple game and roll it out as a stand-alone java game, a java applet, and an android app.
The game is a simple arcade runner-style (fly until you crash) game. Incidentally, the dynamics of the player in these kinds of game follow that of a simple model we’ve used to analyse fundamental limits on flight through a random forest (as by birds or unmanned aircraft).
Feel free to check it out and play for a bit (the more you play, the more data we get, the more we learn!)
http://ares.lids.mit.edu/forestrunner
The applet page has links to the android app in the Google Play store, a download link for the stand-alone Jarfile, and information and links to read about the theory and what we’re trying to accomplish.
Mostly though, thanks to the developers for sharing this library.
A note for linux users: iced-tea (open JDK plugin) can’t run the lwjgl applet. Either download the jarfile to play (works on open-jdk), or install the oracle proprietary java and it’s plugin. Some instructions to help can be found here: http://ares.lids.mit.edu/redmine/projects/forest-game/wiki/OracleJavaUbuntu
Good to see it as an android app! just played it very nice :), gave it 5 stars.
1.91MB file size awesome
The only problem I had was that my screen would darken after a while, and so I had to touch the screen to awaken it. Tried on the hardest settings, that’s so impossible
Once again, nice experiment!
Hey, great to hear from you. Theres a lot of projects like yours that stay below our radar, reports and feedback are much appreciated. The applet works fine on my MacBook, nice clean work. Did you use eclipse for the distribution or did you extend the ant build to create a “fat jar”? If so posting the targets here would be much appreciated, maybe it can be integrated as an option to the SDK.
Kind of fun little game. On the default settings, I could fly forever… I eventually just kamakazied a tree at 500 pts just to end my suffering.
On max density it was nice and tough. I’d kill for some analog controls, though. Probably spoils the point, I guess.
@wezrule said:
The only problem I had was that my screen would darken after a while, and so I had to touch the screen to awaken it.
Ah yes. Outstanding issue I forgot about. I need to figure out how to tell android not to go to sleep. It's probably very easy.
@normen said:
The applet works fine on my MacBook, nice clean work.
Thanks! I forgot to mention. For some reason on some macs the gui will flicker. I haven't figured out the issue but it seems to only affect machines with low-powered gfx cards.
@normen said:
Did you use eclipse for the distribution or did you extend the ant build to create a "fat jar"? If so posting the targets here would be much appreciated, maybe it can be integrated as an option to the SDK.
I ended up using eclipse for the distribution. I copied the applet pack from the JME and then wrote a simple ant script based on looking at the ant-build from jMP applet builder to build the code.jar and data.jar files. I was using jMP, but I'm much more familiar with eclipse than netbeans so it was much easier for me to setup the project in eclipse. I ended up creating three separate projects (one for each version: desktop, applet, android) and linking to a common source code directory. Within each project I'd customize the build path to link to distribution specific libraries and exclude certain classes which were specific to each build (for instance, the communication is abstracted so that there is a separate version of the backend for each distribution).
I'm not 100% sure if that answers your question. In any case, my ant script looks like this for the applet:
[xml]
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project default="all" name="Create Jar for forestrunner applet code">
<!--this file was created by Eclipse Runnable JAR Export Wizard-->
<!--ANT 1.7 is required -->
<target name="create_code_jar">
<jar destfile="/home/josh/Codes/java/forestrunner_applet/dist/code.jar" filesetmanifest="mergewithoutmain">
<manifest>
<attribute name="Class-Path" value="."/>
</manifest>
<fileset excludes="Interface/**" dir="/home/josh/Codes/java/forestrunner_applet/bin"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/eventbus.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/jME3-core.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/jME3-effects.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/jME3-lwjgl.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/jME3-networking.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/jME3-niftygui.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/jME3-plugins.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/nifty.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/nifty-default-controls.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/nifty-style-black.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/stack-alloc.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/vecmath.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/xmlpull-xpp3.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/apache_commons/commons-lang3-3.1/commons-lang3-3.1.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/google-gson-2.1/gson-2.1.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/sqlite4java/sqlite4java-282/sqlite4java.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/jME3-desktop.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/json-simple/json_simple-1.1-all/lib/json_simple-1.1.jar"/>
</jar>
</target>
<target name="create_data_jar">
<jar destfile="/home/josh/Codes/java/forestrunner_applet/dist/data.jar" filesetmanifest="mergewithoutmain">
<manifest>
<attribute name="Class-Path" value="."/>
</manifest>
<fileset excludes="edu/**" dir="/home/josh/Codes/java/forestrunner_applet/bin"/>
</jar>
</target>
<target name="all" depends="create_code_jar,create_data_jar">
</target>
</project>
[/xml]
And this for the desktop applet:
[xml]
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project default="create_run_jar" name="Create Runnable Jar for Project forestrunner_desktop">
<!--this file was created by Eclipse Runnable JAR Export Wizard-->
<!--ANT 1.7 is required -->
<target name="create_run_jar">
<jar destfile="/home/josh/Codes/java/forestrunner_desktop/dist/forestrunner.jar" filesetmanifest="mergewithoutmain">
<manifest>
<attribute name="Main-Class" value="edu.mit.lids.ares.forestrunner.DesktopGame"/>
<attribute name="Class-Path" value="."/>
</manifest>
<fileset dir="/home/josh/Codes/java/forestrunner_desktop/bin"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/eventbus.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/jinput.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/jME3-core.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/jME3-effects.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/jME3-lwjgl.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/jME3-lwjgl-natives.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/jME3-networking.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/jME3-niftygui.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/jME3-plugins.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/lwjgl.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/nifty.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/nifty-default-controls.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/nifty-examples.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/nifty-style-black.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/stack-alloc.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/vecmath.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/xmlpull-xpp3.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/apache_commons/commons-lang3-3.1/commons-lang3-3.1.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/json-simple/json_simple-1.1-all/lib/json_simple-1.1.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/sqlite4java/sqlite4java-282/sqlite4java.jar"/>
<zipfileset excludes="META-INF/*.SF" src="/home/josh/Codes/java/jme_nightly/lib/jME3-desktop.jar"/>
</jar>
</target>
</project>
[/xml]
Just played it a bit, high density is very hard but as much due to a lack of precision on control as anything else, I could see the gaps but didn’t have enough fine control over the motion to get into them - most of the time it over or under shot them and hit the object.
The random placement also gave me impossible situations quite a few times, they just formed a wall in front of me.
Ok, max density minimum radius was ok, no more walls.
I found quickly tapping the control keys gave me a bit of precision and max density min radius and min speed I made 83 seconds. I found I was mostly looking at the far area planning a clear route rather than looking near my arrow thing though. I don’t know if that’s relevant to your experiment as I was using information that would not be available in a genuine flight through trees scenario.
Tried it on max density min radius max speed and the lack of precision was killing me again, I no longer had time to tap the key the right number of times to get the position. Made 46 seconds on max d, min r, mid speed. That seems to be about my limit with the current controls.
I think you might need to filter min density out of your figures. Even at max radius I could have flown forever, kept going till 100 then suicided… Density up one and I did have to think a few times and crashed after 78s but to be honest I wasn’t really paying attention by the point I crashed.
Hope you get some useful figures though
@cheshirekow
Well-done extension of the sample game to a real-world use case!
My first thought was, if the player was flying at a lower altitude and basically only saw the trees from the front, obstructing one another --like the hawk in the video sees it-- it would be a more interesting challenge. It’s easy to find one’s way when one can basically see all upcoming obstacles in relation to oneself from above, but that is an unnatural situation. Unless you are simulating the point of view of a satellite controlling a drone, and not a bird.
Thanks for posting the build scripts (I linked this post to the Eclipse article on the wiki). We don’t have a lot of Eclipse articles – if you have tips to share, I think Eclipse users would appreciate it. Feel free to start a new wiki page, or send them to me and I’ll do the write-up.
I generally collect tips that struck users as being valuable for everyone. How you structured the project, how you went about integrating Nifty, or best practices for building and uploading the distributions, how did you do the Java version test, etc, any revelations that you want to share? – We can integrate them into the tutorials and articles.
@Normen
Are you updating/improving the build script?
@zathras said:
@Normen
Are you updating/improving the build script?
No, if it had been a jar slimming tool then maybe but like this its basically just one or two lines in a -post-jar target. I'm also not exactly sure if we should propose this way of setting up the project in Eclipse. I know a lot of eclipse users like to just make projects of some sources and integrate those projects into others but thats not needed to set up javadoc and source access in eclipse and world mostly be done by people who want to change the source structure and thus (hopefully) know what they do anyway. Those that don't stumble over the implications of this kind of setup pretty soon. Its not an especially nice way to update the engine, adapt to changes etc.
OK, so this is one specific solution. What would be the best/recommended way for Eclipse to do it, in general? Or should I just point to the eclipse doc?
@zathras said:
OK, so this is one specific solution. What would be the best/recommended way for Eclipse to do it, in general? Or should I just point to the eclipse doc?
Yeah, as we explain it in the eclipse doc is fine, if somebody could find out the exact steps to get it also use the sources for access (like netbeans) would probably be cool so eclipse users have the same convenience of "show source" for every class/method.
@zarch said:
Just played it a bit, high density is very hard but as much due to a lack of precision on control as anything else, I could see the gaps but didn't have enough fine control over the motion to get into them - most of the time it over or under shot them and hit the object.
Yeah, we've been discussing this a lot. This is a problem with the fact that the keyboard can't really provide the input as we model it in the theory (i.e. pick your horizontal speed). We could do that with a joystick or game pad (x-box controller) so we have support for that on the list of things to do.
@zarch said:
The random placement also gave me impossible situations quite a few times, they just formed a wall in front of me.
Yup. Thats expected. This is a difference between theory and reality. In the theory, we look at existence of infinitely connected collision-free components. However, the fact that such a component exists does not imply that it is reachable from all starting-conditions (and therefore, from any particular one). In addition, at high density/radius, it's likely that you're well within the case where such a component doesn't exist, and a crash is inevitable.
@zarch said:
I found quickly tapping the control keys gave me a bit of precision and max density min radius and min speed I made 83 seconds. I found I was mostly looking at the far area planning a clear route rather than looking near my arrow thing though. I don't know if that's relevant to your experiment as I was using information that would not be available in a genuine flight through trees scenario.
Good strategy. We've postulated that this is in fact the *best* strategy, and even have some ideas on how to prove it ;).
@zarch said:
I think you might need to filter min density out of your figures. Even at max radius I could have flown forever, kept going till 100 then suicided... Density up one and I did have to think a few times and crashed after 78s but to be honest I wasn't really paying attention by the point I crashed.
I agree. We plan to tweak the speed,density,radius intervals as we gather data. We also have some ideas about how to make it more fun, by creating a sort of "levels" structure so that it starts of easy and gets harder as you go, instead of manually picking the difficulty.
@zarch said:
Hope you get some useful figures though :)
Me too ;).
@zathras said:
Well-done extension of the sample game to a real-world use case!
Thanks!
@zathras said:
My first thought was, if the player was flying at a lower altitude and basically only saw the trees from the front, obstructing one another --like the hawk in the video sees it-- it would be a more interesting challenge. It's easy to find one's way when one can basically see all upcoming obstacles in relation to oneself from above, but that is an unnatural situation. Unless you are simulating the point of view of a satellite controlling a drone, and not a bird.
I agree, and we actually had it that way for a while. We may in fact make that an option in the future. The reason we do it this way is that we have these results for fundamental limits even if you knew the entire forest. It turns out there are interesting results even if the player/bird has limited information (i.e. can only see a small area around them). In this game, there is limited information in the sense of far-plane culling and left/right edge visibility... but we didn't want to limit the user's information much more than that. If they're flying close to these speed/maneuverability threshold, it will be hard enough to survive without adding additional constraints.
@zathras said:
Thanks for posting the build scripts (I linked this post to the Eclipse article on the wiki). We don't have a lot of Eclipse articles -- if you have tips to share, I think Eclipse users would appreciate it. Feel free to start a new wiki page, or send them to me and I'll do the write-up.
I am hoping to have time to write up a little about that because I agree there isn't much information for Eclipse users and I would have appreciated a bit more. Once I figured out the build system enough to setup my eclipse projects I felt like it was much easier to develop there (not to rag on jMP, I just use eclipse *alot* so I'm quite familiar with it's setup and style), especially when it came to working on the android port.
@zathras said:
I generally collect tips that struck users as being valuable for everyone. How you structured the project, how you went about integrating Nifty, or best practices for building and uploading the distributions, how did you do the Java version test, etc, any revelations that you want to share? -- We can integrate them into the tutorials and articles.
I would really love to give you some feedback here, but to be honest there isn't much that comes to mind. Though I promise the next time I'm hacking on this code I'll take some notes because I'm sure I'll remember some things.
As far as integrating Nifty, my strategy was basically to start a separate executable and work just on the gui. Because of the (potentially unique) fact that gui visibility and game-play are mutually exclusive, it was quite easy using the AppState mechanism to get things integrated. Each of my gui "screens" implements the AppState interface. Most of the work that needs to be done in a particular screen needs to happen at the same time that the screen goes away. At first I would put all that work in an input event handler (button click event, key press event) and then end with a nifty.gotoScreen(). This lead to a significant lag (at least on android) for the screen animations. My guess is that this is because of how nifty handles animations, so that at the next frame (which may have a rather significant tpf value due to all the work that was done in the previous frame callback) the animation jumps ahead pretty far. However, since I ended up making each screen into an appstate, I just put all of the work into the update() handler, and ensure that the call to nifty.gotoScreen() is the only thing called on that particular frame. This lead to much smoother screen transitions.
My strategy for building and uploading distributions is pretty ad-hoc and manual, so I wouldn't really have much to contribute with regard to smart ways of doing that. I basically build and test in eclipse on ubuntu, upload everything to the web server and then walk around to all my labmates computers and make them go to the webpage to see if it works. This is also my strategy for java version testing. We have Windows,Mac, and Linux users in the office... so I got a pretty good sampling this way.
One particular revelation that may be worth noting somewhere (I found it on the lwjgl mailing list or website or something) is that the linux open-source java implementation wont run the lwjgl applet, though the oracle one does. However, there is a bug open on the open-jdk iced-tea tracker specifically to support lwjgl and it is closed. Supposedly, it will work with the next iced-tea release (2.0 I believe).
@normen said:
if somebody could find out the exact steps to get it also use the sources for access (like netbeans) would probably be cool so eclipse users have the same convenience of "show source" for every class/method.
In eclipse, go to project preferences or settings or whever, and then click on the "Java Build Path" item in the left, and then click on the "Libraries" tab. This is the same place where you add a library to the build path. In this window, click "add external jar" and then select the jarfile (i.e. jME3-core.jar). It will be added to the list of "JARs and class folders on the build path". Each item in that list is expandable. Click the little arrow icon to the left of the item and you'll be given four options. The two options that are useful are: "Source attachment" and "Javadoc location". In this case, since we have the source, I click "Source attachment" and click the "edit" button on the right. I then set the path to where the source are (i.e. /home/josh/Codes/java/jme_nightly/source for me).
Now any class or method can be navigated to from "show source" tools. For instance, the hotkey "F3" when the cursor is on a JME name will popup a window to the appropriate class file.
Note: in eclipse you can access things in many ways. The way I usually access the build path settings dialog is to right click on "Referenced Libraries" from the "Package Excplorer" view. Then I select "configure buildpath" from the context menu.
@cheshirekow said:
Yeah, we've been discussing this a lot. This is a problem with the fact that the keyboard can't really provide the input as we model it in the theory (i.e. pick your horizontal speed). We could do that with a joystick or game pad (x-box controller) so we have support for that on the list of things to do.
You could use tilt on android devices too, I don't know how sensitive it is but it would be better. Mice on desktops too. It's going to introduce more differences between the platforms but I imagine you already factor that in.
@cheshirekow said:
Yup. Thats expected. This is a difference between theory and reality. In the theory, we look at existence of infinitely connected collision-free components. However, the fact that such a component exists does not imply that it is reachable from all starting-conditions (and therefore, from any particular one). In addition, at high density/radius, it's likely that you're well within the case where such a component doesn't exist, and a crash is inevitable.
Sucks for gameplay though ;)
@cheshirekow said:
Good strategy. We've postulated that this is in fact the *best* strategy, and even have some ideas on how to prove it ;).
Well it's pretty simple to demonstrate in that it's using the maximum amount of information possible as soon as it becomes available.
I guess you could say that the game is still a reasonable approximation to the real world as even though you are effectively seeing through trees as you still have a limited amount of information available on which you are making flight decisions.
@cheshirekow said:
I agree. We plan to tweak the speed,density,radius intervals as we gather data. We also have some ideas about how to make it more fun, by creating a sort of "levels" structure so that it starts of easy and gets harder as you go, instead of manually picking the difficulty.
That would be a good idea, especially if you want people to play it other than volunteer testers :)
It will also help organise the result set since (especially on the earlier levels) what % of people complete the level first time is probably more meaningful than survival time.
@zarch said:
You could use tilt on android devices too, I don't know how sensitive it is but it would be better. Mice on desktops too. It's going to introduce more differences between the platforms but I imagine you already factor that in.
Oh the android port uses tilt. We read the accelerometer and (soon, hopefully) gyro (if available) to get screen orientation and we translate that to horizontal speed.
@zarch said:
Well it's pretty simple to demonstrate in that it's using the maximum amount of information possible as soon as it becomes available.
The trick is showing (mathematically) that a short term strategy like this is long-term optimal, but you're right in that there isn't much more information available to make a decision on, so it's really just showing that the statistics of the "forest" don't offer any incentive to deviate from the short-term optimal.
@zarch said:
That would be a good idea, especially if you want people to play it other than volunteer testers :)
It will also help organise the result set since (especially on the earlier levels) what % of people complete the level first time is probably more meaningful than survival time.
Exactly.