Kamis, 21 Juni 2012

Porting Flixel from Flash to Android

Game developers targeting Android are at a disadvantage compared to those working on the iPhone, simply because Android currently does not have anywhere near the same level of 3rd party support. If you want to make an iPhone game there are numerous tools available including some big ones like Unity and Torque. Android developers still have to make a game from the ground up, using a few scattered resources on the internet and the Lunar Landing example provided on the Android developer site.

To address this issue I have started a project to port the popular Flixel Flash game engine to Android. I thought that I would write up some of my observations about Android and Java that I ran across during this conversion process.

For the most part the conversion from the original ActionScript code to Java was straight forward. ActionScript is what the next version of JavaScript will probably look like, and for the most part it uses the same curly braces C syntax that Java uses. There are a few difference in the syntax though. ActionScript defines types like var i:int, whereas Java uses the more familiar int i. Function declarations are also different, with ActionScript functions looking like public function Whatever():void, and Java looking like public void Whatever().

The bulk of the syntax differences were handled with a quick and dirty C# Regular Expression program that handled the grunt work converting ActionScript to Java. This kind of automated conversion prevented the small but annoying errors that occur when converting the syntax by hand.

There were a few major limitations in Java that had to be dealt with. The first was the lack of optional parameters. Im sure there are very good reasons for not supporting optional parameters (and many a flame war surrounding the topic). Since I wanted to maintain as much compatibility with the original Flixel library as possible, this limitation meant that I was forced to write several functions, each progressively adding one more parameter. I personally cannot understand why optional parameters are so bad, and it seems that Im not alone: C#, which also does not support optional parameters, is introducing them with the next version.

Java also lacks get and set functions, forcing you to bundle up parameters in functions pairs like getParam and setParam. This led to a situation where the ported code used a mixture of public variables with the occasional getParam and setParam functions. Mixing public variables and get/set functions, while not generally the best idea, does not have any real impact on the end user in ActionScript: the syntax for both is the same. The difference between the two is glaring in Java though, with a simple statement like object.alpha += 1 becoming object.setAlpha(object.getAlpha() + 1). Yuk.

Java also does not support delegates or function pointers. I personally prefer delegates to interfaces for call backs, but it was not hard to create interface classes, like FlxFadeListener, FlxFlashListener, FlxCollideListener and FlxAnimationListener, to deal with those few situations where Flixel implemented call backs.

I have always liked the way Flash allows you to embed resources into the final SWF file. With the Embed keyword a file, like an image or mp3, will be encoded and presented to the developer as a class. You can then create a new instance of this class and play or display it.

Android uses a different system. Resource files are copied into the res directory, and these resources are packaged into the file apk file, with an automatically generated R class providing the index to these resources. This R file is a plain class that defines a few internal classes (representing the various directories under the res directory), with each of these internal classes defining integers that can be used to reference the resources in code. So as an example R.drawable.spaceman would be used to get access to the resdrawable.nodpispaceman.png file.

The downside to this is that external libraries, like Flixel, have no type safe way of accessing the R class. It is generated in a package that differs between applications, so the library cannot reference it before hand (because it has no idea what the package the R file will be in), and since the R class does not conform to any interface there is no way to access its members in a type safe way should the library be provided a reference to the object at run time. This was an issue with the Flixel library because there were a handful of standard sounds and images that the library needed to access.

A work around was to use reflection to pull out the integers at runtime. This method relies on the format of the R class being consistent, which is no guarantee. It would be nice if future versions of the R class implemented an interface of some sort. This would mean that external libraries would not have to rely on the R class being generated in the same way all the time, but instead could rely on a consistent interface.

There may be some tricky way to compile or package a Java library for Android that can access its own R class, but if so I didnt see any information on the process. In any case I dont see a downside to the R class implementing an interface.

For the most part the combination of Androids Bitmap and Canvas classes provided a drop in replacement for the Flash BitmapData class. In fact to save some time I ended up recreating the BitmapData class in Java, providing the draw and copyPixels functions that Flixel uses. Creating this compatibility layer ensured that there was a single place in the code where the graphics functionality had to be replicated and leaving the Flixel code more consistent with the original Flash library (a god send when you are comparing two classes for differences when a new version of the original library is released).

The Android MediaPlayer class provided a simple interface for playing sound effects. In fact the Android code for dealing with sound effects is cleaner than the original Flash code because of all the additional work that was needed to add the ability to pause and repeat a sound effect in Flash. Since the Android MediaPlayer was so much easier to use than the Flash Sound and SoundChannel classes I did not try and replicate the original Flash classes like I did with the BitmapData class. Instead I rewrote the Flixel FlxSound and FlxG classes to work with the MediaPlayer class directly.

Working with the render loop in Flash is a piece of cake. Flash uses a single threaded model, so you simply wait for the screen drawing events to update the scene and draw the frame.

Android was a little more complicated. The render loop needs to be implemented in a separate thread, with all of the synchronisation and thread safety that something like that entails. The Lunar Landing demo shows a good example of how this style of render loop can be implemented, but those using the Android version of Flixel will simply be able to extend the FlxGameView class and not have to deal with threads.

Flash applications dont really deal with the concept of exiting the application it ends when the page that contains the Flash applet is closed. As such the original version of Flixel did not include any logic to shut itself down and exit.

In truth Android applications dont really exit either. By default applications are supposed to always run in the background once they have been started, albeit in a suspended state to conserve memory while they arent being used. You can use the finish function on an Activity to close out of it if you choose to. Since it is unlikely that you would want a game running in the background for hours on end the Android version of Flixel has been modified to allow the render loop to exit, which will finish the application.

The speed of the AVD that is used to test you application is a big issue. I was getting around 2 3 frames per second in the AVD. Initially I thought I must have been doing something wrong and chewing up CPU cycles in a badly designed loop somewhere, but after some Googling I found out that I wasnt alone. It would seem that you need quite a kick ass PC to emulate an Android phone. This is a problem because I dont actually have an Android phone (or a kick ass PC) to test the application on. Although I have not done any iPhone development myself, from what I can gather the virtual iPhone that developers can test their code on runs at near native speeds. Surely if we can put a man on the moon we can emulate a modest Android phone at a reasonable speed?

All in all porting Flixel to Android wasnt too difficult. While there are still some issues to be ironed out, it is now possible to create playable 2D games on Android with a minimum of fuss. You can download the Android Flixel library from here, and learn how to use it with the tutorial series here.





iAutoblog the premier autoblogger software

0 komentar:

Posting Komentar