Learning and Applying Mathematics using Computing

This post is about adding friction, or: rotating, splitting and manipulating vectors.

We recently changed our spaceship into a bus when developing our scenario, but it still handles more like a spaceship than a bus. The reason is friction. Our bus still uses the asteroids-style handling from the spaceship, where it slides around without ever slowing down and where you can spin round with no effect on your current speed. Let’s change that.

Different Friction

If we wanted to simply slow down over time, we could take the braking code that we’ve seen in a previous post, and simply make the bus be always braking a little bit: this is what friction effectively does.

However, I want to go one step better than that. When dealing with cars, buses, etc, there is a markedly different effect on forwards speed to sideways speed. Ignoring the possibility of rolling over, if you shoot a car out of a cannon along the ground at 20mph going forwards, it will take a long time to come to a complete halt (because the wheels will turn, with little friction). But if you do the same with the car going sideways, it will stop much faster (because the wheels won’t turn, so there’s lots of friction). And so it should be in our little simulation: sideways speed should reduce much faster than forwards speed.

Rotating Motion

So the first thing to do is to separate our motion into forwards and sideways speed. This is a case where there are several mathematical techniques available to produce the same effect: dot product, matrix rotation, etc. We will choose an approach that re-uses some of our previous code. What we’re going to do is take our motion vector, and rotate it counter-clockwise by our current direction, so that the forwards speed will lie along the X-axis and the sideways speed on the Y-axis. Here’s a diagram to help clarify:

On the left is a bus that is heading at 34 degrees. Its current motion is the strong black arrow (so the bus is drifting slightly to its left, rather than heading straight). If we take the motion and rotate it in the opposite direction by 34 degrees, the X component of the motion is the “straight ahead” part (the horizontal dashed arrow), and the Y component is the “sideways” part (the vertical dashed arrow) — effective splitting the vector.

Here’s the corresponding code to do this rotation:

        double dir = calculateDirection(speedX, speedY);
        double speed = calculateMagnitude(speedX, speedY);
        
        // ... not showing braking and skidding code from previous posts
        
        double speedForwards = calculateX(dir - getRotation(), speed);
        double speedSideways = calculateY(dir - getRotation(), speed);

Once we have our forwards and sideways speeds, we can add friction (which we will make different for forwards than sideways) by slowing them down by different factors using our proportional braking technique:

        speedSideways = speedSideways * 0.96;
        speedForwards = speedForwards * 0.995;

        if (Math.abs(speedSideways) > 1)
        {
            layDownRubber();
        }

For an extra flourish, I’ve added some code above to produce tyre marks when our sideways speed is high enough, so that we skid when we turn sharply.

Now that we’ve altered our forwards and sideways speeds, we need to turn them back into X and Y speeds, by using our old methods:

        speedX = calculateX(getRotation(), speedForwards)
               + calculateX(getRotation() + 90, speedSideways);
        speedY = calculateY(getRotation(), speedForwards)
               + calculateY(getRotation() + 90, speedSideways);

To explain what this code is doing, we’ll look at a reverse version of our previous diagram:

We have the forwards and sideways components of our motion (the horizontal and vertical dashed arrows, respectively, on the left-hand side). We need to rotate both of these and add them together to get the rotated motion (the solid arrow on the right-hand side). Our calculateX function can already work out the X-component of a rotation and speed, so we pass it our rotation and our forwards speed (the first line above). But we need to add on to that the X component of our sideways speed, which is at 90 degrees to our heading (the second line above). The calculation for the Y coordinate is nearly identical (third and fourth lines), but uses calculateY instead of calculateX.

Off The Beaten Track

As a final touch for this post, we can change the scenario so that we have a track to race the bus on, and the bus slows down more on grass than on the tarmac. Here’s a slightly hacky function to detect if we’re on the track (the grey part of the background image):

    private boolean onGrass()
    {
        Color c = getWorld().getBackground().getColorAt(getX(), getY());
        if (c.getRed() == c.getGreen() && c.getRed() == c.getBlue())
            return false; // Grey/white
        else
            return true; // Not grey/white
    }

We can use that to decide how much to slow down our speed. Adjusting our earlier code:

if (onGrass())
{
    speedSideways = speedSideways * 0.8;
    speedForwards = speedForwards * 0.9;
}
else
{
    speedSideways = speedSideways * 0.96;
    speedForwards = speedForwards * 0.995;
}

This code counts you as on the track if the centre of the bus is on the track, and off the track if the centre of the bus is on the grass. That’s not ideal: really, it’s whether your wheels are on or off the track that matters. You’ve now seen enough, between the skidding code in the last post, and the rotation code above, to modify the scenario to fix that and slow down depending on how many wheels are on the grass — so that’s your homework! You can find the scenario in its current state on the Greenfoot site to have a play, or download it into Greenfoot and get fixing the wheels issue.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s