Showing posts with label egg savior. Show all posts
Showing posts with label egg savior. Show all posts

Saturday, February 26, 2011

Baking game levels in Egg Savior

This article covers the way Egg Savior handles the rendering of static objects in order to save both CPU and APK size. The method presented is implemented into the last version of Egg Savior and is working really well, given the limited size of game levels.

The problem

Egg Savior currently has 30 levels. Each one of these levels include a generic background and a lot of bricks that act as platforms for the main character (the chicken). The bricks are all 64x64 pixels in size, and placed in a grid. The obvious yet inefficient way of rendering and interacting with bricks is to handle all of them one by one:

for brick in brick_list:
    brick.render()
    character.detect_collision(brick)

This code is inefficient for two reasons:
  1. Renders the same bricks in the same way all the time, one by one.
  2. Forces to detect collisions with every single brick, even if they are stuck together creating a larget platform
The bricks are static, they don't change during the gameplay. For this reason, it would be faster to render them just once into an intermediate buffer, and then just render this buffer on each frame. This saves a lot of CPU time at the expense of more memory usage. If you have huge levels it may not be a good solution, because devices limit the amount of memory granted to each application to a quite small value. For Egg Savior this worked very well, because the size of levels is somehow limited.

Different backgrounds

The first version of Egg Savior had only 10 levels, and the approach was to have a different background image for each level, with the bricks already included into it. It worked really well, but as more levels were added, it proved not very scalable, because each new level added a lot of data to the APK. This approach meant brick objects to be just invisible rectangles, without render code, because they were already drawn into the background. This enabled a way to optimize also point 2, because there were no reason for defining each brick individually. If three bricks were stuck together in a row, they were defined as a single larger brick, thus enabling less time to process collisions.

Run-time baking

The second iteration of the design, focused on solving the scalability problem with an algorithm that allowed baking the bricks above the background when loading a level. This also involved a way to easily define where to place the bricks. The solution was to use strings with spaces on empty cells and hashes on brick cells:

String levelPattern[] = {
        "           ",
        "           ",
        "#   #      ",
        "##  # #    ",
        "   ## # #  ",
        "## ## # # #",
};

With this language to define brick positions, we already know where to place the bricks on the background. Anyway, there is still a problem to solve: brick shape change depending on its neighbours. If a brick is the first or the last in a row, it has round corners, in other case, it has sharp corners in order to blend with the next or previous one. This leaves us with four cases to handle, depending on the previous, current, and next brick (the current brick is in the middle):
  1. [_#_]: The brick doesn't have neighbours.
  2. [##_]: The brick has only a neighbour on the left.
  3. [_##]: The brick has only a neighbour on the right.
  4. [###]: The brick has neighbours on both sides.
Four brick shapes with enhanced contrast.

The algorithm to generate the background with the tiles is as follows (I removed some obvious code to compute drawBitmap rectangles to enhance algorithm readability):

public static Bitmap bake(String[] rows, Bitmap baseBackground) {
    int nRows = rows.length;
    int nCols = rows[0].length();
    Bitmap newBitmap = Bitmap.createBitmap(nCols * TILE_SIDE, nRows * TILE_SIDE, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(newBitmap);
    canvas.drawBitmap(baseBackground, null, ..., null);
   
    for (int row = 0; row < nRows; row++) {
        for (int col = 0; col < nCols; col++) {
            char left = '#';
            char right = '#';
            if (col > 0) {
                left = rows[row].charAt(col-1);
            }
            if (col < nCols - 1) {
                right = rows[row].charAt(col+1);
            }
            char c = rows[row].charAt(col);
            Bitmap toRender = getTile(left, c, right);
            if (toRender != null)
            {
                canvas.drawBitmap(toRender, null, ..., null);
            }
        }
    }   
    return newBitmap;
} 

The getTile method just handles the four cases mentioned earlier, and returns the right bitmap from the app resources.

Automatic collision optimization

The baking of the bricks into the background solved the render part of the original problem (point 1). The bricks, which now consisted only in invisible rectangles, were defined by hand, looking at the hashes and chosing a smart way of arranging them in a minimum set of rectangles. This is a highly boring work that discouraged me to add more levels, and the perfect candidate for automatization.

Finding the minimum set of rectangles that covers all the bricks is not an easy task, because there usually exists more than one way of covering all the bricks with rectangles. Sometimes the best way involves using overlapping rectangles. Even though finding the optimal solution was an interesting challenge, I realized that including complex (and maybe slow) code to reach perfection was not worth in this case. Having 10% more rectangles than the optimal solution didn't impacted the performance in a visible way. I decided to go with a simple algorithm that provided a good enough solution.

Original bricks


The basic idea of the algorithm is to process each row independently, looking for contiguous blocks which define single rectangles, and then try to mix rectangles from consecutive rows if possible. As I said, this doesn't find the optimal solution, it is just a greedy algorighm that provides a good enough one.


Column joins Row Joins

This is the pseudo-code:

previousRow = new Set[Rectangle]
currentRow = new Set[Rectangle]
rectangles = new List[Rectangle]
for row in rows:
    currentRectangle = None
    currentRow.clear()
    for char in row:
        if char == '#':
           if currentRectangle == None:
               currentRectangle = new Rectangle(colNum, rowNum)
             else:
                 currentRectangle.grow()
        if (char == ' ' or isRowEnd) 
           and currentRectangle != None:
           if canMix(previousRow, currentRectangle):
                  mix(previousRow, currentRectangle)
            else:
                 rectangles.add(currentRectangle)
       currentRow.put(currentRectangle)
    previousRow = currentRow
return rectangles

The algorithm already tries to mix rectangles with the previous row ones while they are generated, and does a pretty good job joining lots of bricks into big rectangles. Another optimization that I did to the algorithm is to encode the previousRow as an array with the same width as one row, where each position of the array has a pointer to the rectangle that covers this cell or to null if it is empty. This way, finding the right rectangle to mix with in the previous row runs in constant time. Actually, my implementation doesn't need a currentRow structure, I overwrite the previousRow one after reading it...

I will not go into the actual implementation to avoid making this article infinite, but if anyone has some doubt on how to implement it, feel free to leave a comment and ask.

Conclusion

With the background baker and the rectangle optimizator, generating new Egg Savior levels reduces to actually thinking them and mostly writing a string with hashes. This code runs each time that the player starts a level, and is so fast that it is actually overshadowed by resource loading time.

Wednesday, February 16, 2011

A very exigent market for games

It has been almost four months since I published the first version of Egg Savior in the market. This game was mainly an experiment to learn a couple of new things. First of all, I wanted to learn how to develop games for the Android platform. Secondly, I wanted to learn how the market reacts to games. And finally, I wanted to learn how global apps perform, since OTempo, my former app, is a local app targeted to galician users.

For the first target, I am quite proud. The game performs quite well even on middle-end hardware like my old Galaxy Spica, and I was able to try graphics, sound and input. You can read my post on Creating 2D games with Android and Blender where I share some of my gathered knowledge.

About market reaction to games, I have learned a quite hard lesson: The market is very exigent, even with free games. Actually, I think that gamers don't care if your game is free or costs a couple of bucks, they expect a high quality product from the first version, and if the first version is not good enough, upgrading and enhancing it will not work like it would have done with other apps. Advertising has been very helpful on recognizing usage patterns on the first version and each upgrade.
I have to admit that this lesson was hard to fit, because I spent long hours working in this game, but sometimes one has to get back to planet earth and remember its objectives. It was a learning experience, and I have learned a lot. I realize that the artistic needs of the game far exceed my ability, and the number of levels is quite ridiculous, but it was not about fame and glory ;-)

About the global market, I also learned a valuable lesson. There is a lot of fragmentation. Every country behaves differently to this game. All the statistics that admob provides point to the fact that the usage pattern varies a lot from one country to another. The most obvious example is the case of Korea, where the game was a huge success, maintaining the first position in free games for several consecutive days. I believe that this is a great opportunity rather than a threat. It enables development of products with a strong personality, focused in a concrete market, and opens more opportunities for everyone. It also warns of the risk of starting a serious project (I mean, investing money) without any kind of market research.

Monday, January 24, 2011

Egg Savior reached 100K downloads in Korea

Some time ago, I made an agreement with UbiNuri to distribute Egg Savior in the korean market. On January 17, Egg Savior was published in the T Store, and the users reaction was really impressive. In Just five days it came to the first position in the free game category, with 50K downloads, which doubled during the weekend, making a total of 100K downloads after just eight days.

I want to thank UbiNuri and all the korean users who are enjoying this game, and promise new versions with more levels.

By the way, the korean name of Egg Savior is 달걀 지키기.

Sunday, December 5, 2010

Egg Savior website

I have finished the website of Egg Savior. There you will find images, videos, and instructions to play it, as well as the latest news and a download area for those of you without Android Market in your device:

Egg Savior website
From now on, general Egg Savior news will be published in this website, and The organic android will focus on technical articles derived from the development, as well as other apps like OTempo.

Saturday, December 4, 2010

Egg Savior 1.5 is here!

After a lot of work, I have published a new version of Egg Savior, my Android game. This version includes three new levels and these improvements:
  • Added a level panel, where you can see your achievements. I had advanced it here.
  • The last level includes something new that will change the gameplay in next levels.
  • Changed black background in small levels for a more interesting one. I hope that this makes the game less ugly :)
  • If the chicken is teleported into an obstacle, it dies. This makes the game a bit more realistic (As much as possible with teleportation system ;-)
  • Changed game icon for a more dynamic one. I hope this attracts more players.
If you are reading this from an android device, you can download Egg Savior from this link:
market://details?id=com.eggsavior

Monday, November 29, 2010

Egg Savior will have a levels menu

I have been quite busy these days developing a couple of new features of Egg Savior. One of them is the levels menu, that will let you know which levels have been played, and what was your score on them.
This menu is the reason why the previous version started to record scores. I wanted to share its design here, just in the case that anyone with good taste would like to suggest some enhancements before the next version is out:


I am planning to add 3 more levels in the next version, thus reaching a milestone of 20 levels. Expect it in the following days...

Monday, November 22, 2010

Egg Savior 1.4 in the market

I have just finished a new version of Egg Savior, with the following improvements:
  1. Adds a new level
  2. Warns if you try to move pieces while the game is paused
  3. Stores achievements on the phone. Players should not notice anything about this in the current version, but it the next version will do level blocking based on played/unplayed levels, and the game needs to gather info from current users to effectively block levels for them. You will be able to see your achivements in the next version too.
This is a little release because I was working really hard in my last article. Next version will be much more interesting, I promise!

If you are reading this from an android device, you can download Egg Savior from this link:
market://details?id=com.eggsavior

Tuesday, November 16, 2010

Egg Savior 1.3 fixes the force close bug

I have just published a new version of Egg Savior to solve a bug introduced in 1.2 that caused force close on some situations when going to sleep and coming back.
Sorry for the inconvenience.

Sunday, November 14, 2010

Egg Savior 1.2 in the market

I have just finished a new version of Egg Savior, with three new levels and the following enhancements:
  • Automatic scroll when approaching an screen edge. Can be configured, or even disabled if you dislike it.
  • Now you can drag the pieces that were below the inventory. This was a bug and on some devices could get very annoying.
  • The audio resumes after turning off/on the screen. This was related to this contribution in StackOverflow.
  • The game is now paused when you return from another app, avoiding you to lose the game if you had an incoming call.
If you are reading this from an android device, you can download Egg Savior from this link:
market://details?id=com.eggsavior

I am working on an in-depth article explaining the Blender/Android pipeline that was used to develop this game. Stay tuned!

Saturday, November 6, 2010

Egg Savior 1.1 in the market

Egg Savior has been updated. This new version includes two new levels and the following changes, suggested by users:
  • Added a fast-forward button (asked by a lot of people)
  • Disabled scroll when the whole level fits on the screen (should fix the problems with big-screen devices)
  • Fixed bug: having won, you could not restart the level to improve the score.
If you are reading this from an android device, you can download Egg Savior from this link:
market://details?id=com.eggsavior

Thanks for your support.

Friday, October 29, 2010

Egg Savior 1.0 published, worldwide!

After just a week of beta testing, it seems that the game is stable enough to be released worldwide. I have uploaded the 1.0 version to the Android Market, and its available for you to download.

This version includes 11 levels, of varying difficulty. I will be adding extra levels on each new release, plus bug fixes and gameplay enhancements as suggested by the players.

Thanks to all contributors who made this possible, especially to Diego F. Goberna for his invaluable help with graphics and Silvia Izquierdo for her patience and the help with the marketing and level design.

Thanks also to all the beta-testers, because without them, this first stable release would not be nearly as stable.

If you are reading this from an android device, you can download Egg Savior from this link:
market://details?id=com.eggsavior

Tuesday, October 26, 2010

Egg savior beta3 - available also in UK

I have released a new beta of Egg Savior with bugfixes and 6 new levels. This time I extended the beta to United Kingdom also, with a larger android user base than previous countries (according to admob stats).
Some of the new levels have a new item in the player inventory: the teleport device. This is a bit harder to use than previous items, because you can not predict at first where the chicken will exactly end. In fact, it always gets the same displacement, but you will have to get used to it before taking full advantage from it.

Monday, October 25, 2010

Introducing Egg Savior

As my regular readers already know, I've been busy these last two months developing an Android game. This game already has a name: Egg Savior. My goal with this project is to learn a bit about game development on this platform, similar to my goal with OTempo, but focused to a different audience.

Production

Because of this purpose, I defined a clear rule: the only budget of this project is my own free time. This means that I had to do coding, graphics, animation, game design, and even marketing. Of course, I asked for help to my friends, and I have to thank all of them for the great support that I received, specially during the latest stages of development. An exception was the sound, because I have not enough knowledge to create my own music or effects. But even on this case, I searched for free CC music at ccmixter and free CC sounds at freesound, both of them excelent sources that I recommend.

Being a software engineer with limited art and graphics knowledge (not to mention good taste), I had to set a limit on visual quality. In fact, my first attempts were quite a bit over-limited:

I have to thank Diego F. Goberna for his help with art an graphics, because he pointed me to the right direction with lots of advices and even a paintover that helped me turn the previous abomination into this:

Find the differences? ;-)

This has been a very inspiring project that besides knowledge, has given me confidence and has brought me forward to continue with more ambitious projects. I will not lie, this was a very time consuming project. I spent an average of 3 hours a day during these months (including weekends), trying to meet my own deadline, and working after my full-time job. I will not go into depth about the benefits of a deadline, but I want to make clear that it is essential for a personal project like this to come to an end.

And talking about times, I would like to share the time that I have needed to get to the first beta version, just in the case that others want to do something similar.

Coding (engine):90 hours
Art/Graphics:40 hours
Audio (search):3 hours
Marketing:10 hours

I created the marketing category just for things like filling the android market form, chosing nice screenshots, or telling the world about this game (like this post). Silvia Izquierdo helped a lot with these tasks, putting her marketing and advertising skills at my disposal.

Take these times with a pill, because no one has nearly the same productivity when working 8 hours a day for someone else's project than when working 3 for his own project... Not to mention that my arguments with the director (myself) are solved really fast :)

Gameplay

Ok, enough of production details. What is this game about? Well, I have to put some context before introducing the game idea: How did I came to this idea?

Ignoring the usual recommendations when doing something barely related to art (I mean, start with the idea), I started by setting the technical requirements of the game. Why? Because I wanted to learn, and I wanted to set a minimum set of skills to earn on the process. So, the game idea should include:
  • Sound
  • Frame-based animation
  • Drag & drop of game elements
  • Screen scroll
As you see, these were not very ambitious requirements, but they were enough for me. I specifically wanted to avoid device button interaction, because many devices are quite limited on this.

The original idea was that the user should build a bridge to carry the main character from one point to another of the screen. This bridge would be built from pieces by dragging and dropping them. The character would enter from one gate and exit from another. 

This idea evolved when I put face and body to this character: a chicken. Why a chicken?, you may ask. Because it is funny. So what is the purpose of this chicken? Why would the user bother helping it? Because there is a thief in the farm, trying to steal eggs, and if you do not help the chicken to reach the eggs and hide them by pretending to hatch, the thief will succeed on his evil plans, and this will be the end of the farm as we know it!

The game is composed of a set of levels, increasing in difficulty, or introducing new game elements, with a common objective: take the chicken to the egg.

You can see here a video of one of the hardest levels in the emulator:


Beta

At this moment, the game is in beta. First of all, I sent it to all my friends who have an android phone. After this, I have relased it to just two countries: New Zealand and Switzerland, to contain the amount of users that may not understand what beta means. Users from these countries are experiencing the first 5 levels of the game, and after I can fix any bug that may arise during this phase, I will publish Egg Savior worldwide with more levels.

Blender

All the game graphics were rendered with Blender. This is a great tool for 3D graphics, and it is quite easy to build a comfortable pipeline based on this software even for 2D games like Egg Savior. I will write a bit more about using this tool for 2D graphics in a future article. Stay tuned!

So this is the result of my hard work. I will write further articles about the technical details as soon as I get more time, but feel free to ask any question related to this game and I will do my best to answer it.