Word Search Blitz is my third Android game, and I think things have come a long way since Goblin Market was first released. I'd like to share some aspects of the development process to help others, especially those who might just be getting their toes wet in game development, learn a thing or two.
Goblin Market was developed using a custom engine that was based largely on the ideas found in the book Beginning Android Games by Mario Zechner (which is an excellent book that I highly recommend). The engine was really rough around the edges, but doing things manually instead of using a pre-built engine really helps in the learning process.
For Adaman, I moved over to using Libgdx. There are a couple of Android-focused game engines out there, but Libgdx was a very natural fit for me, especially because it was created by the author of the book previously mentioned. It was still very much a learning process, especially since Libgdx had so many more features than my custom engine.
Word Search Blitz was also created with Libgdx, but this time I was much more comfortable with the engine. There were some bumps along the way, but overall I think this is the game I'm most proud of.
Choosing an Engine
While I went the route of creating a custom engine for my first game, I would never want to go back after using Libgdx. Creating your own engine is a great learning experience, but it can really detract from what you're trying to accomplish - making games. You can literally sink hours into tracking down bugs in your engine. And if you're doing this in your spare time, it can really kill your momentum. My advice is to evaluate the available engines out there, play around with a few, and pick the one you like.
I can easily recommend Libgdx for Android development. It's cross-platform, has a very active community, and good documentation. AndEngine seems to be another popular engine, but I found their documentation severly lacking. Regardless of which engine you choose, the one vital feature is to make sure you can run it on the desktop outside of the Android emulator. This is the #1 time saver. The Android emulator is so slow it's laughable and practically unusable for game development. I didn't have this ability for Goblin Market and I wasted so much time just waiting for the game to start up after each compile.
As previously stated, my engine of choice is Libgdx. Which leads me to my next topic...
Scene2d is a 2D scene graph implementation available in Libgdx. One of its main uses is for building UIs for game menus, but I've found that it actually works out really well for 2D games in general. Both Adaman and Word Search Blitz use Scene2d exclusively for everything you see in the game.
At its core, Scene2d consists of a stage that holds one or more actors. As an example, the screenshots below show how I'm using actors for all of the elements in the game.
Traditional Mode With Actors
In the screenshot on the right, every actor is highlighted. If we start at the top, we have:
- Board Actor - generates and renders the board, handles touch input, raises events that other actors can react to.
- Letter Actor - each letter on the board is its own actor.
- Highlighter Actor - highlights a discovered word.
- WordList Actor - shows the list of words remaining in the puzzle.
- Label Actor - each word in the word list is a Label actor (Labels are a included in Libgdx).
- Clock actor - while the entire clock is an actor, each digit is not. I'm using TextureRegions to render the numbers since I have a glow effect applied to the graphics which I couldn't do with a normal BitmapFont.
- Image Actors - I don't have these highlighted because it would be the entire image, but basically the background starburst image as well as the dark blue rounded rectangles on the board, word list, and clock are also actors.
Another nice thing about using Scene2d is that you gain the ability to use Actions. Actions allow you to perform some action on an Actor in your scene over a period of time. Things like moving, rotating, and changing color, can all easily be performed with a single line of code. Actions can be executed in sequence or parallel, which makes more complex actions possible. All of the animation in Word Search Blitz is done through the use of actions.
One of the only things I came across that couldn't be accomplished with Scene2d was rotating nine-patches. Originally I had wanted to use a nine-patch for the highlighter actor that is used to highlight the letters that you swipe over on the game board, but I ended up having to scratch that and use a dynamically generated Pixmap implementation instead.
If you find that you'd rather do things without Scene2d, you can still render things using SpriteBatches. To replicate the functionality of Actions, you can use TweenEngine, which works well with Libgdx.
If you do decide to use Scene2d, it's worth taking the time to understand TableLayout. TableLayout allows you to specify the size and position of your actors using a logical table. It's a lot like using HTML tables to position your actors on the screen. This can free you from needing to hard-code absolute pixel positions in your UIs, and makes things work better in multiple resolutions. Word Search Blitz uses table layouts exclusively on every dialog in the game.
The only thing I would suggest is to make sure you read the documentation very closely, because it can be a little confusing at first to understand all the effects that different cell properties can have on your actors. I recommend making a simple standalone project that you can play around with for a while to get a feel for how the layout works.
As you work with TableLayout, one very helpful feature is the table's ability to toggle debug lines. It's super easy to do:
It's important to remember that you must call Table.drawDebug on the stage itself or the debug lines won't be drawn. In the screenshots below, you can see the outcome of using debug lines.
Rankings Dialog With Debug
Libgdx includes a Dialog actor that can be used to show a dialog to the user. Dialogs have the ability to be modal and support being dragged around on the screen. The content of the dialog is handled by two tables (via TableLayout) - one table is for the dialog content, and another is for any buttons you want in the dialog.
Word Search Blitz relies heavily on dialogs for a lot of its UI outside of the actual game screen. I ended up running into a decent amount of problems with dialogs because I wanted to make sure that they respected the back button on Android devices and would close themselves when the back button was pressed.
Libgdx uses the concept of an InputProcessor to handle input events. When user input is detected, whether it's a touch, a key event, or a gesture, that event gets forwarded to whatever is the current input processor. In the case of Word Search Blitz, each screen is the processor and forwards all events to the stage, which is also a processor.
For key events, the stage will send the event to whichever actor currently has keyboard focus. If no actors have keyboard focus, nothing happens. In Word Search Blitz, if I see that no actor has handled the back key, I then allow the screen to do any logic required when the back key is pressed. That's how on the title screen, dialogs are closed when the back key is pressed, but if no dialog is present, the screen ends up going backwards through the menu, or prompting you to confirm that you want to quit if it's at the root menu.
The problem I discovered is that while the Dialog actor automatically registers for keyboard focus when it is shown, it never restores keyboard focus when hidden or removed. This was a problem because when having more than 1 dialog at a time, after the first dialog closes, keyboard focus is not restored to the dialog underneath. This meant that after closing one dialog, pressing back again caused the screen to execute its logic instead of closing the original dialog.
To get around this, I implemented my own Dialog called GameDialog that can correctly handle restoring keyboard focus. Below is the class in its entirety.
There are two other tidbits I wanted to share about dialogs. The first is a rather annoying behavior that causes the touch event of any button in the button table of the dialog to cause the dialog to close. This behavior is wired up in the base Dialog.initialize method, which is private. It sets up a ChangeListener on the button table so any change event on any actor in the table will trigger it. There are no methods or properties that you can set to disable this behavior.
So, as an example, let's say you have a "submit" button that has to perform validation before allowing the dialog to be closed. If the validation fails, you obviously don't want to close the dialog. If you aren't aware of this behavior, you'll be wondering why no matter what you do, the dialog still closes.
To prevent this from happening, you can do one of two things:
- In your change listener for your buttons, make sure to call event.stop() to prevent the event from being propagated to the button table.
- Don't use the button table and instead place your buttons in the content table. This is the approach Word Search Blitz uses because I'm lazy and didn't want to have to remember to always call the stop method.
The second item I wanted to share is the use of a Label actor with word wrap enabled within a dialog. If you try to do this, you'll notice real fast that the dialog incorrectly autosizes itself and look completely screwed up.
The reason for this behavior is that when the Dialog packs itself, it calls each actor's getPrefWidth() method to get their preferred width. It uses these widths to determine how large to make the dialog. In the case of the Label actor, enabling word wrap causes the getPrefWidth() method to return zero.
The fix for this is simple, but unless you know where to look, it could take you a while to figure out. All you need to do is override the getPrefWidth method to return Label.getWidth:
Framebuffers, or FBOs, are an OpenGL mechanism that, in a nutshell, allow you to render things offscreen to a texture that can be used for a multitude of purposes. Libgdx provides a FrameBuffer helper class that allows you to use framebuffers very easily. Word Search Blitz uses a framebuffer to render the "share" graphic that is sent when sharing your score with friends.
When first learning how to use framebuffers, the problem I ran into was that all the examples I could find always had bits and pieces of how to use framebuffers, but never a complete example. A lot of times, there would simply be a comment line in the example code that said "render stuff here", which wasn't very helpful. So through some trial and error and some help from the Libgdx irc channel, I was able to finally get things working. The cool thing about it is that you can simply reuse all of your Scene2d actors to render to the FBO. In Word Search Blitz, this is how I accomplished creating the graphic for sharing:
Using the above method, it's trivial to share the graphic in Android from your activity:
Oh boy, keyboards. This was a source of a lot of frustration. Once I had a playable release candidate, I gave out copies to some friends. They quickly alerted me to a host of issues all related to the soft keyboard. My test device is a Nexus 7, unrooted, with the standard keyboard. On my device, everything worked great. On my friends devices (mostly phones), some of which had default keyboards and some which used Swype or some other custom keyboard, support was spotty at best. In some cases, the user had to type the key, and then tap on the TextField to get the key to register. In others, the keys didn't register at all, and as I later discovered, the new gesture type in Jelly Bean 4.2 also didn't work.
This all boils down to support for ACTION_MULTIPLE on soft keyboards as well as the wonderful IME spec that doesn't really have any standard that IMEs have to adhere to when sending events for keypresses, as detailed here.
Originally, when I saw that Libgdx didn't support ACTION_MULTIPLE, I figured that I'd go add support for it if I could. After tons of digging through Libgdx source, the sparse documentation on IMF for Android, and lots of forum and stack overflow crawling, I got pretty close, and was able to almost get the gesture type working, but it still wasn't 100% accurate. And if there's one thing you need to be aware of, it's that users will trash your app on the store over the smallest of reasons, and I knew I couldn't just leave things at a "good enough" state and expect to get away with it.
So, knowing that I just couldn't get keyboard support working 100%, and knowing that I didn't want to have to fall back on ugly native prompt dialogs one field at a time, I ended up rolling my own keyboard from scratch:
Custom Word Search Blitz Keyboard
Implementing one isn't as hard as it sounds, especially since the TextField in Libgdx has support for invoking custom keyboards. Below is the code for the entire class. Hopefully this will help others because this was a question I've seen come up in the Libgdx forums quite a bit where people end up just falling back on the native Android dialogs for input.
Note - This is straight out of the game and I didn't try to make it 100% generic. There are some things in the code that are specific to Word Search Blitz that you'd obviously have to adapt to your own game.
Once the custom keyboard is in place, using it is really easy:
When first designing Word Search Blitz, I had envisioned something more akin to a classic arcade machine high score list, where the user can only type in their initials, and if the the same person got the top 10 scores, then so be it, and you'd see their initials plastered all over the screen.
As it turns out, there are two reasons why this approach didn't work:
- Accountability - in the days of the arcade machine, there's no easy way of getting around having to actually earn your score. With a typical server architecture for storing the high scores, there's no quick way to prevent malicious users from just sniffing the traffic, finding out the URLs and POST data required, and submitting a million top scores of zero seconds.
- Information Overload - After playtesting a bit, I really didn't want to only store the top 10 or so scores for each puzzle code. But I also didn't want the same user to play the same puzzle 100 times and take up the first 10 pages of a high score list.
To get around the accountability issue, I decided on a full-blown authentication system. Without going into too much detail, I use an expiring token-based authentication system that utilizes asymmetric encryption for securing everything that is transmitted between the client and server. This, coupled with a captcha mechanism built into the registration process, prevents most typical automated attacks from succeeding. It's absolutely not bulletproof, but it presents a big enough barrier that unless an attacker is really determined to screw with the scores, they will probably pass me by and look for an easier target.
To solve the information overload issue, the solution was to only store each user's best score for a particular puzzle. That way, each page of results on the high score list is actually meaningful, instead of pages and pages of the same user.
Keep in mind that my requirement was unique in that I needed to have essentially unlimited high score lists that had to be generated on the fly, since each puzzle code has its own high scores. If you only need a single high score list, I would suggest using something pre-built, like Scoreloop. And if you don't mind being tied to Facebook, you can always use their SDK for authentication instead of rolling your own.
Both Goblin Market and Adaman were paid apps, but I knew right from the beginning that I wanted to try the ad-supported model with Word Search Blitz. I'm not a fan of the "freemium" model that is unfortunately getting more and more popular these days because in many cases, the end result is that users end up getting gouged over time for the extra crap that is largely required to play effectively. With that said, I do like when developers offer a version of their game with zero ads and a single up-front cost. That's the route I chose for Word Search Blitz.
Integrating ads was pretty easy. I didn't spend too much time looking around at different ad networks, and ended up going with Google's AdMob service. I used this article as a base for implementing the ads into the game, since setup is a little bit more involved when using Libgdx, but it's still relatively straightforward.
One troubling item that I came across literally a week before I thought I would be finished is Adblock Plus. I happened across an article detailing how it had just been released, and that it didn't require a rooted device to block ads in other apps. I downloaded it and fired up Word Search Blitz and sure enough, the ads weren't displayed. Because ads are the only source of revenue for the game, I decided to take steps to detect ads being blocked.
To handle this, regardless if an ad is received or not, certain tests are performed against the /etc/hosts file to handle rooted devices that have AdMob servers redirected to a bogus IP, as well as package detection of known ad blockers (not just Adblock Plus). Unfortunately, there's no way to differentiate between ad blocking software being installed but not active vs. actively blocking ads. As a result, whether or not the user actually has the ad blocker running, I still stop the game and display a screen that politely asks the user to uninstall the ad blocking software.
Keep in mind that you end up walking a fine line by putting this type of detection in your app, because while I feel it is necessary, you must make absolutely sure that it doesn't screw up a legit user's experience.
When building your signed application for distribution on Google Play, I recommend taking the extra time to configure Proguard to obfuscate your code. It can be a bit of a hassle to get working properly, but it prevents your code from being (very easily) reverse engineered.
Proguard is built into the Android ADT for Eclipse, which makes it a snap to enable when building a signed package. All you need to do is uncomment the line in your project.properties file in the root of your project that starts with "proguard.config=". By default, this points to a specific template that is a part of the Android SDK, but it won't work for Libgdx or AdMob. What I do is point it to a file called proguard-project.txt in my project root. Below is the full contents of that file that you can use as a base for your Libgdx application.
It is very important that you always test your signed package on your device before uploading the APK to Google Play. If you get your config wrong, you can suddenly have crashes in your app that don't happen when the app is not obfuscated.
Free and Paid Versions
Early in development, I originally thought I would have the free version of the app with a single in-app purchase to remove ads. That was until I looked at the documentation for how to implement the payments. Talk about a train wreck of an API. I will say that towards the end of development Google did release version 3 of their billing API that promises to be a much leaner implementation (and for their sake I sure hope so).
Since I decided to go with two versions of the app, I needed a way to set up my project structure in Eclipse to support both versions. I decided to use Android library projects and it worked out really well. To start, I always use the very excellent gdx-setup-ui to create my core, desktop, and Android projects. In order to target two different Android apps, I turned the generated Android project into a library project:
Setting the Library Project
Once that's done, create two more new Android projects, and delete their assets directories. Then, link the assets directory from the original Android project to the two new ones. This way, both paid and free projects "inherit" all the assets that are in your library project:
Finally, in the properties of both the free and paid projects, set up the original Android project as a referenced library project:
With all the setup out of the way, you'll want to make your main Activity in your library project like usual. Then, in your paid and free projects, derive from that activity and implement whatever logic is necessary to differentiate between versions.
I hope you found this postmortem to be an interesting insight into the development process and maybe learned a thing or two along the way. Feel free to contact me or hit me up via Twitter or the forums if you have any comments or questions.