I wanted to finally fix some lingering issues with Renoki and get the final version up on the market. This will be version 4.0 and I believe that this will be my final version for now.
I have been struggling with tablet resolutions for a while now. When I was first designing the graphics, I made a stupid decision and created them for a 480×854 resolution, because I thought that would be the largest resolution I would support. Unfortunately, a bunch of other phones came out with enormous resolutions (1280×720) and I realized that my app was still showing up for tablets.
Supporting Tablet Resolutions
One of the annoying things I ran into was that I set Renoki to have a minimum Android API level of 7 (froyo). This means that any device with froyo or newer can download it. Given that ~25% of phones are still running froyo (, I felt this was a good decision to make. Unfortunately, if you do not want to support tablet resolutions, there is no way to make your app not show up,). There are some things in the Android Manifest you can do (support-screens, compatible-screens) but the useful mechanism were not introduced until API level 9. This means it is IMPOSSIBLE to prevent a level 7 app from appearing on tablets.
The next best thing I could do is run the app in screen compatibility mode. What this would do is create the smaller resolution game in the middle of the screen and surround it with a black border for all the unused pixels. While this doesn’t offer a full experience, it would at least prevent the game from crashing. The problem was that the view the game was in was still the entire screen size (e.g. 1280×720), but I was only drawing within my original 960×540. I had a few things based on the screen width which were screwed up by this, and images I had go “offscreen” didn’t really go offscreen and instead just drew on the black border. It was in general a very screwed up thing.
The solution I realized was to hardcode my base view to a specific height and width, and use the view width to do my calculations. This basically solved all my problems and allowed it to run correctly on any resolution.
My app has 1 FrameLayout (@id=frameLayout) that contains a single custom SurfaceView. By creating a set of LayoutParams and setting the width, height, and gravity, I made that root FrameLayout conform to the size I wanted. Then, my app always thinks it is running on the correct resolution, regardless of the actual device resolution. I also made sure to multiply by the pixel density “dip” to make sure it works on all devices.
So now I have an app that “works” on all resolutions, although it is extra tiny on big tablets. However, this is good enough for me and I definitely don’t want to spend the time creating all the images for this game at bigger resolutions.
Leave a Comment
Renoki has been on the market for a while and I’ve gotten a lot of feedback on the game. Here are some of the improvements I’ve made so far.
Improving the Shop
People didn’t know what the items did. In particular, nobody had any idea what the wings did (which is pretty understandable). Anyway, I added a pop up box when you select the item and you have to press “BUY” in order to actually make the purchase. The pop up box has a short description of what the item does, hopefully eliminating all question.
Having the informative picture on the start screen tended to be completely ignored. I’m not sure if it’s because the picture itself is unintuitive, or people don’t even realize that it has information on it. Anyway, I added some text to the start screens to make it painfully obvious what you are supposed to do. I really wanted to avoid using text but I couldn’t see any better way of doing it with the limited space I had.
Improving Number Monsters
Nobody understands the number monsters (correction, one person from work understood it). Anyway, I added text explaining what to do and also the first level with number monsters have the numbers painted on the door. This “should” make it painfully obvious what the point of this monster is.
Adding More Feedback
Someone brought up a good point that if sound is off, there is no feedback on good or bad hits. At first I thought of using the vibration feedback that Android already has built in for keypresses, but realized this might be too intrusive for users. Plus, it requires an explicit permission and I’m trying to keep my app permission free right now. So instead I have the entire gameboard briefly flash red when a bad press occurs. I think this worked out very well as it clearly stands out and indicates that something went wrong.
From an early stage of development, I wanted to add a Renoki Encyclopedia where I could list every Renoki along with funny descriptions/flavor text. I stumbled upon ViewPager, a brilliant little thing for Android that allows you to automatically do horizontal swiping of pages (similar to the current Android market). All you need to do is drop in some images to each page, and the user can easily swipe through each one. Now I just need to think up funny back stories for each Renoki.
I think the game is too hard for people. I toned down the speeds for Normal and lowered shop prices. I realized that people actually mess up a lot so they have to buy hearts quite frequently. Someone mentioned that they got far in the game but were never able to save up enough to buy a hammer. I needed to amend this, as the later levels get really hard without the upgrades.
Leave a Comment
Or search “Renoki” on Android market. The Amazon Appstore dev fee is free for 1 year, so I’ll be uploading there as well soon.
Leave a Comment
I finally decide to just publish the app. I was going to go and balance test it some more, but I got lazy. Plus, publishing the app will motivate me a lot more to keep fixing it once bug reports and such start flowing in.
You can find in the Android Market by searching Renoki.
Leave a Comment
I discovered a very annoying thing recently that made me have to reconsider how I handled touch input.
Originally, my game consisted of a game loop that updates the state of all the sprites and then draws them onto the screen. Touch input was handled asynchronously, meaning I need a lock around my update section. The reason is that I don’t want touch input to affect the state of anything while I’m in the middle of updating, otherwise I could have a race condition where the update section is about to jump to state X, the user presses a button to go to state Y, and then the update section resumes and pulls it back to state X.
Now I understood the problems that could arise from this. Touch input needs to be processed relatively quickly or you get Android Not Responding errors. I felt that even if my touch input had to wait for 1 or 2 update loops to finish, that is still only fractions of a second. Unfortunately for me, sometimes the Android kernel will decide not to give the touch input priority to run. Instead, it will allow the update loop to keep acquiring the lock over and over, resulting in the dreaded ANR. And there wasn’t any sort of fair lock I could use, because I was just using Java’s “synchronized” command, which is supposed to handle locking for you.
I realized that I would have to make a queue of all touch events. When a user touches, it enqueues into a touchQueue. In each loop iteration, I dequeue 1 touch and process it. This way, you only have to lock around the touchQueue, which greatly reduces the chance of a touch event getting blocked.
It’s at this point that I realized something else stupid. When a touch occurs, it calls some onTouchEvent(MotionEvent event) and passes you a MotionEvent object which describes properties like the x, y coordinates, and whether it was a down or up press. Unfortunately, you need to make a copy of the MotionEvent it passes you, because after the onTouchEvent() is done, it will reuse that object. I sat around for a while, wondering why the MotionEvents in my queue were getting corrupted and it took me forever to realize this.
Now you need to consider having a pool of Events. It’s inefficient to recrete a new Event object every time a new touch is made. It’s better to have a pool of Event objects. When a new touch is made, you request an Event object from the pool. If the pool is empty, it creates a new one for you, otherwise it hands you a pre-existing one. When you are done with the Event object, you add it back to the pool for future touches to use. This saves on the overhead of dynamically allocating a new Event object every time there is touch input.
Leave a Comment
FrameLayout with a ScrollView
My game consists of a single FrameLayout with a child SurfaceView. This essentially gives me a blank canvas to draw anything I want to, which works fine because I have a static background. What I wanted was when the user presses the “About” button, I would display the credits and the user could vertical swipe to scroll through them. At the bottom there would be an exit button. A bonus would be I could interweave images in between the credit lines, but that wasn’t necessary.
I think the typical way to do this would be to create a new activity that you create with the credits. However, I didn’t want to stop my current activity because I use a gameloop and gamestate that is local to my main activity. What I really wanted was for when the user touches the “About” button, a new ScrollView appears on top of my current SurfaceView and I change the gameState to be the ABOUT state. This is a surprisingly annoying topic to search and I had to look at a dozen posts online until I could get something to work.
First of all, you will need to create your ScrollView programmatically instead of through XML. One of the things that originally turned me off of Android was how they used XML to define the layout of the UI. I’m sure there is a good reason why they did it, but all I’ve noticed is it pisses me off and makes it harder to find the answer because most of the time people provide XML solutions. Anyway, you need to make sure your FrameLayout has an ID field so you can get a reference to it in your code.
A FrameLayout can have multiple children views, where each view will overlap the previous one. So what I want is to create a ScrollView and add it to the FrameLayout. A ScrollView can only have 1 child view (you can only insert 1 view into it). Because I want to display both images and text in the scrolling area, what I did was insert a LinearLayout into the ScrollView. Inside that Linear Layout, you can insert as many ImageViews, TextViews, etc that you want. It will all be scrollable.
Lastly, when you want to close the ScrollView, you merely remove it from the FrameLayout. You can remove it either by passing the same View you originally created or passing the index of the View you want to remove and using removeViewAt(int ID);
The neat thing about this is if you set the size of the ScrollView to be less than the fullsize of the original SurfaceView, you can still see whatever is underneath it. TouchEvents on the non-covered sections will still trigger on the original SurfaceView, so you can reuse whatever touch logic you have from your original SurfaceView. This is how I close the ScrollView.
Leave a Comment
It’s been a long time since I made post about this. I’ve been kind of lazy about updating this and uploading a video to youtube.
Surprisingly, sound was relatively easy to implement and did not produce as much of a strain on my app as I expected. There are two types of sounds: short, quick sound effects that are played frequently and long background clips. To play background clips, I use the default Android MediaPlayer, which takes an mp3 file and streams it. This allows for lower memory usages as opposed to bringing the entire mp3 into memory. For sound effects, I created a SoundPool for quick retrievaly. You essentially pre-load all your clips into memory and can play them immediately. This is more efficient than having to create a new MediaPlayer object for the hundreds of little beeps and blips that play while smashing Renoki.
The only thing to take note of is that you have to kill your mediaplayer every time you stop the app. Otherwise it wastes resources and also keeps playing in the background. That and making sure you take into account pausing and resuming your app in every possible method. I ran into quite a few points where I would turn the app off in the middle of something and resume with no sound.
The victory screen now gives you the option to Continue, enter the shop, or return to menu. Originally, the shop was a random monster that appeared and when you hit it, it opened the shop. Although neat, this confused the hell out of people (I still don’t understand how it isn’t painfully obvious that it’s a shop when it opens) and didn’t really add any value to the game. So now you can enter the shop in between levels no matter what and buy what you need.
The shop is the same as the normal gameboard. Each item is a monster that never retreats and only dies if you have enough money to kill it. Had to add some logic to make certain items disappear or not show up depending on what the user already had. For example, if a user owns the gold hammer, the bronze and silver hammer should disappear.
Bosses worked out incredibly well with my existing architecture. I merely had to add a boolean flag in the regular monster as to whether it’s a boss or not. In the event that it’s a boss, it draws an additional little health bar above it’s head that decreases in size as it’s health goes down. I have a special case in my levels at 5, 10, 15, 20 to insert the boss monster into the corresponding position and set the stay time to be huge. I then just had to change my level timer logic to make the player lose if the time ran out before they killed the boss, instead of winning.
Unlike iPhone, Android has a back button that allows you to navigate between screens of the same app. I had to override the default action to prevent back from accidentally closing the game. Instead, if you are playing it pauses, if it’s paused you go to the menu, and if it is at the menu, it exits with a ghetto confirm box (art pending). The thing I need to decide on is whether pressing back in the confirm exit screen exits the app or just closes the confirm box. Some games (Angry Birds) pressing back twice will confirm and then exit. Other games (Cut the Rope) pressing back twice will open and close the confirm box.
Switching Number Monsters
I realized (quite late) that a Number Monster (the guy where you hit the corresponding number location and not the monster) didn’t need to have “health”. Instead, it would have a hit count, and each time it was hit the monster would rotate and switch to a new number. Once the hit count ran out, the monster would automatically be destroyed. Luckily, my architecture for monsters was flexible enough to allow this by just modifying the hit method in my NumberMonster class. I already had the spinning animation when the monster first appeared, so all I had to do was reset the monster so it would spin again, while subtracting the hit count. So now I have stronger number monsters that take 2 hits with 2 different numbers and also a boss that works the same way but with a lot more health.
One enormous nested if/else shitfest that draws hearts depending on the life of the player. I considered making an algorithm that could scale for any number of hearts, but I realized it wasn’t worth the effort because a player can’t have more than 6 hearts (24 life).
I actually have a ghetto scrolling credits page right now. It looks really terrible though, so I’m thinking about just making a victory graphic with all the Renoki or something and have a credits button on the main menu.
I have to run a separate thread that loads all the images and sounds. Meanwhile, my main game thread has to just loop and wait until the loading is complete.
Leave a Comment
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.
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.
Leave a Comment
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:
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”.
Leave a Comment