We are also really close to starting our Open Beta round so if you want to know when that starts or anything else related to our game, follow us on Facebook:
facebook.com/scawargame
While working on our first Android game, I came across a tool that let's you combine all your small sprites in to a single sprite sheet that will be loaded by your game engine. The tool is called Texture Packer.
The tool will try to fit all the images you throw at it in the smallest possible spritesheet. If you want, it can also crop and rotate the images to make it possible to fit even more images to same space. Several different game engines are supported, so for example AndEngine which we currently use, the tool will create one image file, one XML data file that contains the locations, rotation etc. information of the sprites in the image file and one Java file which has a constant for each of the sprites for typesafe handling of sprite loading.
Packing all your sprites to one image has multiple benefits. OpenGL ES requires the textures dimensions to be a power of two. This way, if you have a single texture buffer for each sprite, you end up reserving lots of extra space for nothing. With single, tightly packed spritesheet you usually can get away with considerably less empty buffer space. Using a single TextureAtlas for multiple textures has performance advantages in both loading the textures as well as drawing them. Details about advantages in drawing performance are explained here.
The tool is easy and intuitive to use. The developer is friendly and fast to response to any problems you might have. And it does now have a version for Ubuntu, which was perfectly timed as I had just moved my development to Ubuntu from Windows as I kept having issues with Windows filesystem and Maven.
As I'm starting to sound like a marketing droid here, I will state here that I don't have contacts to TexturePacker or it's author, I didn't receive any payment from this post. Andreas Löw, author of TexturePacker, was kind enough to provide me with a free Pro license of TexturePacker.
I’m a big fan of automated build process. Especially on Android where creating a release-ready package is a really multi step process: you need to compile, strip with ProGuard, dex, package, sign, verify and what not.
When it came time to integrate Scoreloop to our game, we had some problems. Scoreloops offers a nice UI ready for use. But this means you can just put it in a jar and use it like normal dependency, because UI’s need graphics, layout xml’s and other resources. Finding info on how to get this to work with Maven proved difficult. Thanks to Julien Donguy at Scoreloop for pointing me to Android Library Projects.
Here’s how you would go about adding Scoreloop to your Maven build:
Download the Scoreloop SDK and unpack it somewhere. Inside you’ll find two directories: ScoreloopCore and ScoreloopUI. ScoreloopCore contains scoreloop-core.jar which you can install to your local maven repository like any other jar:
mvn install:install-file -Dfile=scoreloop-core.jar -DgroupId=com.scoreloop -DartifactId=scoreloop-core -Dversion=2.4 -Dpackaging=jar
Next you want to create a zip file from the contents of the ScoreloopUI directory (so that AndroidManifest.xml is in the root of the zip) and rename that zip to scoreloop-ui.apklib.
We can then add that package also to your Maven local repository with command:
mvn install:install-file -Dfile=scoreloop-ui.apklib -DgroupId=com.scoreloop -DartifactId=scoreloop-ui -Dversion=2.4 -Dpackaging=apklib
Note the different packaging parameter.
Next you’ll add both of these packages as dependeciens to your pom.xml.
<dependency>
<groupId>com.scoreloop</groupId>
<artifactId>scoreloop-core</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>com.scoreloop</groupId>
<artifactId>scoreloop-ui</artifactId>
<version>2.4</version>
<type>apklib</type>
</dependency>
Now running mvn clean package should create an apk that includes all necessary parts from Scoreloop. You can see the apklib getting included with something like this:
[INFO] --- android-maven-plugin:3.0.0-alpha-11:generate-sources (default-generate-sources) @ YourGame ---
[DEBUG] Expanding: C:\Users\someuser\m2\repository\com\scoreloop\scoreloop-ui\2.4\scoreloop-ui-2.4.apklib into C:\ideaworkspace\examples\YourGame\target\unpack\apklibs\com.scoreloop_scoreloop-ui_apklib_2.4
We did experience problems with earlier versions of maven-android-plugin so we suggest using Maven 3 and android-maven-plugin 3.0.0 alpha’s.
Atleast for Intellij IDEA, there’s one more step to go. Apklib’s, unlike regular jar dependencies, are not visible inside IDEA, so if you start coding, you’ll find none of the classes from the scoreloop-ui.apklib available. To fix this you can add the ScoreloopUI as a module dependency inside IDEA:
Now the sources for the UI classes are available to your IDE, and this doesn’t affect the maven build process.
From here on forwards you can follow the User Guide that comes with Scoreloop.
Comments and feedback are welcome as usual!
Our game is progressing nicely and we are planning on starting closed beta phase soon. So it was time to get ProGuard’s optimize and obfuscate features working. So far they had been disabled.
Optimizing seemed to go through just fine but dexing the end result failed with:
[INFO] UNEXPECTED TOP-LEVEL EXCEPTION: [INFO] com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type java.lang.Object using a local variable of type long. This is symptomatic of .class transformation tools that ignore local variable information.
So I knew I needed to limit the optimizations, but what exactly I didn’t know. Most of the exceptions referred to different Scala classes like
[INFO] ...at bytecode offset 00000022 [INFO] locals[0000]: Lscala/collection/IndexedSeqOptimized; [INFO] locals[0001]: Lscala/collection/Iterable; [INFO] locals[0002]: I [INFO] locals[0003]: Lscala/collection/IndexedSeq; [INFO] locals[0004]: Lscala/collection/mutable/Builder;
and one exception from AndEngine’s classes.
I finally got my solution by combining the typical optimization flags for Android with something I found from the simple-build-tool wiki. Resulting string to add to your proguard.cfg is:
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*,!code/allocation/variable
That did it for me, so I thought I’d share it.
with Proguard | without Proguard (Scala:Provided) | without Proguard and with lazyunpackage | without Proguard with scala cc (fcs:false) and lazyunpack | |
mvn clean package | 1 min 56 sec | 35 sec | 27 sec | 21 sec |
mvn package | 1 min 32 sec | 25 sec | 24 sec | 14 sec |
Here’s a nice way of shaving around 10 seconds off your average maven builds total time when building Scala Android applications.
You know the part when you need to unpack everything to have ProGuard go through all your classes to remove that extra fat. Well, even if you make a lot of changes to your code, most of the time is spent unpacking the libraries (Scala and Android mainly). And these libraries don’t change that often. So after you’ve done it once, and they have already been unpacked to your target directory, why do it again? Well, someone figured that out and now it’s added to maven-android-plugin in version 2.9.0-beta-5, so switch to that from 2.8.4 which is what you are probably using.
Then all you need to do is add the line:
<lazyLibraryUnpack>true</lazyLibraryUnpack>
under the <configuration> of your maven-android-plugin.
If this causes force closes on your device/emulator, check that your ProGuard settings are not too aggressive.
package com.studfarm.example
import com.studfarm.example.ParticleSystemSimpleExample._
ParticleSystemSimpleExample {}
package com.studfarm.example
import com.studfarm.example.ParticleSystemSimpleExample._
class ParticleSystemSimpleExample {
}
object ParticleSystemSimpleExample {
val CAMERA_WIDTH:Int = 720
val CAMERA_HEIGHT:Int = 480
}
package com.studfarm.example
import org.anddev.andengine.ui.activity.BaseGameActivity
class ParticleSystemSimpleExample extends BaseGameActivity{
override def onLoadScene() = null
override def onLoadResources() {}
override def onLoadEngine() = null
override def onLoadComplete() {}
}
object ParticleSystemSimpleExample {
}
Next we'll implement the methods.override def onLoadEngine:Engine= {
Toast.makeText(this, "Touch the screen to move the particlesystem.", Toast.LENGTH_LONG).show
this.mCamera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT)
return new Engine(new EngineOptions(true, ScreenOrientation.LANDSCAPE, new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), this.mCamera));
}
onLoadResourcesoverride def onLoadResources {
this.mTexture = new Texture(32, 32, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
this.mParticleTextureRegion = TextureRegionFactory.createFromAsset(this.mTexture, this, "gfx/particle_point.png", 0, 0);
this.mEngine.getTextureManager().loadTexture(this.mTexture);
}
particle_point.png is placed under /src/main/resources/assets/gfx/ folderoverride def onLoadScene:Scene= {
this.mEngine.registerUpdateHandler(new FPSLogger());
val scene = new Scene(1);
val particleEmitter = new CircleOutlineParticleEmitter(CAMERA_WIDTH * 0.5f, CAMERA_HEIGHT * 0.5f + 20, 80);
val particleSystem = new ParticleSystem(particleEmitter, 60, 60, 360, this.mParticleTextureRegion);
scene.setOnSceneTouchListener(new IOnSceneTouchListener() {
override def onSceneTouchEvent(pScene:Scene, pSceneTouchEvent:TouchEvent):Boolean= {
particleEmitter.setCenter(pSceneTouchEvent.getX(), pSceneTouchEvent.getY());
true;
}
particleSystem.addParticleInitializer(new ColorInitializer(1, 0, 0))
particleSystem.addParticleInitializer(new AlphaInitializer(0))
particleSystem.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE)
particleSystem.addParticleInitializer(new VelocityInitializer(-2, 2, -20, -10))
particleSystem.addParticleInitializer(new RotationInitializer(0.0f, 360.0f))
particleSystem.addParticleModifier(new ScaleModifier(1.0f, 2.0f, 0, 5))
particleSystem.addParticleModifier(new ColorModifier(1, 1, 0, 0.5f, 0, 0, 0, 3))
particleSystem.addParticleModifier(new ColorModifier(1, 1, 0.5f, 1, 0, 1, 4, 6))
particleSystem.addParticleModifier(new AlphaModifier(0, 1, 0, 1))
particleSystem.addParticleModifier(new AlphaModifier(1, 0, 5, 6))
particleSystem.addParticleModifier(new ExpireModifier(6, 6))
scene.getLastChild().attachChild(particleSystem)
});
return scene;
}
All done. Run the mvn clean package. You should now have a working application. You can test it with the Android emulator or with a real Android phone.<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>5</maven.compiler.source> <maven.compiler.target>5</maven.compiler.target> </properties>
<dependencies> <dependency> <groupId>com.google.android</groupId> <artifactId>android</artifactId> <version>2.3.3</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.andengine</groupId> <artifactId>andengine</artifactId> <version>1.0</version> </dependency> </dependencies>
<groupId>org.example</groupId> <artifactId>first-application</artifactId> <version>1.0</version> <packaging>apk</packaging> <name>first-application</name>
<groupId>com.jayway.maven.plugins.android.generation2</groupId> <artifactId>maven-android-plugin</artifactId> <version>2.8.4</version> <configuration> <sdk> <!-- Don't forget to set your ANDROID_HOME environment variable to your SDK directory! --> <path>${env.ANDROID_HOME}</path> <!-- Platform 10 is Android 2.3.3 --> <platform>10</platform> </sdk> <jvmArguments> <jvmArgument>-Xmx1024m</jvmArgument> </jvmArguments> <!-- All of these go into the /src/main/android/ directory, we don't want to polute the project root directory. --> <androidManifestFile>${project.basedir}/src/main/android/AndroidManifest.xml</androidManifestFile> <resourceDirectory>${project.basedir}/src/main/android/res</resourceDirectory> <assetsDirectory>${project.basedir}/src/main/android/assets</assetsDirectory> <nativeLibrariesDirectory>${project.basedir}/src/main/android/native</nativeLibrariesDirectory> <resourceOverlayDirectory>${project.basedir}/src/main/android/overlay</resourceOverlayDirectory> <deleteConflictingFiles>true</deleteConflictingFiles> <undeployBeforeDeploy>true</undeployBeforeDeploy> </configuration> <extensions>true</extensions> </plugin>
Last we need the most important plugin of them all, ProGuard. Without this plugin the apk generation will fail since the Scala dependency will generate too many files.
<plugin><groupId>com.pyx4me</groupId> <artifactId>proguard-maven-plugin</artifactId> <version>2.0.4</version> <executions> <execution> <phase>process-classes</phase> <goals> <goal>proguard</goal> </goals> </execution> </executions> <configuration> <injar>android-classes</injar> <libs> <lib>${java.home}/lib/rt.jar</lib> </libs> <obfuscate>false</obfuscate> <options> <option>-keep public class * extends android.app.Activity</option> <option>-keep public class * extends android.app.Application</option> <option>-keep public class * extends android.app.Service</option> <option>-keep public class * extends android.content.BroadcastReceiver</option> <option>-keep public class * extends android.content.ContentProvider</option> <option>-dontskipnonpubliclibraryclasses</option> <option>-dontoptimize</option> <option>-printmapping map.txt</option> <option>-printseeds seed.txt</option> <option>-ignorewarnings</option> </options> </configuration> </plugin>With these settings you should be able to create a working development environment for Android development with Scala. Make a test run by running the following command: mvn package to see if the environment compiles.
Regular compilation with Scala compiler is really slow. Most of the time is spent compiling parts of Scala libraries, instead of your own code though. Solution is to use fsc (fast scala compiler), which is daemon that compiles the required Scala libraries on the first compilation, and stays in memory after that, so your subsequent compilations after a lot faster.
My first guess was to just click the checkbox “Use fsc” in the Scala plugin settings page. This alone only causes exceptions. You also need to create a Scala compilation server instance for yourself.
Your second press of Play should be a lot faster!
<settings> ... <localrepository>C:/Users/someone/Documents/My Dropbox/localRepository</localrepository> ... </settings>
That’s it! Thanks Eric Lefevre-Ardant for the tip on changing the repo location.