home // text // multi-media // misc // info
video-games
computer-science
android
unity
]
Unity and the Android API
WARNING: This is a rather technical post about something completely different and not at all about Red Rover.
Several Ludum Dares (Ludums Dare?) ago, I made a game called Oases about seeking refuge from wifi hotspots in the environment. I built this on a Windows PC (my laptop), since it seemed the quickest way for me to bang out the network code in 48 hours.
Surprisingly, it turns out that it’s not all that easy to run around town escaping wifi hotspots while holding a laptop with an old and dying battery.
Cue the rise of mobile, wifi-enabled devices, however, and things get interesting. I’m an Android man myself, and since I’m slowly coming to terms with Unity, I started some tests in getting the latter to call functions from the Android API.
It was not straightforward.
The tutorial on Unity’s site gave a number of techniques for calling Android’s basic Java functionality from within Unity’s C# context, through JNI and C/C++. Unfortunately, even after reading it through multiple times, I still felt that, while it gave a lot of background information, it wasn’t as prescriptive as I was hoping.
Long story short: after patching that tutorial together with several dozen desperate and increasingly-confused online searches, I managed to gain access, in Unity, to the information polled from my Android device’s wifi manager.
I present those steps here for truth, justice, and the American whey. Hope it’s useful, and do call me out on the inevitable errors.
Calling the Android API from a Unity application
- Download and install the Android SDK. Learn it, love it.
- Start the Android SDK Manager, and install the SDKs you want. Make sure to also install the Android Support Library listed in “Extras.”
- Download and install the Android NDK. Love it, learn it.
- In my own setup, I’ve got the above installed at
C:\Android\android-sdk-(version)
andC:\Android\android-ndk
. Short paths simplify things. I’ll be referring to them as${ANDROID_SDK}
and${ANDROID_NDK}
, respectively (Oh, and your Unity installation is probably something likeC:\Program Files\Unity
, to which I will refer as${UNITY}
). - Download and install Eclipse; make sure to get the C/C++ variant.
- Start Eclipse, and install the Android Eclipse plugin as per these instructions.
- Create your Android library; these are the Java functions running on the Android API side of things, returning device status &c. to… “the ether” (essentially, back to Unity). In my case, these are the functions accessing the device’s wifi state. In Eclipse, go to New -> Project… -> Android Application Project.
- Give the app a name (what your device will visibly call it – not really important to us), a project name (for use in Eclipse’s workspace), and an Android package name (see those notes on nomenclature). MAKE SURE TO CHECK “Mark this project as a library.”. I forgot this once, and couldn’t figure out why Eclipse wasn’t compiling a .jar library for me.
- Leave everything else as is and continue; accept whatever clipart is shown, create a blank activity, and finish the wizard.
- Navigate your computer to
${UNITY}\Editor\Data\PlaybackEngines\androidplayer\bin
. You’ll findclasses.jar
– a Unity-to-Java helper package from the Unity folks themselves – which you should copy to your Eclipse project’slibs/
directory (that directory was created automatically with your Android project in Eclipse, and should be visible in the hierarchy on the left). - In Eclipse, go to Properties -> Java Build Path -> Libraries, and add
classes.jar
to your library path. - Eclipse generated some code for your Android activity. How very kind of it. It’s called something like
ProjectActivity.java
. We’ll have to make some changes though; most importantly, we no longer extend the Android “Activity” class – but we do pass in an Android context. I have reasons. In your auto-generatedMainActivity.java
source code (you may have, and still can, rename it as you like), you can have the following (with these particular members specific only to my endeavour):
- As you can see, I need the general Android context, if I am going to have access to the
WifiManager
class. The same is probably true for a lot of Android’s *Manager classes. - In Eclipse, make sure Project -> Build Automatically is checked. If so, you should have a
ProjectName.jar
file in your bin/ folder (again, this folder is auto-generated). - In Eclipse, in your file hierarchy, make a folder called
jni/
(it should be the same level asbin/
,libs/
,res/
, &c.) - Create a .cpp file in there. Call it anything you like;
ProjectCppBridge.cpp
is an option, as isSheila.cpp
. - You’ll also need an Android.mk file in the same directory, and maybe (but probably not) an
Application.mk
file. I’ll admit my Android-Fu is a bit weak, so start your learning process here. Why not. My ownAndroid.mk
looks as follows:
And Application.mk
:
- Now, the actual .cpp file, where you write all your JNI code, is going to be a bit complicated, so I’m going to just include it wholesale here.
- A few things to note. The
JNI_OnLoad()
function is called automatically; just use it to initialise things. Here, I get the running instance of Unity, find Unity’s hook to the running application/activity, and use that as a context to pass to theMainActivity
class (which, as shown above, requires a Context argument in its constructor). - I also define a function,
_cppbridge_isWifiEnabled()
. This function is called from Unity, and then passes the call along to the Android API, and then returns a value back to Unity. Important to keep track of where all these calls are going, and to make sure all the names are referring to the right things. - Download and install Cygwin.
You don’t need any specific packages, just the command-line interface.You do need ‘make,’ so make sure that you search for it in the list of packages and install it. - Open Cygwin and navigate (
cd /cygdrive/c/Eclipse/workspace/...
) to your Eclipse project directory (i.e., one abovejni/
). - In Cygwin, run
${ANDROID_NDK}/ndk-build
(with the appropriate path typed in). This createslibs/armeabi/projectname.so
in your Eclipse project directory. - Now, finally, create your Unity project.
- In the Unity project’s Assets/ folder, create a directory called Plugins/, and within Plugins/ create another directory called Android/.
- Copy
ProjectName.jar
(from your Eclipse project’sbin/
directory) andprojectname.so
(from thelibs/armeabi/
directory) into your newAssets/Plugins/Android/
directory. - Copy
${UNITY}/Editor/Data/PlaybackEngines/androidplayer/AndroidManifest.xml
into the sameAssets/Plugins/Android/
directory. - In the Unity script where you want to access the Android API, you must declare the following private references for every function you defined in your .cpp file:
Note that your_lib_name
is the name of the LOCAL_MODULE
variable in your Android.mk
file, without the “lib” prefix; or, it’s the name of the .so file you generated with ndk-build, but without the “lib” prefix or “.so” suffix. Also, the extern
reference refers to the exact name of the C/C++ function we defined in our .cpp file.
- God, this blog.
- In Unity, go to File -> Build Settings…, and build your app for Android. You will end up with a .apk file, which you can then transfer to your device for installation and testing.
- A great way to debug your app is to fire up the DDMS. Plug your Android device into your PC via USB; open up a terminal (in Windows: Win-R, then type “cmd”); change to your
${ANDROID_SDK}/tools
directory, and run ddms.bat; you should see a running list of all messages coming from your device. You can filter these in a number of ways: for instance, I prefaced all my error messages with “bagel,” so I could just search for that string. Look at the__android_log_print()
function in the .cpp file provided above to learn more about printing debug messages.
Right. I think that’s just about complete, but feel free to give me a shout if something is missing/unclear/incorrect.
Now excuse me whilst I go rest my eyesies.