Renoki: Making An Android Game (part 10)

Added level functionality. Level is a preference (need to save the level when the app is closed) that is stored in the global application context, along with all the other saved player states (hammerType, gold, score, health, etc). I insert monsters and adjust speed based on the level. At the moment, there is a timer that runs for 10 seconds until the level ends and progresses to the next.

Something I wanted to implement that was missing from the iphone version was waiting for all monsters to retreat before ending the round. People were complaining that they would try to hit something only to have the LEVEL COMPLETE message pop up and block them. I basically just stop inserting monsters when the timer is past the limit (10 seconds right now) and don’t end the round until the entire gameboard is empty. I think this is a lot better.

I also added the money drops for monsters. Instead of creating a money object and replacing the monster with it, I merely change the monster bitmap to be the diamond. Then I reset the duration of the monster to make sure it doesn’t immediately run away. I also had to add an extra state isMoney to the monster to check if it’s been turned into a diamond. If isMoney is true, I can ignore all the features of the monsters and just immediately kill it.

The only main feature remaining is the store, which Allison is working on.

Renoki: Making An Android Game (part 9)

I coded a good amount this past week and managed to complete a good chunk of work.

Re-organizing Bitmaps – Originally I just initialized bitmaps in the main view. I decided that was a bit too limiting and moved them into the global application context. I grouped bitmaps into arrays depending on the game state (menu, help pages, gameboard, monsters, items, etc). I realized when making monsters that I needed access to multiple bitmaps (different stages of the animation) and so having all the bitmaps be globally accessible makes things a lot simpler. This way, I can declare multiple NumberMonsters and they all share the same bitmap, without having to pass in 4-5 bitmaps per object.

Number Monsters – Number monsters are of course the monster that has a number on it which forces you to hit the corresponding number location instead of the monster itself. In the code, we refer to this as owning a spot. The problem we’ve always had with these guys is when you go to hit a normal monster and a number monster appears in another spot and takes ownership of the spot you’re about to hit. Then you tap down and the hammer is redirected to hit the number monster, instead of your intended target. Unless the user has fast reactions or is aware of this case, they become confused. Where did my hit go? Why is this monster still here? Why did another door open and immediately close with no monster in it?

To prevent this problem from happening, you have the number monster appear, but be un-hittable until the door has fully opened. At this point, it is blank and has no ownership. Once it is fully in view does it then take ownership. This eliminates the problem of doors opening and immediately closing with no monster (that looks like a bug). It also makes it much more obvious where the hammer is being redirected to, as you can see the full number. Of course, you are still going to have people not noticing that a number monster appeared but there’s only so much I can do without sacrificing the entire game mechanic.

Shield and Sword Monster – These were very similar. In certain states they play one animation and cannot be hit. In other states they act like normal monsters. Interestingly enough, once I had figured out how to do the spinning animation for the new NumberMonsters, I just leveraged this code for Shield and Sword. It was a simple state diagram where one state loaded a different bitmap sheet and reacted to hits differently.

So now the core set of monsters are done. The only things to add are higher levels (trivial, I just adjust the health/damage they do), money monster (trivial, I just make hitting it steal money), and bosses (somewhat trivial, just modify the stay time, health, and force the location). The next step is work on the framework for levels.

Renoki: Making An Android Game (part 8)

We finished moving into our new apartment, which involved a large number of boxes. Along with those boxes was a lot of Ikea furniture that required assembly. As such, I didn’t have much time to work on Renoki.

I did a significant amount of code cleanup. I also added several new monsters. There is basic2, which is basic1 but with more health. This was trivial as I just changed the health of the basic monster. I added the bomb, which was also trivial. All monsters do damage to the user when you hit them, with regular monsters dealing 0 and bombs doing damage. Finally, I implemented the number monster. The number monster appears with a number on him, requiring you to hit the corresponding numpad location rather than the monster himself.

In order to do the number monster, I had to use two arrays. One array contains what each position owns. Thus, if a number four monster appeared in position one, I would put a 1 in index 4. The second array contains what each position is owned by. For the same situation, I would put a 4 in index 1. Then, when you touch a position, you first check these two arrays. In the event of ownership, you must change where the hammer animation appears and what position actually gets hit.

I made another decision to simplify code. Every X frames, you insert a monster at a random location. If that location is occupied, you just don’t insert for that turn. Similarly, since multiple number monsters cannot own or be owned by more than one number monster, duplicate ownership will just cause the insert to fail. I was considering making it keep generating random numbers until it finds a match, but I realized that this adds nothing to the game while simultaneously increasing the delay of calculation. Since everything is random anyway, having random failures to insert monsters are fine. I can always offset this by increasing the speed at which monsters are inserted.

In addition, Allison started to work on it as well. She wrote up the menus and buttons, as well as the start screen animations where the clouds scroll by and the monster blinks. She is going to start working on the shop soon.

Renoki: Making An Android Game (part 7)

Better Pausing

The reason I was obsessing so much over behavior when you press Home, is I was afraid my app would be consuming so many resources while theoretically “closed”. It would be hanging onto a ton of memory from loaded bitmaps, music, data structures, etc. In addition, my original design would have the game loop continue to spinlock until the user resumed the game. This would drain battery and result in people getting all pissy.

I had wanted to use myThread.wait() and myThread.notify() in the beginning, but I couldn’t seem to get it to work right. It was only after I realized that calling myThread.wait() does not actually make myThread pause. It makes whatever thread you’re currently in pause. So the way to actually make my game loop pause is to make my main Activity set a pause flag in the game loop thread. Then the game loop will check that flag and pause itself with wait(). Then later on, you can do myThread.notify() to start the thread again and make the thread disable the pause flag immediately after waking up.

Renoki: Making An Android Game (part 6)

The Home Button

I decided my next step was to implement player state information (lives, items acquired, gold, points, etc). I realized that by adding these, I would also have to handle saving state between closing and opening of my app. This is where I ran into some annoying issues.

The lifetime of your Android app can be modeled by this:

http://developer.android.com/images/activity_lifecycle.png

What’s annoying is that pressing “Back” and pressing “Home” are completely different things. Pressing “Back” will cause the kernel to destroy your app, effectively closing it and freeing all the resources your app has allocated. Originally, I had planned that when a user resumes playing my app, they would always begin at the start of whatever level they were last at. This would save me the trouble of saving tons of game state variables, like what monsters are in the game board at the time they last exited. Instead, I just need to save the level. If the user exits my app with “Back”, then all of this works as planned.

The problem occurs when the user exits my app by pressing “Home”. The Home key will immediately return to the homescreen, but will NOT destroy your app. It will call the onStop() method and then your app will continue to run in the background. Interestingly enough, this is the exact same behavior that will occur if you receive a phone call while playing your app, or any other activity interrupts your app. This is where I have two choices, which are implemented very nicely by Plants vs Zombies and Angry Birds.

If Plants vs Zombies gets interrupted, either by Home Key or phone call, when you restart it you will see a loading bar. Once loading is complete, it will place you in the exact state you were in except the game is paused. This implies that they are manually freeing memory or something of the sort, and when you resume they have to spend the time to reload it. On the other hand, when Angry Birds gets interrupted, restarting it is instant. There is zero loading time and you immediately return to the exact moment but paused. This implies that the Angry Birds pause screen is still running at full capacity in the background of your phone.

So for my game, I’m probably going to take the Angry Birds approach and just have my game continue to run in the pause screen whenever users press Home. The game loop will continue to run, just each iteration will do nothing. All the graphics and music will remain in memory and resuming the game will be pretty much instant. Luckily, I can still use my original plan of starting the game at the beginning of the level when the user exits correctly with the “back” button. Since everything is always running in the background, I will not need to save any player state when the user exits with “Home”.

Renoki: Making An Android Game (part 5)

I worked a little bit today. Added hammer swing animation and logic to handle hitting monsters.

And yes, the hammer swings will queue up if you hit before the previous swing animation is done. It’s a simple queue that accepts up to 3 hits. After that, it just disregards future touches until the queue frees up a space. Hitting pause will always enter into the queue.

At first I thought I was going to need some synchronization because the hammer hits are independent of all the gameboard logic. Then I realized that instead of making the hammer swings asynchronous and needing locks, I can just make the hammers occur on the game clock.

Basically when you tap, you insert the location into the hit queue. On the update part of the game loop, you first check whether a swinging flag is true (animation already going). If not, then you remove from the queue and do a hit on that location and set swinging flag to 1. In the draw part of the game loop, you draw as long as the swinging flag is true. The only synchronization I need is around the queue.

Renoki: Making An Android Game (part 4)

My First Big Step

I worked on Renoki a lot this weekend. This was a combination of Allison being gone and the HoN servers being under attack from a DDOS making games unstable. Regardless, I managed to get a monster loaded and the door animations working. And yes you can faintly hear the radio station playing annoying Miku Hatsune in the background.

Anyway, I set up my framework exactly how I described it in one of the earlier parts. There is a loop that runs continuously that continually draws sprites to a screen and also updates their state. There is an array of sprites, with each index corresponding to one of the nine locations on the game board. For now, it just inserts sprites at constant rate, whereas in the future it will be random and dependent on the level speed.

The animations are done by using sprite sheets. This is where I take five frames of an animations and just paste them one after another onto one big image. After you load the combined image, you create a viewable rectangle that only shows one of the frames. Then, you just shift the rectangle over 1 frame at a time and that creates the moving animation.

The monster object is completely independent of the rest of the framework. By this, I mean that the monster controls the door open and close animation and how long it stays on the gameboard. Once the monster decides it is dead or retreats, it sets a flag and the loop will remove it from the array.

Renoki: Making An Android Game (part 3)

Dealing with Multiple Resolutions

Something that had originally confused me was how I was going to set the game board to support multiple resolutions. As of Android 1.6, you can basically have any resolution you want, ranging from the original Android G1 phone 480×320 to my Thunderbolt with 800×480. The way Renoki is going to be made is to have a static background image (the game board) and the monsters, doors and hammer swings would be sprites I animate on top of it. The question is: How do I take a static back ground image and make it look right on two phones, where one is almost double the resolution of the other?

http://developer.android.com/guide/practices/screens_support.html
Turns out Android handles this by just lumping everything into small, medium and large categories (and extra large categories for tablets). They also use two terms to describe this: “size” and “pixel density”.

http://developer.android.com/images/screens_support/screens-ranges.png

Size is the physical dimensions of your device, across the diagonal. So anything that is less than maybe 2-3 inches is small, 4-5 is medium, 5-7 is large, anything bigger is extra large. Even if two phones have slightly different physical dimensions, they would still get lumped into the same category. Thus, most phones tend to fall into the medium size, and since I’m developing Renoki only for phones, I don’t need to really concern myself with size.

Pixel density is the number of pixels per inch. Now the Thunderbolt has absolute pixel (px) dimensions of 800×480, but Android has another unit called density pixels (px). If you query the screen height and width from within your app on the Thunderbolt, it will return 533x320dp. If you did the same for the G1, you get 480x320dp. The reason is that the Thunderbolt has a high pixel density while the G1 only has a medium pixel density. On the Thunderbolt, 1 density pixel is the equivalent of 1.5 absolute pixels. On the G1, 1 density pixel is 1 absolute pixel.

Thus, you can pretend the Thunderbolt has a 533×320 resolution when specifying the X and Y coordinate of the sprites. As long as you always use dp for your units, Android will automatically rescale everything for you. Therefore, putting a sprite at position (50,50) will be the same relative location on both the G1 and the Thunderbolt. If there are other phones with other strange resolutions, 1 dp might equal 1.33 px and so on.

There are still 2 different things to worry about. Firstly, although Android will automatically rescale everything for you, this does not prevent them from becoming warped or distorted when scaling. Just like when you blow up a tiny image it looks blurry, the same thing would happen if you provided an image for a mdpi phone and viewed it on a hdpi phone (enhance…enhance…enhance). Android solves this problem by allowing you to provide multiple versions of the same images, resized for different pixel densities. In the ‘res/drawable’ directories, there is a ‘res/drawable-ldpi’, a ‘res/drawable-mdpi’ and a ‘res/drawable-hdpi’. You might put a 48x48px image for mdpi, and a 72x72px image for hdpi. You give all the images the same name and place them in the appropriate directory in your Eclipse project. Android will automatically pick the image depending on the device you are running your app on.

http://developer.android.com/images/screens_support/screens-densities.png

The last thing to take into account is when you have screens with different aspect ratios. The G1 is 480×320 or 1.5. The Thunderbolt is 533×320 or 1.66 (repeating of course). This means the Thunderbolt screen is a little bit longer (or taller, depending on how you want to say it) than the G1. When I display an image like the game board, you will see “more” of the image on the Thunderbolt than the G1. The only way to really handle this is to design for the smallest aspect ratio and to only put non-essential things outside of that.

I will make my main game board (the doors, the score, your hammer, your gold, etc) all fit into a 480×320 box. I will then extend the image another 53 pixels underneath with some non-essential image or just plain color. On the G1, everything under that red line won’t even show up. On the Thunderbolt, maybe I would put an image of a monster waving at you or something else stupid. Regardless, the game board is the same size and nothing essential is cut off. Unfortunately, I don’t think there is a better solution than this other than just not supporting certain aspect ratios.

Renoki: Making An Android Game (part 2)

I’ve been somewhat busy recently with CMU graduation one weekend and apartment hunting the next. However, I did manage to do some learning.

I sat down and watched this long tutorial which gives a good background of Android graphics.

I’ve also begun to look at several tutorials on how to setup a game loop.
http://p-xr.com/android-tutorial-how-to-make-a-basic-game-loop-and-fps-counter/

Finally, I am taking a look at a tutorial on animating sprites
http://p-xr.com/android-tutorial-how-to-paint-animate-loop-and-remove-a-sprite/

The basic structure of the code will be simple. You have a loop running that continually updates the screen depending on a state. The state will follow the below Finite State Machine. Depending on the state, you will have various sprites or other actions appear on the screen.

Linux Is Dumb

I want to know what stupid fucker decided that the standard tab width for linux kernel code would be 8 spaces. I can understand the 80 character limit, the spacing for *, the braces style, and all the other crap required to conform to their style. Seriously, does Linus Torvald program with 8 width tabs? Once you’ve indented a few times for functions or nested ifs, it’s literally impossible to write a line of code without breaking it into multiple lines, making it twice as hard to read.

This pisses me off so much.