Tutorial: Animated grass using LibGDX

Jun 15, 2012 by Aurelien Ribon   10 Comments    Posted under:Tutorials

A little and easy tutorial today, but which can add a nice value to your games: we’re going to animate some grass to enhance the floors. Indeed, even if you really suck at graphics as I do, you can fake the user into thining it’s still awesome by animating everything! If it doesn’t look pretty, make it move!

Of course, I don’t claim that’s the best way to implement animated grass, but I found the effect quite cool and easy to achieve, so why not share it? :)

1. Initialization

We first need to draw some static grass on the screen, that’s our starting point. Two choices: (1) use one sprite per blade, or (2) combine multiple blades in a long sprite. Since grass blades only look nice when they overlap a lot with each other, solution 1 would be inefficient and would quickly lead to very bad performances in big scenes. Therefore, I randomly combined four drawings of blades in a single sprite, as follows:

Now, we need to initialize our game and draw this sprite on the screen. The code I used was this one:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class AnimatedGrassApp extendsApplicationAdapter {
    privateOrthographicCamera camera;
    privateSpriteBatch batch;
    privateTextureAtlas atlas;
    privateSprite grassSprite;
 
    publicvoid create() {
        floatscreenW = Gdx.graphics.getWidth();
        floatscreenH = Gdx.graphics.getHeight();
        floatw = 1;
        floath = w * screenH / screenW;
 
        camera =new OrthographicCamera(w, h);
        batch =new SpriteBatch();
 
        atlas =new TextureAtlas("data/grass.pack");
 
        grassSprite = atlas.createSprite("grass");
        grassSprite.setSize(w, w * grassSprite.getHeight() / grassSprite.getWidth());
        grassSprite.setPosition(-w/2, -h/2);
    }
 
    publicvoid render() {
        Gdx.gl.glClearColor(1,1, 1,1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
 
        batch.setProjectionMatrix(camera.combined);
        batch.begin();
        grassSprite.draw(batch);
        batch.end();
    }
}

I’m basically creating a sprite that is as wide as the camera viewport, and I position it at the bottom of the viewport. Always work with a viewport that has a fixed width (here: 1 unit), and never work with pixel units. This way, your application will resize itself correctly to every possible resolution, and is not bound to a specific resolution. That’s the most important thing to remember fromthis previous article! This simple code sample, once launched, creates the following scene:

What a pure recipe for awesomeness, no? Maybe not. This doesn’t move, so let’s add some wind in those blades!

2. Animating the grass

We want to make our blades move like if wind was pushing them to the left of the screen. If we used one sprite per blade, a rotation of each sprite would fake the effect quite nicely. However, we’re using a single sprite for all the blades, so we can’t rotate it (just try it and you’ll see why). What we want instead is to perform a “skew transformation”. This is a well-known rectangle transformation for those who ever used vector drawings (see following illustration).

To apply this effect, we need to modify the horizontal position of the vertices 2 and 3 of our sprite. Hopefully, LibGDX lets us access the vertices of the sprites with thesprite.getVertices() method! Yay! Only thing we need to do is to animate these vertices in a wave motion. That’s quite easy to do, but we also need to add some randomness to the effect, both in its duration and in its amplitude. And we also want to keep our code quite clean and small. Why not eat my own dog food? The Universal Tween Engine can do all that!

If you ever used the engine with LibGDX, you should already have a TweenAccessor for the Sprite class. This class is responsible for telling the engine how to mess with our objects. What we want to do is just to tell the engine how to apply a “skew transformation”, so we won’t have to dirty our hands with it: the engine will do it for us. Here is how I added support for such transformation to my SpriteAccessor class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class SpriteAccessor implementsTweenAccessor<Sprite> {
    publicstatic final int SKEW_X2X3 = 1;
 
    @Override
    publicint getValues(Sprite target,int tweenType, float[] returnValues) {
        switch(tweenType) {
            caseSKEW_X2X3:
                float[] vs = target.getVertices();
                returnValues[0] = vs[SpriteBatch.X2] - target.getX();
                returnValues[1] = vs[SpriteBatch.X3] - target.getX() - target.getWidth();
                return2;
        }
 
        assertfalse;
        return-1;
    }
 
    @Override
    publicvoid setValues(Sprite target,int tweenType, float[] newValues) {
        switch(tweenType) {
            caseSKEW_X2X3:
                floatx2 = target.getX();
                floatx3 = x2 + target.getWidth();
                float[] vs = target.getVertices();
                vs[SpriteBatch.X2] = x2 + newValues[0];
                vs[SpriteBatch.X3] = x3 + newValues[1];
                break;
        }
    }
}

Now, only thing left to do is to apply the effect in a wave motion, and repeat indefinitely:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class AnimatedGrassApp extendsApplicationAdapter {
    privateTweenManager tweenManager = newTweenManager();
    ...
 
    publicvoid create() {
        ...
        Tween.registerAccessor(Sprite.class,new SpriteAccessor());
        Tween.call(windCallback).start(tweenManager);
    }
 
    publicvoid render() {
        tweenManager.update(Gdx.graphics.getDeltaTime());
        ...
    }
 
    privatefinal TweenCallback windCallback =new TweenCallback() {
        @Override
        publicvoid onEvent(inttype, BaseTween<?> source) {
            floatd = MathUtils.random() * 0.5f +0.5f;   // duration
            floatt = -0.5f * grassSprite.getHeight();   // amplitude
 
            Tween.to(grassSprite, SpriteAccessor.SKEW_X2X3, d)
                .target(t, t)
                .ease(Sine.INOUT)
                .repeatYoyo(1,0)
                .setCallback(windCallback)
                .start(tweenManager);
        }
    };
}

The windCallback instance fires an animation for one wave motion of the grass. This animation calls this windCallback instance again to relaunch the animation with new random parameters for the duration or the amplitude. This way, the animation loops infinitely, but each time with new random parameters. That’s what we wanted! Result is:

It’s a lot better than the static sprite version, but we can improve it way more!

3. Polishing the effect

This issue with the previous effect is that all blades move at the same speed, that doesn’t feel very natural. To solve this issue, only one way:we need to divide our grass sprite into multiple layers. Therefore, I divided our grass image into three chuncks:

Instead of creating one sprite in the game, I made three of them, each one associated to one of these three grass images. The code is very similar to what we already have. However, what needs to change is thewindCallback instance. Indeed, I want the three sprites to move like what we did previously, but with a little time offset between each one. Therefore, I went to use a timeline instead of a simple tween this time:

1
2
3
4
5
6
7
8
9
10
11
12
13
private final TweenCallback windCallback = newTweenCallback() {
    @Override
    publicvoid onEvent(inttype, BaseTween<?> source) {
        floatd = MathUtils.random() * 0.5f +0.5f;    // duration
        floatt = -0.5f * grassSprite1.getHeight();   // amplitude
 
        Timeline.createParallel()
            .push(Tween.to(grassSprite1, SpriteAccessor.SKEW_X2X3, d).target(t, t).ease(Sine.INOUT).repeatYoyo(1,0).setCallback(windCallback))
            .push(Tween.to(grassSprite2, SpriteAccessor.SKEW_X2X3, d).target(t, t).ease(Sine.INOUT).delay(d/3).repeatYoyo(1,0))
            .push(Tween.to(grassSprite3, SpriteAccessor.SKEW_X2X3, d).target(t, t).ease(Sine.INOUT).delay(d/3*2).repeatYoyo(1,0))
            .start(tweenManager);
    }
};

Basically, the timeline starts three animations at the same time, with a little delay between each one. It is the exact same thing as if I wrote threeTween.to() animations individually, but is a bit more compact to write (and I’m the lazy type :p). The result is:

Now that’s a lot better! And just imagine how it looks with an animated background and particle effects everywhere!

Conclusion

Of course, there may be many different ways to achieve this effect, but I found this one really easy to implement! Anyway, the only thing to remember is:“if you can’t draw it nicely, make it move!” Especially when it doesn’t involve a lot of work. That’s the kind of thing that can make the difference when people will discover your game.

You can download the eclipse project containing all the sources and imageshere.

10 Comments + Add Comment

  • Nice. You should definitely do more of these small tutorials.

    Reply
  • Sweeeet. Looks really cool. Could do the same effect on the background as well, ex tree, bird or a twinkling star. Good tutorial

    Reply
  • Hi, great article and software. I’m new to LibGDX and am currently programming my first game. I will be implementing the Tween engine as soon as I’m confident with it.

    I have one question on this article. In the last piece of code (where you implement parallel tweening with all three grass sprites), is there a reason sprite 1 requires a callback to windCallBack and yet the others don’t?

    Thank you and keep up the good work!

    Reply
    • There is a reason, yes :)
      Try adding a callback to each line, then try removing the callback entirely, and you’ll see by yourself.

      Actually, the three tweens (for the three layers) are played in parallel, but with a small delay between each one. Once the first tween completes, we want to restart it immediately, so we call the windCallback. However, this callback will create three new tweens, one for each layer, so there is no need to call it again for the two remaining tweens, else we’ll have many tweens per layer (we only want one tween per layer).

      It’s a bit hard to explain, but by playing with the engine, you’ll understand more easily.

      Reply
  • thanks it’s work

    Reply
  • thank you very much. It’s helpful :)

    Reply
  • Awesome tutorial, please, continue do it!!

    Reply
  • Awesome tutorial, thank you very much

    Reply
  • awesome tutorial .. More more . Thank You

    Reply
  • Thanks a lot for yr work and tuts, but i can’t understand by no way what does it mean
    float w = 1;
    float h = w * screenH / screenW;
    camera = new OrthographicCamera(w, h);
    ??? u mean that viewport is 1px wide and 0.2px high…? i have read the docs, but anyway can’t get how libgdx works this time, i feel very stupid…

    Reply

Got anything to say? Go ahead and leave a comment!

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>