Android Day 6: Save games, Audio, other Details
Okay .. the gameplay is totally working, so now it’s time to add a bit of polish to the game!
Step 1 – forced orientation:
If you want to set the orientation of your app to always be the same, add this to your AndroidManifest.xml Activity tag:
android:screenOrientation="portrait"
Otherwise, the app could start up in landscape mode which would give you a rotated OpenGL window, which for most games is probably not what you want.
Step 2 – handling Save / Restore of game state:
Now I want to get save / restore working in the game. As fun as it has been re-typing in my name every time I try the MultiPlayer stuff out, the fun is over. On the plus side, it’s given me plenty of opportunity to work out a few other bugs in there.
There are a ton of options for save / loading data.
http://developer.android.com/guide/topics/data/data-storage.html
I considered using the method named “External Storage” it would allow my app to save and load data from actual REAL files with REAL paths, which was attractive in terms of making my life easier, but there was a whole huge section on checking to make sure that is even possible. I guess there are like 3 cases where it might not work.
So since I’m big on not checking if something works, I decided I’d use the “Internal Storage” method which seemed a bit more idiot proof. It appeared to also have a way to find the folder name, so that will make things pretty easy.
// JNI used to get Save data dir public static String get_docdir() { File fdir = app.getFilesDir(); return fdir.getAbsolutePath(); }
However, it seems that when saving, this is causing some kind of SIGSEGV .. From what I can tell the “JNIEnv* env” that is passed to my C code isn’t the same on every call. So what I’m doing now is I’m saving the first one and only using that, not updating it as new values come in to my code. (Some of the values sent in later looked suspicious to me. Some were even null!)
Step 3 – adding music:
One of my favorite parts of game development is getting the sound working. It always makes the game feel like it’s ready to go. This is the link to read:
http://developer.android.com/guide/topics/media/index.html#playfile
Here’s the code I came up with to play music. Again, I’m storing all my game assets in the assets/ folder since that gives me the most simple direct access to it.
// JNI to play music, etc public MediaPlayer _music = null; public static void music_play(String fname) { Log.v("music_play",fname); AssetManager am = app.getAssets(); try { AssetFileDescriptor fd = am.openFd(fname); app._music = new MediaPlayer(); app._music.setDataSource(fd.getFileDescriptor(),fd.getStartOffset(),fd.getLength()); fd.close(); app._music.setLooping(true); app._music.prepare(); app._music.start(); } catch(IOException e) { } } public static void music_stop() { if (app._music == null) { return; } app._music.stop(); } public static void music_volume(float v) { if (app._music == null) { return; } app._music.setVolume(v,v); }
Step 4 – adding sound effects:
Time to add in sound effects. This is done with the SoundPool class. It might actually be easier to use this class for music as well.
// JNI to play sounds public SoundPool _sounds = new SoundPool(8,AudioManager.STREAM_MUSIC,0); public static int sound_load(String fname) { AssetManager am = app.getAssets(); try { AssetFileDescriptor fd = am.openFd(fname); int sid = app._sounds.load(fd.getFileDescriptor(),fd.getStartOffset(),fd.getLength(),1); return sid; } catch(IOException e) { } return 0; } public static void sound_play(int sid) { app._sounds.play(sid,(float)1.0,(float)1.0,0,0,(float)1.0); }
Step 5 – opening up the web browser to a URL:
In my game I’ve got a handful of spots where I let the user open up a webpage (more games, forums, etc…) Here’s the code:
public static void open_url(String url) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); app.startActivity(intent); }
Step 6 – adding the app icon:
It’s about time I got an Icon ready for this app. I’m going to use my Galcon Fusion style icon since that one works nicely with transparency. You can get really fancy with providing a bunch of different icon sizes:
http://developer.android.com/guide/practices/ui_guidelines/icon_design.html
But I just provided a single 72×72 icon.png which I put into res/drawable. And then I added this into the Application tag of my AndroidManifest.xml:
android:icon="@drawable/icon"
That seemed to be sufficient.
Step 7 – further thoughts on pause / resume:
Another cleanup item I found was getting pause/resume working more “the Android way” .. Or at least, working at all. Again, I reviewed this and made some changes to how my app works.
http://developer.android.com/reference/android/app/Activity.html
Working on this sent me into a spiral of weird crashes for no particular reason. I sprinkled around the “synchronized” keyword with no luck. I did some code cleanup and fixed some memory leaks and now it seems better. A bit tricky though, I can’t always tell why things happen.
In general, I found that during onStop you should free up all your audio assets, so that on the next onStart you could reload them. I also found that the GL assets automatically get freed, so on any onSurfaceChanged you need to reload your GL assets.
But the game is seeming pretty stable now. Time to set it in for some testing for a while 🙂
-Phil
July 27th, 2010 at 11:56 pm
You need a testing goon? (hands up)
July 28th, 2010 at 2:46 am
(hands up) Me Me Me ^^ (overclocked european milestone)
July 28th, 2010 at 8:42 am
I volunteer for testing. I have a MyTouch running 1.6. Played Galcon on my ITouch for sometime. Let me know, I’d be happy to help.