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 extends ApplicationAdapter {
     private OrthographicCamera camera;
     private SpriteBatch batch;
     private TextureAtlas atlas;
     private Sprite grassSprite;
 
     public void create() {
         float screenW = Gdx.graphics.getWidth();
         float screenH = Gdx.graphics.getHeight();
         float w = 1 ;
         float h = 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 );
     }
 
     public void 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 from this 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 the sprite.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 implements TweenAccessor<Sprite> {
     public static final int SKEW_X2X3 = 1 ;
 
     @Override
     public int getValues(Sprite target, int tweenType, float [] returnValues) {
         switch (tweenType) {
             case SKEW_X2X3:
                 float [] vs = target.getVertices();
                 returnValues[ 0 ] = vs[SpriteBatch.X2] - target.getX();
                 returnValues[ 1 ] = vs[SpriteBatch.X3] - target.getX() - target.getWidth();
                 return 2 ;
         }
 
         assert false ;
         return - 1 ;
     }
 
     @Override
     public void setValues(Sprite target, int tweenType, float [] newValues) {
         switch (tweenType) {
             case SKEW_X2X3:
                 float x2 = target.getX();
                 float x3 = 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 extends ApplicationAdapter {
     private TweenManager tweenManager = new TweenManager();
     ...
 
     public void create() {
         ...
         Tween.registerAccessor(Sprite. class , new SpriteAccessor());
         Tween.call(windCallback).start(tweenManager);
     }
 
     public void render() {
         tweenManager.update(Gdx.graphics.getDeltaTime());
         ...
     }
 
     private final TweenCallback windCallback = new TweenCallback() {
         @Override
         public void onEvent( int type, BaseTween<?> source) {
             float d = MathUtils.random() * 0 .5f + 0 .5f;   // duration
             float t = - 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 the windCallback 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 = new TweenCallback() {
     @Override
     public void onEvent( int type, BaseTween<?> source) {
         float d = MathUtils.random() * 0 .5f + 0 .5f;    // duration
         float t = - 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 three Tween.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 images here.

10 Comments + Add Comment

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

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

  • 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!

    • 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.

  • thanks it’s work

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

  • Awesome tutorial, please, continue do it!!

  • Awesome tutorial, thank you very much

  • awesome tutorial .. More more . Thank You

  • 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…

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>