Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
Tags
(1 edit)

Creating the in-game animations

In the prévious devlog, I created a spritesheet containing all the images I need to create an Idle and a Fly Animation. This spritesheet was created with the libGDX Texture Packer, and comes with a .pack file, that contains the coordinates of every sprite in the spritesheet.

Thus I have the files Tom_Animation.png and Tom_Animation.pack. I put them in android ---> assets ---> Images

Loading the animation spritesheet

In the LoadingScreen.java, I load the spritesheet, like I load any Texture Atlas, with this single code line :

game.assets.load("Images/Tom_Animation.pack", TextureAtlas.class);


Creating the animations

In the Hero.java, to create the animations, we need to add this lines in the constructor :

TextureAtlas tomAtlas = game.assets.get("Images/Tom_Animation.pack", TextureAtlas.class);
Animation tomIdle = new Animation(0.1f, tomAtlas.findRegions("Tom_Idle"), Animation.PlayMode.LOOP);
Animation tomFly = new Animation(0.1f, tomAtlas.findRegions("Tom_Fly"), Animation.PlayMode.NORMAL);

And that's it ! We created 2 Animations, based on 1 TextureAtlas. Easy, ain't it ?

Details of an Animation declaration :

  1. 1st argument is the duration of a single frame. I put 0.1f, so every frame of my animations will last 0.1 second. Thus, an animation with 20 frames will last 2 seconds
  2. 2nd argument is the name of the frames (contained in the spritesheet) we'll use to create the animation. It's mandatory that all the frames of a single animation have the same name, and are differentiated with a number (ex : Tom_Idle_000, Tom_Idle_001...)
  3. 3rd argument is the animation mode. This one is pretty self-explanatory. You can play the animation with various modes like NORMAL, LOOP, REVERSED...


Using the animations

To use the animations, I create a draw() function in the Hero.java. That draw() function will be called in the GameScreen.java, between a batch.begin() and a batch.draw().

Here is the draw() function of the Hero.java :

public void draw(SpriteBatch batch, float animTime){
        if(Gdx.input.isKeyPressed(Keys.W) && fuelLevel > 0){
            if(!fly){
                GameConstants.ANIM_TIME = 0;
                fly = true;
            }
            batch.draw(tomFly.getKeyFrame(animTime), 
                    heroBody.getPosition().x - bodyWidth, 
                    heroBody.getPosition().y + bodyHeight - spriteHeight, 
                    bodyWidth,
                    spriteHeight - bodyHeight,
                    spriteWidth, 
                    spriteHeight,
                    1,
                    1,
                    heroBody.getAngle()*MathUtils.radiansToDegrees);
        }
        else{
            if(fly){
                GameConstants.ANIM_TIME = 0;
                fly = false;        
            }
            batch.draw(tomIdle.getKeyFrame(animTime, true), 
                    heroBody.getPosition().x - bodyWidth, 
                    heroBody.getPosition().y + bodyHeight - spriteHeight, 
                    bodyWidth,
                    spriteHeight - bodyHeight,
                    spriteWidth, 
                    spriteHeight,
                    1,
                    1,
                    heroBody.getAngle()*MathUtils.radiansToDegrees);
        }
    }

About this code :

  • The draw function takes 2 arguments :
    • A SpriteBatch, to draw the animation, it's always the same SpriteBatch that use, the one I created in the MyGdxGame.java.
    • A float that I call ANIM_TIME. This float is used to know which frame of the animation to draw at a given time. For that, I need to add this float in the GameConstants.java, and update it in the render loop of the GameScreen.java with this line : GameConstants.ANIM_TIME += Gdx.graphics.getDeltaTime();
  • With the line if(Gdx.input.isKeyPressed(Keys.W) && fuelLevel > 0), I check if the jetpack is activated. If yes, I play the tomFly animation, else I play the tomIdle animation.
  • I also added a boolean called fly to the Hero.java. This boolean allows us to check if the jetpack is on or off.
  • The if(!fly) and if(fly) statement is very useful to check the transition between Idle and Fly animations : With these statements, we check if we begin of the 2 action that are "Fly" and "Stay Idle". If we begin a new action we MUST put the GameConstants.ANIM_TIME to zero. Thus we can take the animation from the beginning, it is to say, from the 1st frame.
  • Then, to draw my animations, I need to use the draw function that use 10 arguments. This function allows us to draw a sprite with a rotation. This is appropriated to my game, as Major Tom will rotate. Here are the 10 arguments :
    • The frame to draw
    • The X position of the frame
    • The Y position of the frame
    • The X position of the rotation center
    • The Y position of the rotation center
    • The width of the frame
    • The height of the frame
    • The X scale factor
    • The Y scale factor
    • The rotation angle


Dealing with the collision detection

You can see in my code, that for there are parameter called bodyWidth, bodyHeight, and spriteWidth, spriteHeight.

Why ?

I could have drawn the sprites at exactly the same size as the Box2D body, but I would have a weird collision detection :


As you can see on the picture above, as my character is moving, in some frames, his limbs cover a smaller portion of the Box2D body, but, the body would still detect collisions in the non covered area... So I made the choice to create a body that has the same height as the sprites, but is thiner. Therefore, there will be som frames (mainly in the idle animation) with part of the limbs going out of the detection zone, which is OK, as limbs are flexible, it's not really a problem if we don't take into account the collision with small parts of them.

As the frame is not the same size as the Box2D body, in the draw function, I had to do a bit of mathematics to calculate the X and Y positions of the frames rotation center, as they don't correspond anymore to the Box2D body rotation center.

And here is the result !