Hi, Habr! In this topic, I would like to share my impressions of the libGDX game engine, talk about the everyday life of an ordinary indie developer, and lift the veil of secrets over the game that I have been doing the past few months in my office-free time. I hope these notes will be useful for those who are just starting to do something with libGDX or for those who choose the engine for the “game of their dreams.”
And sorry for the cats. They have absolutely nothing to do with the igrostroy. I’m at the same time learning (trying to learn) to draw and now these training cats of mine are just everywhere! They demand that they, idlers, be shown to someone.
Drawing
Speaking of drawing. Of course, I’m being showered with horny CG artists (and they will be absolutely right), but how can I drag myself from the process! I never thought drawing was so cool! I had a long time ago, in the midst of all the rubbish, lying around someone donated a graphics tablet. Bamboo something there, I think everyone buys the same "to be pampered." And then, once, I came across a book by Mark Kistler "You can draw in 30 days." No, naturally, learning to draw in 30 days is all the same as learning C ++ in the same time. Everyone understands this. But it turns out, drawing is not some intangible talent there, not elves herding unicorns in the meadows of uncharted islands. It is just a set of rules, as simple as the syntax of a programming language. Plus experience, of course! Hand stuffing But after all, a good programmer develops in a similar way, solving problems, studying patterns, technologies and following all sorts of best practices.

So, in this book (or rather the book, it is not serious at all) there are all the basic rules to start drawing something more than a stick-pickle. And when the hand-drawn squiggles stop causing you horror at least, it's cool! The process itself begins to bring pleasure. For me personally, this was a revelation.
')
Still, taking this opportunity, I want to advise a great editor - SketchBook Pro. It draws and pros, but it is exactly the kind that is not needed by a professional. Yuzerfrendli, not overloaded with functionality. A license is relatively inexpensive, unlike Photoshop. Although this abusive “software by subscription” scheme is used here, the basic functions are available for free.
I use only 3 tools: a pencil, a ballpoint pen, a brush. There is, I don’t know how to call it correctly, the straightening function on the run of your crooks is a mega useful thing! We take in one hand a feather, the second for a space (turning and scaling the canvas) and going! Create creativity.
Why not Unity?
But I digress. When it comes to choosing a game engine, the first and only question that now arises is “why not Unity, dude?”. Therefore! Because I'm used to Android Studio (I wrote a corporate application lately), I know Java more or less, the Android SDK. And the use of such a multifunctional monster as Unity for simple 2D games is a little “from the cannon on the sparrows”, don't you find? A stranger IDE, a different approach, again. And the size of the empty project in 20 MB? On libGDX, the whole game, along with graphics (which are not so few) weighs less! I understand that we live in the age of fast Internet, but still, the dependence between the size of the application and the number of installations exists.
If you dig deeper, the bottom line is that the warning “The size of the application is too large, are you sure that you want to download it not via Wi-Fi?” (Literally, I don’t remember, but this is the essence) - it cuts off a certain number of impulsive installations.In general, if you have previously dealt with the Android SDK, libGDX is an excellent choice for a smooth transition to cross-platform development. I will try to formulate all the pros and cons that I learned from the first acquaintance with libGDX.
pros
- Free.
- Cross platform The desktop build is equally seamlessly built and runs under Windows and Mac (there was no way to check on Linux, but I'm sure it is the same). For HTML, I ran into a small nuance: all the packages that you add should be written into the Core project in gwt.xml, otherwise it won't be built. With Android, there are no problems at all, I still have iOS in the plans.
- Low entry threshold for Android developer. Same Studio, Same Gradle. We have a Core-project in which we describe all the game logic and the Android-project - this is, roughly speaking, the MainActivity on which the View lies, and in it the Core-project is executed. From the core project, we can easily call the platform-specific part that we write in the Android project. And what is very nice, we implement the platform code (advertising, analytics, gaming services) directly by Google tutorials. As in the usual application. That is, no libraries, third-party extensions, and so on!
Minuses
- I have not found a relevant, sensible tutorial. There is a lot of information, even here, on Habré, but usually it comes down to how to display the sprite on the screen. In practice, it is not necessary, why? It is more reasonable to use higher level abstractions: Actor, Scene (see below). When I got acquainted with the new engine, in Quikstart Guide, I would like to see the following: correct scaling of the game for various screens, background loading of resources ( everything is fine with that), control of screens / scenes and objects on them.
Continuing to talk about the cons, I have a feeling of a slight understatement in libGDX. There are excellent abstractions here: Screen is a game screen, Stage is a scene or a layer, Actor is any object on this scene. In my game I decided to make one Screen and many Stage for all game screens: levels, menus, etc. From Actor we inherit game objects, then we just do
stage.addActor (ball) and that's it. Further Stage is engaged in drawing, movements, etc. this object. So Actor, in itself, does nothing. It does not display sprites (and 99% of game objects are sprites), you cannot check the collision between two actors, in general, nothing!
You have to make your SimpleActor and already inherit game objects from it:
Codepublic class SimpleActor extends Actor { private final TextureRegion region; public SimpleActor(TextureRegion region) { this.region = region; setSize(region.getRegionWidth(), region.getRegionHeight()); setBounds(0, 0, getWidth(), getHeight()); } @Override public void draw(Batch batch, float parentAlpha) { batch.draw(region, getX(), getY(), getOriginX(), getOriginY(), getWidth(), getHeight(), getScaleX(), getScaleY(), getRotation()); } }
If you need animated game objects (well, where do without them?) We write SimpleAnimatedActor:
Code public class SimpleAnimatedActor extends Actor { private Animation animation; private TextureRegion currentFrame; private float stateTime; private boolean started; public SimpleAnimatedActor(float frameDuration, Array<TextureRegion> frames) { animation = new Animation(frameDuration, frames); currentFrame = animation.getKeyFrame(stateTime); setBorders(frames.get(0)); } private void setBorders(TextureRegion sample) { setSize(sample.getRegionWidth(), sample.getRegionHeight()); setBounds(0, 0, getWidth(), getHeight()); } public void start() { stateTime = 0; started = true; } public boolean isFinished() { return animation.isAnimationFinished(stateTime); } @Override public void act(float delta) { super.act(delta); if (!started) { return; } stateTime += delta; currentFrame = animation.getKeyFrame(stateTime); } @Override public void draw(Batch batch, float parentAlpha) { batch.draw(currentFrame, getX(), getY(), getOriginX(), getOriginY(), getWidth(), getHeight(), getScaleX(), getScaleY(), getRotation()); } }
To implement a primitive collision checker, you can add Polygon to our SimpleActor:
Code Polygon polygon = new Polygon(new float[]{0, 0, getWidth(), 0, getWidth(), getHeight(), 0, getHeight()}); polygon.setPosition(getX(), getY()); polygon.setOrigin(getOriginX(), getOriginY()); polygon.setScale(getScaleX(), getScaleY()); polygon.setRotation(getRotation());
And so on. Of course, this does not cause any particular difficulties. But still, I can not understand, why not do something like this in the engine? And so it is necessary to drag your add code from the project to the project.
Fragmentation
How to make the game look equally cool on the zoo of all Android devices? For me, this is one of the key questions. Therefore, I will tell you in more detail how I solved the problem of scaling images in libGDX.
I like the already classic approach when we show more environment on tablets (4: 3) and less on tablets (16: 9). At the same time, nothing stretches anywhere, and game objects, one might say, do not change their size. All the graphics are stored in one resolution, I have it 960x1280 (4: 3). In the center of the screen we have a “play area” 720x1280 (16: 9) where all objects with which the player interacts are required to fall, the rest is scenery and can be cut off depending on the particular device. The easiest way to illustrate this principle with a picture:
LibGDX provides all the features for this. The only caveat - some objects may need to attach to the edge of the screen. Like the Home button in the image above. To do this, we need to know the actual left and right edges of the screen in the game coordinates (with the top and bottom easier - it is always 1280 and 0, respectively). Let's write some code:
Code public class MyGame extends Game { public static final float SCREEN_WIDTH = 960f; public static final float SCREEN_HEIGHT = 1280f; public static float VIEWPORT_LEFT; public static float VIEWPORT_RIGHT; private GameScreen mGameScreen; @Override public void create() { mGameScreen = new GameScreen(); setScreen(mGameScreen); } @Override public void resize(int width, int height) { super.resize(width, height); float aspectRatio = (float) width / height; float viewportWidth = SCREEN_HEIGHT * aspectRatio; VIEWPORT_LEFT = (SCREEN_WIDTH - viewportWidth) / 2; VIEWPORT_RIGHT = VIEWPORT_LEFT + viewportWidth; } @Override public void dispose() { super.dispose(); mGameScreen.dispose(); } }
What's going on here? This is the main class of the game, inherited from Game. Here we create and install the Screen (the screen, in my case, the only one) and override
resize () . This event for Android is called when the application starts and here we can calculate the left and right edges of the picture (VIEWPORT_LEFT and VIEWPORT_RIGHT).
In GameScreen, we need to install
FillViewport , it is he who is responsible for ensuring that the image is cropped if the aspect ratio does not match the target. Viewport is our window to the game world. They are of several types, the
documentation about this will tell even better than me, and I just give the code:
Code public class GameScreen implements Screen { private final OrthographicCamera mCamera; private final Viewport mViewport; private final AssetManager mAssetManager; private Stage mActiveStage; public GameScreen() { mCamera = new OrthographicCamera(); mViewport = new FillViewport(MyGame.SCREEN_WIDTH, MyGame.SCREEN_HEIGHT, mCamera); mAssetManager = new AssetManager(); mActiveStage = new MyStage(mViewport, mAssetManager); } @Override public void render(float delta) { mCamera.update(); Gdx.gl.glClearColor(65 / 255f, 65 / 255f, 65 / 255f, 1f);
Here, in general, everything should be intuitively clear, just remember to do
mViewport.update (width, height, true) with
resize () .
Mechanical Box
Now, when I talked briefly about the engine, I would like to say a few more words about the game itself. I haven't played games for a year, the corporate quagmire almost sucked me. But this did not stop me from dreaming. To dream of a quest, a puzzle as difficult as possible. Without prompts for money and other fritupleyny nonsense.
This is how the idea of ​​the Mechanical Box appeared - a device created with one sole purpose: to reliably protect what is inside. Where each layer of protection is a separate, independent puzzle with a new game mechanics.
A huge plus indie, we can afford to do what we want. I don't care about the retention rate and other marketing things. Yes, the gamediz of any commercial studio will shoot itself, having seen the percentage of players stuck on the first level! And I can just make a game that I would like to play myself.
In general, in August of this year I started the construction of the Box. Having drawn a little bit, I realized that I could not draw out such volumes, and my art did not suit me in quality. He, to put it mildly, peculiar. As a result, an artist was found at gamedev.ru who agreed to get involved in this adventure. Surprisingly, it turned out that Vladimir and I live not only in the same city, but also in the neighboring streets. It was he who gave the Mechanical Box an appropriate appearance. I even saved as a memory “as it was” and “as it became”. The difference, I think, is obvious.
I am still working on the Mechanical Box, I have some thoughts on the development of this idea. If you decide to try your hand, the game is already published on Google Play. I do not give the link,
so as not to attract the attention of the UFO once again . And although the Box was rather quickly dismantled for parts on one popular resource, for a single person, this puzzle is a rather serious challenge. What, personally, pleases me. I can even predict where you are stuck. This is either a level with a crane (how to move the damn claw to the right?), Or three multi-colored squares (this mystery seems easy to me, but the statistics are a stubborn thing). Or, welcome to the "club of the lost zero" - well, this is already the elite.
Thanks for reading, ask questions, write comments!
Do good and throw it into the water.