Tutorial: Animated grass using LibGDX

Jun 15, 2012 by    
   
Posted under:

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 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 can do all that!

If you ever used the engine with LibGDX, you should already have a 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 .

10 Comments

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