Galcon Games
Phil Hassey - game dev blog
Phil Hassey as Snidely Whiplash
"You can't buy awesomeness.
You're born that way."

Archive for the 'Uncategorized' Category

Android Day 3: Packaging, Assets, JNI, and OpenGL

Thursday, July 22nd, 2010

I considered using SDL for this port, however, I became concerned that none of the ports were quite ready for prime time. None of them have been really released as complete and stable packages, and there are enough weird things about Android that I think I’ll be better off doing the work directly myself. Galcon is an “OpenGLES” app in essence, so much of what SDL provides is unnecessary anyways. But if you are interested:

There is a summer of code project relating to this:

http://socghop.appspot.com/gsoc/student_project/show/google/gsoc2010/sdl/t127230762779
http://hg.libsdl.org/SDL-gsoc2010_android/summary

And there’s also another SDL port here:

http://www.anddev.org/code-snippets-for-android-f33/sdl-port-for-android-sdk-ndk-1-6-t9218.html

Step 1 – Using command line tools instead of the Eclipse IDE:

I also decided that avoiding Eclipse might be a good move. I’m sure there are those of you who would argue otherwise. But, err, I’m a CLI kind of guy, so we’ll see how it goes.

First I added ~/android/tools to my path, so that I wouldn’t have to keep typing in the whole path :)

In my Galcon android folder, I ran this command:

$ android create project -t “android-4″ -k com.igalcon.galcon -a Galcon -n Galcon -p .

Which created my other project files.

Then to create a debug build of Galcon:

$ ant debug

Then to install it onto my device:

$ adb devices

List of devices attached

emulator-5554 device

deviceid device

$ adb -s install bin/Galcon-debug.apk

And the app is installed, and I can run it. However, at this point I’ve just got a Hello World thing showing up, so I’ve got a long way to go until I’ve got Galcon appearing :)

Step 2 – Including resources (graphics, audio) with your game:

There are a number of options as to how to bring data long for your game. A few are explained on this page:

http://developer.android.com/guide/topics/resources/providing-resources.html

I made a folder called “assets” and placed all my game assets in it. Instead of mp3s, I include ogg files. And instead of .wav, I include ogg files. On the Android handhelds, there is only 256 MB of internal storage. Many games just pack the binary and then download the data separately onto the users flash card. I deem that to be totally unawesome and will ship Galcon with all its data. But I will do my best to pack as little data into my binary as possible. By using oggs instead of mp3s and wavs, I’ve trimmed a couple MB off my data size.

UPDATE: App can request to be stored on the SD card! http://developer.android.com/guide/appendix/install-location.html

To do this, add android:installLocation=”preferExternal” to the manifest tag in AndroidManifest.xml .. And while you’re at it, add in <uses-sdk android:minSdkVersion=”4″ /> to require OpenGLES 1.1 support. Also change target=android-8 in default.properties.  This will give a warning since your minSdkVersion and your target aren’t the same.  Just read that link above for more info.

If you save your files in “res” folders, some kind of Android magic kicks in and it seems you can’t reference anything by a String. You have to use Java IDs, which makes it really hard for C code to say “get me file X” .

Files saved in the assets/ directory are not given a resource ID, so you can’t reference them through theR class or from XML resources. Instead, you can query files in the assets/ directory like a normal file system and read raw data using AssetManager.

At this point, I’ve got a basic project building, and I’ve got assets included in my package. (I can tell this because my package is 3MB, not 200k like a empty Hello World example.) I think 3MB is an acceptable size.

Step 3 – Debugging the game:

Next I need to figure out how to get debugging working. I’m pretty sure I’m going to have bugs, so .. doing on-device debugging will be ideal. Again, if I were to take the wimp’s way out, I’d use Eclipse (I guess? for me actually, Eclipse seems like a greater learning curve..) but I’ll be using CLI methods to debug. I’ve edited Galcon.java and added a Divide by Zero bug, so I can see how it appears. And upon installing it, it crashes.

As a side note, to replace an app you’ve already installed, this command:

$ adb -s install -r bin/Galcon-debug.apk

Error that appears on my device: “Sorry! The application Galcon (process com.galcon.igalcon) has supped unexpectedly. Please try again.”

To debug:

$ adb -s logcat

And I get to see a lovely dump of errors as I have them:

E/AndroidRuntime( 1960): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.galcon.igalcon/com.galcon.igalcon.Galcon}: java.lang.ArithmeticException: divide by zero

Here’s a link to a page that talks more about adb, getting a shell on the device, and other fun goodies:

http://developer.android.com/guide/developing/tools/adb.html

Step 4 – Getting Java to call C functions:

Now I’m going to try and get Java to call out to the C / C++ code I built yesterday. At this point I’m going to send a thanks out to James Brown (Ancient Frog) who lent me a few lines of his attempt at an Android port. There are lots of tutorials and documentation on-line for JNI stuff, but seeing real code that is in the ballpark of what I want to do is a huge help. I’ll be posting a few snippets as I go in this blog, but if you do want to really learn about JNI, there is a ton of information.

First of all, in Galcon.java, in my Galcon class, I’ve sprinkled a handful of references to native functions:

public native void init();
public native void loop();
public native void paint();

In onCreate() I’ve added a call to init().

And then I created a myjni.cpp file which I filled with templates like this. I love all the ludicrous namespacing. I’m not sure who it is helping, but I’m pretty sure it isn’t me.

JNIEXPORT void JNICALL Java_com_galcon_igalcon_Galcon_loop(JNIEnv* env) { /* do stuff here */ }

After building and attempting to run I get this error from logcat:

W/dalvikvm( 2168): No implementation found for native Lcom/galcon/igalcon/Galcon;.init ()

I definitely need to add myjni.cpp to my jni/Android.mk and re-run ndk-build.

Tip: if you want to do some logging to the Android message log:

#include
#define MYLOG(msg) __android_log_write(ANDROID_LOG_ERROR,"MYLOG()",msg);

Seems to do the trick.

I’m still getting the error, so I think my .so isn’t being loaded. I add this before I call init to load the shared library:

System.loadLibrary("igalcon2");

I get a NEW error:

E/AndroidRuntime( 2255): java.lang.UnsatisfiedLinkError: Library igalcon2 not found

I guess libigalcon2.so isn’t being packaged. Gotta check into this. An unzip -v of the package reveals that libigalcon2.so is being included in the package. But I found this error in logcat:

W/dalvikvm( 2338): JNI_OnLoad returned bad version (0) in /data/data/com.galcon.igalcon/lib/libigalcon2.so 0×449e1260

I guess I need to make sure I’m returning the right version in my JNI_OnLoad function. This is my function:

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
  JNIEnv* env;
  if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
      MYLOG("JNI_OnLoad(): GetEnv failed");
      return -1;
  }
  return JNI_VERSION_1_4;
}

That seemed to work fine. So I’ve got JNI up and running and talking to my C code. I was able to get my various JNI functions calling my Galcon functions with no trouble.

Step 5 – Setting up an OpenGL context in Java:

logcat reveals that I’m getting a TON of OpenGLES errors similar to: “E/libEGL ( 2463): call to OpenGL ES API with no current context (logged once per thread)” .. This is pretty self explanatory. Time to figure this one out.

This page seems to reveal how to set up an OpenGL context:

http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/GLSurfaceViewActivity.html

And this seems to be how to set up and OpenGL renderer:

http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/CubeRenderer.html

Sort of bodging those together got me pretty far. At least, I know my GLES code is being called and I’m not seeing errors.

Step 6 – Getting C to call a Java method (to load a texture):

I gotta get my texture images loading up so that my paint code is rendering something a bit more interesting to the screen. In this case instead of Java calling C, I need my C code calling Java code. So it’s like JNI in reverse. Or something. Here’s the documentation on doing just that:

http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jniref.html#call

You’ll note that no matter how we do this, we gotta have access to an “env” or something. I’m going to set up some kind of awful global variable that will be available to all my code so that I can use “env” to do stuff like this. I put this at the top of my JNI calls:

JNIEnv *_my_jnienv = 0;
void set_jnienv(JNIEnv *env) { _my_jnienv = env; }
JNIEnv *env *get_jnienv() { return _my_jnienv; }

And added set_jnienv(env); to the top of each call. Now in my other “driver” code I can call get_jnienv() to get it back.

Here’s my dummy load_texture method on the Java side:

    public static int load_texture(String fname) {
        Log.v("load_texture",fname);
        return 0;
    }

Here’s my C function that calls it:

int jni_load_texture(const char *s) {
    JNIEnv *env = get_jnienv();
    jclass cls = env->FindClass("com/galcon/igalcon/Galcon");
    jmethodID mid = env->GetStaticMethodID(cls,
	       "load_texture",
	       "(Ljava/lang/String;)I");
    // there could be some exception handling happening here, but there isn't
    jint ret;
    jstring mystr = env->NewStringUTF(s);
    ret = env->CallStaticIntMethod(cls, mid, mystr);
    return ret;
}

Pretty gruesome, but it seems to get the job done. Now if I can fill in my java load_texture method with something that works, I think I’ll have visuals! Here it is:

import android.graphics.BitmapFactory;
import android.graphics.Bitmap;
import android.content.res.AssetManager;
import android.opengl.GLUtils;
import android.opengl.GLES10;
import java.io.InputStream;
import java.io.IOException;

// gl is a GL10 instance, grabbed from within my renderer.  app is my Galcon Activity.

    public static int load_texture(String fname) {
        Log.v("load_texture",fname);

        AssetManager am = app.getAssets(); //new android.content.res.getAssets();

        try {
            InputStream stream = am.open(fname);
            Bitmap bitmap = BitmapFactory.decodeStream(stream);
            int[] textures = new int[1];
            gl.glGenTextures(1, textures, 0);
            int textureID = textures[0];
            gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);

            // no mipmaps
            gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
            gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);

            GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

            return textureID;
        } catch(IOException e) {
//             Log.v("load_texture:open() fail");
            return 0;
        }
    }

Not trivial, but this saves me from having to decompress the files in C land. I currently don’t have any image decompression code that I use regularly so this works for me.

App Store pricing

Thursday, April 29th, 2010

Galcon Fusion is currently priced at $8 on the App Store. I originally had a longer post written, but I think this App Store user review says so much about the psychology of pricing and the values on the App Store that I really can’t think of anything to add to it.

“The price stinks, but it’s worth double!” – Jacques P.

Ludum Dare 2010 Keynote (starring Cuzco the Goat!!!)

Friday, April 23rd, 2010

See the Ludum Dare 2010 Keynote:

Inviting all python game-devs! Even if this notice is coming too late, we’ve got a compo coming in August, December, and mini-compos every month! Just check out the website and join the fun!

-Phil

Multi-hour AMX sketch

Friday, April 16th, 2010

Man, I’ve been having fun with this iPad :)

IMG_0005

This image took a couple hours to draw. The AMX was much harder because I couldn’t just sloppy out whatever, I had to use straight lines and try to get it looking like the original. So being “less creative” is really quite a bit harder.

-Phil

48+ minute drawing “swamp”

Thursday, April 8th, 2010

Got a bit inspired by Monkey Island 2 swamps for this one …

IMG_0004

Again, iPad + Sketchbook Pro + Pogo Sketch Stylus = Fun!

-Phil

48 minute drawing: “Under the Sea”

Wednesday, April 7th, 2010

I think the iPad + the Pogo Stylus + Sketchbook Pro is a pretty killer combo after all.

under_the_sea

That took 48 minutes, I used this image for inspiration.

Galcon Fusion in the App Store, iGalcon update, and a freebie game!

Saturday, April 3rd, 2010

fusion-ipad

The iPad launched today, and Galcon Fusion is ready for it! The iPad version is fully multiplayer compatible with the desktop versions, so if you have an iPad or know anyone who has an iPad or know anyone who is thinking about iPads, it’s time to buy Galcon Fusion!

smiles-banner

Also, a good friend of mine has launched his game “Smiles” onto the App Store. It’s a super slick Match-3 game and it’s FREE today only! Get it while it’s hot!

Thanks!
-Phil & Nanno

P.S. the iPhone Galcon update got approved, I hope that fixes all the issues! Please update ASAP. Galcon Labs is still pending approval, but about a day after it gets approved, I’m going to require updates to play the multi-player so that all the users are playing together again :)

Galcon Fusion for Windows / Mac OS X / Linux / Steam has arrived

Thursday, February 11th, 2010

Galcon Fusion has arrived.

Yay, it’s finally here! Check out Galcon Fusion today! We’ve got a free demo and it works on Windows, Mac OS X, Linux, and Steam! It features some crazy nice hi-res graphics and soundtrack. And epic multi-player battles like you’ve never seen ‘em before! Have fun!

-Phil & Nanno

P.S. Did you buy Classic Galcon or Galcon Flash? Thanks! You are awesome, and because you are awesome, your purchase of those games count as pre-orders of Galcon Fusion! That’s right – you already own the game! Get in there and play it!

P.S.S. Want to help me out? Spread the word! Play the game! That’ll make my day :)

Get Ready for Galcon Fusion – for PC / Mac / Linux!

Friday, January 29th, 2010
Galcon Fusion Poster

Introducing Galcon Fusion, the long-awaited update to desktop Galcon! Now with improved HD graphics, a full soundtrack, and a variety of exciting new multi-player modes! Galcon Fusion is set to launch February 11 on Steam and on our website for PC / Mac / Linux.

Check out the trailer!

- Phil & Nanno

VisualC++ 2008 for gcc / SDL people

Thursday, January 14th, 2010

I’ve had to port a project to VC 2008 from my usual mingw setup.  Here’s what I did.  This covers quite a few things, in particular where to find common GCC settings in VC land.

Setting up the project
  1. New project
  2. Type: win32
  3. Template: Win32 console if you want a console.  Or just Win32 Project if you don’t want the console to pop up.
  4. Choose a name for your project
  5. The solution name is the folder everything gets put in.  In VC, you can have several projects within a single solution.
  6. On App settings, I choose Windows application, Empty project

Adding the source files

  1. I right click on my project name and Add>New filter, and use that to bundle a few different sets of source files together
  2. This is done so that VC will compile the files, but keep in mind that the separations don’t seem to mean anything to VC.  It’s just for your visual convenience.
  3. Note that if there are two files with the same base name “dostuff.c” and “dostuff.cc” this will cause VC to choke during compilation as it will create two .obj files with the same name and fail during linking.
  4. VC doesn’t support C99 so any C99 files will have to be Right Click > Properties > C/C++ > Advanced > Compile as > C++ Code

Note: a Project can have a number Configurations (Debug, Release ..) If you are targeting a certain distribution site with some special requirements you can add more Configurations via Solution > Properties > Configuration Manager >

Compilation Options

Adding some project compile time options (mainly -D and -I equivalents for specific defines and include folders.)  Keep in mind which Configuration you are modifying at all times.  You can also switch to “All Configurations” for global changes.

  1. (GCC -I)  To add in include folders, Project > Configuration Properties > C/C++ > General > Additional Include Directories > … I found with include folders you often need to stack on a few ../../’s to get it to point at what you want it to point at.  (In my project I’ve got my VC project separate from all my source code, so everything is up and over a few folders.)
  2. (GCC -D)  To add in defines, Project > Configuration Properties > C/C++ > Preprocessor > Preprocessor Definitions > .. I often add a few of these to tell the code what build we’re doing.  (iPhone vs, Windows, vs Linux, etc)
  3. (GCC -W)  Although warnings might be useful .. They made it impossible to see the errors.  You can turn down the warnings from level 3 to 1 in Project > Configuration Properties > C/C++ > General > Warning Level >
  4. (GCC -l)  Linked libraries are probably going to be needed.  Again in my project I’ve often got a few ../../’s in the paths.  Here’s where you add this stuff: Project > Configuration Properties > Linker > Input > Additional Dependencies >
  5. .. an alternate way is to add #pragma comment(lib,”MyLib.lib”) into your main.cpp.  By doing this you can refer to a lib right within your source if you prefer.
  6. For whatever reason, you may need to exclude some libs from being compiled into your project.  (If you see errors like “_tolower already defined in MSVCRTD.lib, etc).  You can exclude a lib in Project > Configuration Properties > Linker > Input > Ignore Specific Library > .. (in my case, I had to add msvcrt.lib and libcmt.lib)
  7. For my project I needed to expand the size of my stack, due to having excessive amounts of static data compiled into the project.  You can do this via Project > Config Properties > Linker > System > Stack (Reserve&Commit) Size > .. I changed them both to 4000000 and things worked.

Final packaging

I found that running right from VC didn’t always work. (Maybe I should find out why, but I haven’t.)  But I can run from the command-line.  Before doing so, I needed to:

  1. Copy in required dlls from SDL, etc
  2. Copy in game data files
  3. Copy in the VC Runtime files from: C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\x86\Microsoft.VC90.CRT
  4. For a release build I’m sure to build against SDLmain_nostdio.lib so that the game won’t crash Vista/Win7 where writing out stdout.txt and stderr.txt may result in some kind of fail.

Saving to Your_Favorite_Version_Control_System

  1. You may leave out all your ‘Debug’ folders or other Configuration folders, those seem to only contain generated files.
  2. You may leave out the .ncb and the .COMPUTER.username.blah file, they seem to be used by VC for stuff that can be regenerated, etc.

Hopefully this will be helpful to anyone who does the same thing.  Between you and me, I found the install of VC 2008 to be pretty nice in comparison to trying to install mingw, which tends to require considerable muscling.  Also, since everyone provides their libs as MSVC libs, building against those vendor libs is not only easy, but possible.  (With mingw, many MSVC C++ libs are unusable due to binary incompatibility.)

-Phil

P.S. Adding an Icon to your Project

This is pretty easy.  Add a Project.rc to your project.  The Project.rc should include this line:

IDI_PROJECT       ICON         “path/to/icon256.ico”

You can create an Icon with IconFX .

P.S.S. Creating a windows Installer

I’ve used Innosetup in the past.  It seems to work.

P.S.S.S. Dealing with LPCTSTR conversions

If your whole project is using C-strings, you can go into Project > Properties > Configuration Properties > General > Character Set > and change it to “Not Set” so that VC will no longer try and use Unicode strings to some of its functions (like ShellExecute.)


Galcon   Watermelons   Dynamite   The Hairy Chestival
All content of imitation pickles (c) 1999-2008 - Phil Hassey  "we care"