TL;DR

Before (animated gif)

After (animated gif)

GitHub Project

## Soccer Games

When building a 2D soccer game, a common problem is figuring out how to animate a rolling soccer ball. Soccer balls have 360 degrees of movement, so the conventional approach of animated sprite sheets does not look particularly realistic.## Sprite Sheets

A google search of "soccer ball sprite sheet" is littered with incomplete solutions.Often, game developers just don't animate the soccer ball at all:

Other times, the soccer ball animation is dramatically simplified:

Notice that the ball only rotates in 2 directions: clockwise or counter-clockwise.

## 3D Soccer Ball

A 3D sphere made in Unity animates perfectly, because its RigidBody allows it to automatically roll around in 3D space.

Unfortunately, using 3D physics for the ball means that your entire Unity game must use 3D physics. There are several reasons you may not want a 2D-looking game to have 3D game physics:

- Most collisions of the soccer ball can send it flying on the Z-axis. The ball could look like it is in the right place for a soccer player to hit the ball, but it could completely miss because the ball is 20 units above the player's head.
- A dogpile of soccer players can stack in the Z-direction, messing up the look and feel of the game.
- 3D physics feels very different from 2D physics
- 3D physics is just more complicated

Assuming you are dead-set on 2D physics, rotation of the 3D ball is no longer automatic. Lame. Let's use Unity scripting to manually rotate the ball as it moves in a 2D physics environment.

## BallRoll Unity Project

The Unity 5.1 project

**BallRoll**rotates a 3D ball using 2D physics. I have put source code on GitHub:
The main scene has a PlayerBall controlled by the arrow keys. There are also 3 PropBalls for you to bash into.

Note that the PlayerBall has a

**CircleCollider2D**and a**RigidBody2D**, and that the RigidBody2D is**constrained on the Z Rotation**.
PropBalls are the same as the player, with a different color, and minus the

**BallInput**script.## Scripting Magic

First, we have a script that moves the ball with the arrow keys:

Next, we have a script that rotates the ball as it moves. The critical part is the

**rotateBall()**method that utilizes**Quaternions**:## Quaternions

Unity uses Quaternions in all GameObjects to store their rotation information in 3D space. Specifically,

**GameObject.transform.rotation**.
There are many benefits to using Quaternions for 3D rotation, but everything that makes them powerful also makes them difficult to understand. I like to imagine a Quaternion as a hand crank:

Now stab the soccer ball straight through its center with the crank pole. That is the

**axis of rotation**. Now turn the crank a little. That is the**angle theta**that the ball is rotated. A Quaternion literally stores these variables as the direction Vector [**x**,**y**,**z**], and angle**w**.
For the curious, I read through this article while implementing the ball rotation algorithm. It made for good bedtime reading.

Quaternions are pretty easy to handle in Unity. Let's figure out how they tick.

## The rotateBall() Method

The

**rotateBall()**method uses Vector Math and Geometry to build a rotation Quaternion. The Quaternion is then applied to the ball's**GameObject.transform.rotation**to rotate it.
We begin with

**transform.position**,**lastPosition**, and the ball's**radius**. First, we determine**currentToLast**and**segment**.// distance the ball traveled between the last Update and this one // we subtract the vectors in this order to use it as a direction vector later Vector3 currentToLast = lastPosition - transform.position;

Vector Subtraction tells us that "lastPosition - transform.position" gives us a direction Vector

**currentToLast**that points from

**transform.position**to

**lastPosition**.

// segment length that the ball rolled along its surface float segment = currentToLast.magnitude; if (segment == 0) { // no distance travelled, nothing to do return; }

The Vector's magnitude tells us the distance the ball traveled, which we save as

With

We use the Cross Product to determine the

**segment**.**currentToLast**and**segment**calculated, we can determine the**axis of rotation**, and angle**theta**.### Axis of Rotation

We use the Cross Product to determine the

**axis of rotation**. Given two Vector3s, the**Cross Product**returns a Vector3 that is perpendicular to both of them.**currentToLast**, the second is

**ballDown**, which points directly at the ground.

// define the down direction vector for the ball // the ball rolls in the x and y directions, // and positive z points to the ground // Important: this is DIFFERENT from Vector3.down because Vector3 assumes // a 3D world space where negative y is down Vector3 ballDown = new Vector3(0, 0, 1); // use Cross Product to find the axis of rotation // https://www.mathsisfun.com/algebra/vectors-cross-product.html Vector3 axis = Vector3.Cross(ballDown, currentToLast); // Cross Product will fail if both vectors are parallel or perpendicular if (axis == Vector3.zero) { // this should never happen because currentToLast.z is always 0 // but who knows where this code will be copy-pasted to... return; }

Now that we know where to rotate the ball, let's see how far we need to rotate it.

### Angle Theta

The ball traveled

**segment**(aka s) distance in between Updates, which means that the ball must roll exactly

**segment**distance along the its surface. We can figure out

**theta**(aka θ) using the Arc Length Formula:

// arc length formula // s = r * theta // theta = s / r // http://www.mathopenref.com/arclength.html float theta = segment / radius; // in radians float thetaDegrees = theta * 180 / Mathf.PI;

With

**theta**, we know exactly how many degrees to rotate the ball.

### Putting It All Together

Now that we know

**theta**and

**axis**, we politely squeeze them into a Quaternion:

Quaternion q = Quaternion.AngleAxis(thetaDegrees, axis);

With our rotation Quaternion defined, we multiply it into the ball's

**transform.rotation**

transform.rotation = q * transform.rotation;

Multiplication order is

**crucial**. Quaternions, like Matrices, have Non-Commutative multiplication. Switch them up, and you'll get some weird results.

## Finally

Let's see what the end result looks like:

BAM. DONE. YOU'RE WELCOME.

Thanks to OpenGameArt for:

## No comments:

Post a Comment