How to fill a bunch of cones and release a game

    It all started back in 2013. Then there were a lot of toys on Android, but in every way less than now. And having released your game on the then Android Market, you could get some money. And since I like to make games, there was no doubt - we release the game. We are a small team of a programmer, artist and tester / generator of ideas. Looking ahead - we collected plenty of cones and rakes. Basically, there will be a review of technical points, so the article will be useful to everyone who is somehow connected with the development of games. Do not repeat our mistakes.

    Game idea


    Of the existing games at that time, we liked the game where the ball rolls through the maze, dodging holes, and trying to get to the final pocket. At that time there were already a few such games, but in terms of graphics they were not very good. Therefore, we decided to make our own, with emphasis on colorfulness. I would like to note the artist, he painted very, very cool. Thanks to him, the game turned out as it is - beautiful.

    It was decided to use libGDX as an engine. I knew him well at that time, and I liked him, already completed projects on him. I was sure that we would reach a victorious end. As it turned out, we got it.

    Gameplay Features


    In principle, the gameplay turned out to be standard for games of this type - we roll the ball with the help of an accelerometer. An additional feature - we have teleporters, and walls can be not just boring rectangles. But more on that later.

    Of the features I want to note management. For a long time, we selected the accelerometer settings for a comfortable game, but our tester constantly noted that something was wrong. As an example, he cited a similar game from the Android Market. In the end, I downloaded this game, decompiled (I was still surprised that it was not obfuscated), and watched the moment with control. I found that the code is identical to mine, only the coefficients differ. I note that the decompiled game was also written in libGDX - that is, at that time mass games were already written on it.


    We can set up management

    Technical part


    As I mentioned, the game is written in libGDX. The game uses many resources - graphics, sounds, maps, fonts. Let's go through every moment.

    Graphics. There are several hundred sprites in the game. For performance reasons, all sprites were packaged in texture atlases. Separate atlas for the menu screen, separate for choosing levels, etc. A standard technique that saves both video memory and extends performance. The atlases were packaged by Texture Packer (available for download on the libGDX website). If I did this now, I would use the built-in TexturePacker class. Atlas packaging looks something like this - TexturePacker.pack (“folder-with-sprite”, “output-folder”, “atlas-name”). The advantage of this approach is that you do not need to switch from the IDE to another program to repack the atlases.


    This is how a reduced atlas with different balls looks like. This is a small part, we have several such atlases.

    The atlas format is 32 bits, png. Some pictures that are without transparency are packaged in jpeg atlases. If I did now - atlases without transparency I would pack in ETC1. LibGDX has native support for this format, and for Android it saves video memory.

    To manage the graphics (loading / unloading atlases, obtaining the desired picture), our own self-made class Images was written. I knew about the built-in AssetManager, but at that time I needed a more convenient solution. For example, if I want to get some background image, I want the previous requested background to be automatically unloaded. My decision allowed me to do such things.

    Stepping aside a little - when I write a game, I often do my resource managers. Often based on the standard AssetManager. The goal is easier access to resources. In the later stages of development, code readability plays a very important role. A well-written code, like a book. It’s easier to understand the line images.createButton ("green-button") than assetManager.get (TextureAtlas.class, "data / atlases / buttons"). FindRegion ("green-button"). Of the minuses of this approach - when you return to the game after a long time, you need to remember the features of these managers. From the pros - when you remember, then editing something is already simple.

    Sounds.All sounds in the game in .ogg format. Why not mp3? On some phone models at that time there were problems with playing several sounds in a row in the .mp3 format. I admit that these were my crooked hands, but there were no problems with .ogg. Fortunately, libGDX supports both formats; replacing .mp3 with .ogg was easy.

    In order to optimize, the sounds were squeezed to the minimum bitrate, the format was mono. Development was carried out on linux, used Sound Converter for conversion. In my opinion, Sound Converter is an example of a great utility that I still use. One small window with settings for the output format, and the “Convert” button. Great answer to monstrous converters.

    Fonts The game uses two types of fonts.

    The first type is .ttf fonts and the gdx-freetype library. It allows at run time to generate bitmap fonts from .ttf fonts. The advantage of the library is that you can set the desired font size right during program startup. This ensures that the font will always be exactly the size you need. Of the shortcomings - since the font is generated "on the fly" in the texture, this means that it will have RAM. And the larger the font size, the more it is needed, this very memory. Another non-obvious point - since the font is a separate texture, then when drawing any inscription there will be an additional draw call (except for the moment when we draw several inscriptions in a row). Based on this, the scope of .ttf was limited only to the About screen.

    The second type of font isdistance field fonts. The idea is simple - using the Hiero program (available for download on the official libGDX website), a special texture for the font is generated. Each letter has its own area in the texture. But this area is not drawn "as is". Instead, a special shader is used, which draws this region in a special way (of course, this also breaks the batching). Advantage - with a relatively small texture, you can draw the font in very large size without loss of quality. Disadvantages - the font needs to be generated in advance, and effects (shadow, stroke, etc.) are not available for the font. Also, as I mentioned, this breaks the batching. You can only set the font color during rendering. For our needs, this was enough, so it is Distance Field fonts that are used everywhere in the game.


    This is what the Distance Field font looks like. The background should be transparent, I just made it black for clarity.

    Cards. One of the “chips” of a good game is the amount of content. You can generate it automatically, or you can prepare sets of levels. We went the second way - we have more than a hundred levels. The levels are divided into boxes - there are 15 levels in the box. It is clear that with so many levels, the question arises of their convenient editing and storage. Need some kind of map editor. The matter is complicated by the fact that we have two types of cards - simple and “original” (during development we called them that way).

    Simple cards- these are standard labyrinths with rectangular obstacles for this type of game. To edit such maps, I wrote an editor - a simple Java program using Swing as a graphics library. Manually place obstacles, holes, etc. Obstacles can be rotated, resized as you like - in this regard, the editor turned out to be quite convenient. The tester complained that it was inconvenient, that actions should not be canceled - I understood it, and wrote cancellation / repetition of actions, having studied the Command pattern.


    Map Editor in action


    A simple banal map - nothing unusual

    Chip - to quickly test the cards, the editor had the ability to switch to the game. Immediately (in the same window) a game was launched where you could check the map. The mouse acted as an accelerometer. I can say that the editor’s writing is like that phrase, “it’s better to lose a day, then fly in five minutes.”

    Original cards. So we called pictures, hand-drawn by the artist, where individual elements of the picture are obstacles. Below is an example of such a card.


    The football field is already more interesting.

    It is clear that the standard map editor is not suitable in this case. I thought and solved the problem as follows - take the tiled editor. We create two layers in it - one background, there will be a picture of the original level. The second layer is the layout layer. A map is marked on it with primitives (circles, squares and polygons). For example, a hole is a circle with the name “hole” (tiled allows each object to be assigned a name). At the output, we get an xml file in tiled format. But this format does not suit us. Therefore, I wrote an additional utility that takes the generated tiled xml and converts it to my level format. At the same stage, it turned out that box2d does not support polygons that consist of more than 4 vertices - in other words, quadrangles (at least the box2d port for libGDX).


    We mark out the original map

    . Although this whole Frankenstein looked awkward, it fulfilled its task perfectly. Almost half of the game levels are “original”, and thanks to tiled we did without writing another editor. Conclusion - sometimes you don’t need to write your own something complicated and large, just look around, and take a ready-made solution with minimal finishing “for yourself”.

    Ball animation


    When the ball moves, it must somehow be animated, creating the illusion of movement. Some games of this genre do not bother with this, and use a simple static picture. We decided to get confused.

    We have several "skins" of the ball - glass, fire (lava), football, etc. At that time I was not aware of the possibilities of 3D in libGDX, and could not use the 3D model. Therefore, we decided to do frame-by-frame animation. A full revolution of the ball took about 50 frames. Depending on the speed of movement, I changed the frame rate, creating the illusion of faster or slower rotation. And of course I changed the rotation of these frames. I can’t say that it turned out perfectly, the 3d model would definitely give a more beautiful picture, but that is, that is.

    Physics


    The game uses the physical box2d engine (more precisely, its port under libGDX). Of the advantages - it is fast, and it has a lot of opportunities. Of the minuses - you need to know it :) A ball is a dynamic body, obstacles are static bodies. When the level is loaded, a physical model of the world is created, and, in fact, the game begins. When paused, the simulation stops. In principle, this is all that can be said about physics - there were no special problems with it.

    Additional chips


    The menu screen is complicated. It is also made as part of the game. A ball rolls on it, listening to the accelerometer. And when the ball hits the buttons, it bounces. And if you touch certain buttons with the ball in a certain order, the portal opens to the secret level :) The


    ball rolls and bounces off the buttons - like in a pinball

    There are many achievements - for completing levels, for a certain number of wins, for a certain time in the game, etc. . We did not use the standard Android Google Play achievements, but made our own achievement system.


    A small part of the achievements

    Moments I would do differently


    It is clear that there are no perfect solutions, and with experience comes the understanding that this or that part is made suboptimal. I will say that I would redo it if I made the game now.

    First moment- this is the choice of boxes. If you open the game and look at the boxes, you will see that there are dots on each box. Depending on the complexity, these points are more or less. So - to arrange these points, I wrote a class that loads a json file, and places points on this config file. The motivation why I did this was to save a place in texture atlases. That is, we store the general picture-background of the box, one point, and the order of placement of these points for a particular box. Why this is bad is the complexity of the modification. If each box was a separate picture, it is enough to correct the picture - and now you need to go into the config files and understand what and how. It would be better to store it just with pictures.


    Each box is not just a picture. This is the background + text file-config with the location of the dots.

    Second moment- I was very confused with optimization (the example above is a confirmation of this). All screens (menus, selection of boxes, game, etc.) I created in one copy, and simply switched between them. This eliminated the creation of the same screens every time we switch to another screen, but led to a cleaning problem. For example, the game screen stores many parameters for a specific level (for example, points, time, etc.). When we load a new level, we must first reset the state of the game screen. But as new chips were added, it was easy to forget to clear something. This "eaten up" a decent chunk of time, debugging such problems. When I make a game now, I do otherwise. If you need to change the screen, I save the settings necessary for this screen in some settings class, then create a new screen. That new screen is being created, and reads the necessary data from the settings class. At the same time, more resources are spent, but against the background of modern powerful phones this time is imperceptible.

    The above example about screens is just one of. Plus this approach - the game does not lag even on the worst phones. The minus of the approach is how many of them are there, such bad phones to spend so much time on optimization? For myself, I solved the problem like this - when buying a new smartphone, I intentionally chose characteristics slightly below average. If the game on it runs without brakes, I don’t bother with optimization - for most people the game will start and work fine.

    Conclusion


    There are no perfect games, just as there is no perfect development. After a while, we brought the game to the end, and she saw the light on Google Play. What can I say - despite all the bumps and rakes, developing games is interesting. All sorts of moments arise, some moments are not solvable, and you have to look for workarounds. But solving these problems raises your level as a developer. I was interested in writing this game, and I believe that it will be interesting to play it.

    Also popular now: