Cartoon Squash and Stretch Animation Using iOS and Cocos2d

By Mike Lyman 

We've been working on a new bomb dodging game and felt that something was missing. Our character follows your finger as he moves around the screen. We decided to look at traditional animation principles to make our game more organic and dynamic, specifically squash and stretch techniques from the 12 basic principles of animation (The Illusion of Life: Disney Animation). Disney animators have been employing this effect since the early days of cartoons to add life to their animations.

Checkout G. Scott Owen's article on animation for computer graphics for more background information.

 

The ball on the left has no squash and stretch  while the right ball does. Which looks more natural? G. Scott Owen - HyperGraph

The ball on the left has no squash and stretch  while the right ball does. Which looks more natural?

G. Scott Owen - HyperGraph

Notice how the ball on the right doesn’t stay perfectly spherical? It stretches in the direction of it's motion and squashes in the perpendicular direction. That creates a sense of weight and really sells the movement. 

 

Implementing Squash and Stretch in iOS using Cocos2d

If you are fortunate enough to have an experienced animator working with you then you might not need to worry about squash and stretch animations yourself. You could build a system that dynamically switches between pre-rendered animations. Or, like us, you could programmatically deform your existing character sprites using 2d transforms.

We can achieve the squash and stretch animation effect easily in Cocos2d using both image scale along an axis and image skew. 

Scaling

In Cocos2d, CCSprite objects have the scaleX and scaleY properties that will stretch and squash the sprite. Typically most apps will scale with a 1:1 to ratio to prevent deforming, but we'll use something like 2:1 to deform the shape of the character.

 

From left to right:   (scaleX = 1,  scaleY = 1),  (scaleX = 2,   scaleY = 1),  (scaleX = 1,  scaleY = 2)   

From left to right:   (scaleX = 1,  scaleY = 1),  (scaleX = 2,   scaleY = 1),  (scaleX = 1,  scaleY = 2) 

 

Squash/Stretch Proportional to Velocity

We use a UIPanGestureRecognizer to move the character around the screen using the translationInView: method. We can also use the UIPanGestureRecognizer to get the velocity of a person's finger dragging across the screen. We'll use the velocityInView: method from as a way to control the scale of our character.

Effects of horizontal velocity on the sprite: as it moves faster it is squashed down more. On the left side, velocity.x == 0, on the right velocity.x == 5000 (values returned from UIPanGestureRecognizer) 

Effects of horizontal velocity on the sprite: as it moves faster it is squashed down more. On the left side, velocity.x == 0, on the right velocity.x == 5000 (values returned from UIPanGestureRecognizer) 

Conservation of Volume

When scaling your sprite you should treat it like a real world object: if you squash it in one direction, the volume needs to be displaced equally perpendicular to the squash direction. In the simple case of horizontal motion, the scale of the x axis would be inversely proportional to the scale of the y axis. If we don't do this, your sprite will appear to grow and gain volume. We want the character to compress like a bouncy ball in a cartoon.

 

Bad Squash The scaled image has scaleX = 2 and scaleY = 1. It appears to have actually grown from the original.

Bad Squash

The scaled image has scaleX = 2 and scaleY = 1. It appears to have actually grown from the original.

Good Squash Here the scaled image has scaleX = 2 and scaleY = 0.5. Now it appears to be squashed since it is conserving volume. 

Good Squash

Here the scaled image has scaleX = 2 and scaleY = 0.5. Now it appears to be squashed since it is conserving volume. 

Skewing

Scaling the sprite works fine for movements along the horizontal and vertical directions, but it doesn't work on the diagonals. We'll need to do a skew transformation to make this work in all directions.

Cocos2d CCSprites have the skewX and skewY properties. We just need to use skewX on the sprite when it moves in the diagonal direction, and we can ignore skewY.  

Left to right: skewX = -45; skewX = 0; skewX = 45

Left to right: skewX = -45; skewX = 0; skewX = 45

Combining Scaling and Skewing

Combining scaling and skewing allows us to draw the stretch and squash in any direction. We need this because motion in our game follows the player's finger as it moves. A player can move left, right, up, down, and at varying angles. 

 

In the center, the sprite is not moving. Each arrow indicates the direction of motion that would result in the corresponding scale or skew.

In the center, the sprite is not moving. Each arrow indicates the direction of motion that would result in the corresponding scale or skew.

We've created a code snippet that you can use to achieve the same kind of effect in your game.

Download project from GitHub

Further Reading and Videos

  1. Visual explanation of squash and stretch animation
  2. Tutorial of hand-drawn squash and stretch
  3. SIGGRAPH Animation Reference 

Questions?

  • Email Mike at: MikeMakesApps@gmail.com
  • Watch the use of squash and stretch in Artwork Evolution's upcoming game, Bomb Dodge. 

 

Posted on August 5, 2013 and filed under game.