Valentine's Day Libgdx App

    Periodically appear topics dedicated to Valentine's Day. This year, I also decided to join in this topic and do something original and unusual. It was decided to create a simple Android application with hearts that would have their own physical models and interact with each other. Later I added text, sounds, particles and some other beautiful things. The result is even something sane and original! This article describes the process of creating together a description of the features and pitfalls of the libgdx library .


    .


    Content



    Programs and Tools


    The following programs and libraries were used to implement the idea:


    1. IntelliJ IDEA is a free development environment for modular and cross-platform applications. An alternative is Android Studio , Eclipse .
    2. libgdx is a cross-platform (PC, Mac, Linux, Android) Java library for developing games and not other graphic applications. This library is licensed under the Apache License 2.0. Some sections of code are optimized using JNI (e.g. Box2d).
    3. box2d-editor - An editor for creating physical models used in the box2d physics engine, which is built into libgdx. Here it will be used to compare the pattern of the heart and its physical model.
    4. Hiero bitmap font generator - A program for converting vector fonts to bitmaps (since only bitmaps are supported in libgdx).
    5. Particle Editor - An editor for creating particle systems from the author of libgdx. Used for particles in the effect of an "explosion" in the destruction of the heart.
    6. Paint.NET - used to edit the image of the heart and create the background.

    All programs and components are freely distributed, and this is a big plus. My choice fell on the libgdx library, because, firstly, I already have some experience working with it, and secondly, when using it, there is no need for a slow Android emulator, since it is cross-platform and allows you to test applications in native java environment with subsequent compilation under the "greenback".


    Hello world


    First, I’ll briefly describe how to create libgdx projects. Using the gdx-setup.jargenerated project template (based on Grandle), which indicates the desired target platform. Currently supported Desktop, Android, Ios, Html. True, the last two failed to try, because I don’t have an Ios operating system, but there were difficulties with Html, which so far have not been solved.



    You can also immediately select which extensions to use. In our case, this is the Box2d physics library.


    However, all this is detailed in the wiki: Creating a libgdx project .


    After generation, three folders are created:


    • core
    • desktop
    • android

    In the last two, launchers are placed under the corresponding platforms DesktopLauncherand AndroidLauncher, which look like this:


    publicclassDesktopLauncher{
        publicstaticvoidmain(String[] arg){
            LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
            config.width = 800;
            config.height = 480;
            new LwjglApplication(new ValentinesDayHeartsApp(), config);
        }
    }

    publicclassAndroidLauncherextendsAndroidApplication{
        @OverrideprotectedvoidonCreate(Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
            initialize(new ValentinesDayHeartsApp(), config);
        }
    }

    There will be no more Android-specific code, which is a great advantage of the selected library. The only thing you need to enable vibration and sleep mode prohibition (so that the world does not reset) in the configuration AndroidManifest.xml. And also set the landscape orientation so that the world does not turn over:


    <uses-permissionandroid:name="android.permission.VIBRATE"/><uses-permissionandroid:name="android.permission.WAKE_LOCK"/>

    and


    android:screenOrientation="landscape"

    Core stores common code. The main class is ValentinesDayHeartsAppone that implements interfaces ApplicationListenerfor handling initialization, rendering, finalization, and other states, and InputProcessorso that user input can be processed.


    Everything, the frame is ready! Now this application will work on both PC and Android.


    General


    The project has a simple structure: the class ValentinesDayHeatsAppoverloaded methods create, render, dispose, touchDown. The method createinitializes all resources (textures, fonts, particles, sounds), creates the physical world. The method rendermiscalculates and renders all the objects in the world:


    @Overridepublicvoidrender(){
        updatePhysics();
        updateBackground();
        updateSprites();
        updateParticles();
        refresh();
        renderBackground();
        renderHearts();
        renderFonts();
        renderParticles();
    }

    In the method dispose, the release of all resources. Yes, yes, despite the fact that Java has automatic garbage collection, unmanaged resources (Box2d objects and some others) still need to be freed manually. The method is touchDowntriggered by a mouse click or by touching a touchscreen. It works like this: if the contact point intersects with some heart, then it is removed. Otherwise, a new heart is created at the click site. The object \ "heart \" Hearthas the following properties:


    • Body - physical model.
    • Sprite - graphic model (sprite).
    • String - text displayed on the heart.
    • Font - The font used to draw the text.
    • ParticleEffect - particles created upon destruction.
    • BreakSound - sound upon destruction.

    Next, I will describe the components of the application in more detail.


    Textures


    First, it was necessary to find or create directly the heart itself. Fortunately, I easily googled it, and then edited it a bit: added a glow and a transparent background. To load textures into libgdx I used a class Texture. Since the same texture can be used several times, additional objects were used Sprite. They are drawn in the method render. The sprite's position and angle are the rendering parameters and the physical model of the heart. For a change, I decided to make the hearts have colors with different shades. To do this, the HSL palette was used, which allows you to manipulate hue, saturation and lightness, and not directly the color components like RGB. The conversion formula RGB -> HSL and HSL -> RGB can be easily found, and I ported the methods from the articleManipulating colors in .NET in Java. All conversions are in methods prepareHeartsTextures, prepareHslDataand generateHeartTexture. Here is an example of one:


    Pixmap pixmap = new Pixmap(fileHandle);
    float[][][] result = newfloat[pixmap.getWidth()][pixmap.getHeight()][4];
    for (int i = 0; i < pixmap.getWidth(); i++)
        for (int j = 0; j < pixmap.getHeight(); j++) {
            int color = pixmap.getPixel(i, j);
            float r = (float)((color >> 24) & 0xFF) / 255.0f;
            float g = (float)((color >> 16) & 0xFF) / 255.0f;
            float b = (float)((color >> 8) & 0xFF) / 255.0f;
            float a = (float)(color & 0xFF) / 255.0f;
            result[i][j] = ColorUtils.RgbToHsl(r, g, b, a);
        }
    return result;

    Unfortunately, the Android application loads with some delay due to the generation of textures with different shades.


    Fonts


    Since libgdx can only work with bitmap fonts, I used the Hiero Bitmap Font Generator program (version 5), which creates images of all characters in png format and an fnt file that contains information about the coordinates of each character in the image. Here is a screen shot of this program:


    After the necessary files are generated, the font can be used in the libgdx application as follows:


    font = new BitmapFont(
    Gdx.files.internal("data/Jura-Medium.fnt"),
    Gdx.files.internal("data/Jura-Medium.png"), false);
    font.setColor(Color.WHITE);

    and then render like this:


    font.draw(spriteBatch, heart.String, screenPosition.x, screenPosition.y);

    When rendering, I came across some nuances: for example, the font cannot be rendered at an angle, as can be done with the sprite. To solve this problem, you need to change the projective matrix y SpriteBatch, and then render the font as follows:


    Matrix4 projection = spriteBatch.getProjectionMatrix();
    projection.setToOrtho2D(0, 0, WorldWidth, WorldHeight);
    projection.translate(tmpVector1.x, tmpVector1.y, 0);
    projection.rotate(0, 0, 1, body.getAngle() / (float)Math.PI * 180);
    projection.translate(-tmpVector1.x, -tmpVector1.y, 0);
    Vector2 stringSize = heart.getStringSize();
    tmpVector1.add(heart.Size.x / PhysWorldWidth * WorldWidth * CenterDisplacement.x
                       - stringSize.x * 0.5f,
                       heart.Size.y /  PhysWorldHeight * WorldHeight * CenterDisplacement.y
                       + stringSize.y);
    spriteBatch.begin();
    BitmapFont.BitmapFontData fontData = font.getData();
    fontData.setScale(heart.Size.x * FontSizeHeartSizeCoef.x, heart.Size.y * FontSizeHeartSizeCoef.y);
    font.draw(spriteBatch, heart.String, tmpVector1.x, tmpVector1.y);
    fontData.setScale(1, 1);
    spriteBatch.end();

    Physics


    Box2d was used as a physical engine .


    To compare the graphical and physical models of the heart, I used box2d-editor :


    Using this program, I created a polygon of a heart that automatically broke into convex polygons. The physical model is essentially a set of coordinates of these polygons in json format.


    Further this file is used in the application (loading occurs in the method addHeart). libgdx can only download files in binary format. Fortunately, the BodyEditorLoader.java class was found , with which you can load the model from JSON, i.e. text presentation.


    Do not forget to set the density, friction and elasticity of the body:


    FixtureDef fdef = new FixtureDef();
    fdef.density = 0.75f;
    fdef.friction = 1.0f;
    fdef.restitution = 0.4f;
    bodyLoader.attachFixture(body, "Heart", fdef, newWidth);
    body.resetMassData();

    That's it, now hearts also have a physical shell!


    In order to prevent hearts from flying out of the screen, four static rectangles are simply created on the sides of our little world. On mobile devices, it is advisable to change the gravity depending on the orientation:


    if (Gdx.app.getType() == ApplicationType.Android) {
        gravity.x = -Gdx.input.getPitch() / 90.0f;
        gravity.y = Gdx.input.getRoll() / 90.0f;
        gravity.mul(gravityCoef);
        world.setGravity(gravity);
    }

    Particle system


    In libgdx, the particle system is defined using special files that can be generated in the editor:

    .

    As you can see, this editor has a lot of settings: you can load various textures, change the lifetime, distribution form, transparency and other parameters. I made particles in the form of hearts that will appear when you press and destroy one large physical heart. In the application, work with particles occurs as follows:


    Initialization


    ParticleEffect effect = new ParticleEffect();
    effect.load(Gdx.files.internal("data/destroy.p"), Gdx.files.internal("data"));

    Life cycle start


    It is important not to forget about startwithout which particles will not be displayed:


    effect.setPosition(.., ..);
    effect.start();

    Sounds


    Sounds are loaded as follows:


    sound = Gdx.audio.newSound(Gdx.files.internal("path/to/file"));

    and then play like this:


    sound.play(1);

    It would seem that could be easier? However, there are also pitfalls here. The fact is that for some reason, only .ogg format files were downloaded and 96 kBit / s bitrate .


    Conclusion


    I hope the knowledge described in the article will be useful to many, and it will come in handy in developing games using libgdx. Sources and resources are allowed to use by all. Give Valentine's Day apps to your halves :)


    It is worth noting that all the words displayed on the hearts can be changed in the file data/words.txt. This works even without recompilation.


    Sources and executables



    Also popular now: