Learning and Applying Mathematics using Computing

Archive for the ‘Mechanics’ Category

Driving – Like A Bus

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.

The Bus That Couldn’t Slow Down

This post is about two different ways of implementing braking.

In a recent post, we saw how to implement a drifting, asteroids-style of movement (and how to limit the maximum speed). As is traditional in asteroids, there was no braking: you just accelerate in the opposite direction to slow down. In this post, we’ll look at how two different ways to implement braking (and to fit with the title, our spaceship will become a bus).

Fixed Braking

One way to implement braking is to subtract a fixed amount from the current speed. (In effect, this performs an acceleration in the opposite direction to the one in which you are currently travelling — not pointing). We saw in our last post how to convert from cartesian to polar speeds, where speed becomes a single number. We can augment the code from our last post to add braking:

    private void clampSpeedAndBrake()
    {
        double dir = calculateDirection(speedX, speedY);
        double speed = calculateMagnitude(speedX, speedY);
        
        final double MAX_SPEED = 10;
        
        if (speed > MAX_SPEED)
        {
            speed = MAX_SPEED;
        }
        
        if (Greenfoot.isKeyDown("down"))
        {
            speed = speed - 0.1;
        }
        
        speedX = calculateX(dir, speed);
        speedY = calculateY(dir, speed);
    }

The braking is the second if-statement: if they’re pressing the down arrow, reduce the speed.

Problem: Pedalling Backwards To Brake

In the code above, braking works fine. But if you increase the strength of the braking, you’ll find a very strange effect. If that speed reduction is set to, say, 1.5, you’ll find that holding the brake key when stopped will jiggle you around. Consider what the above code does if your speed is zero: it reduces the speed to a negative number, but once that gets converted back to cartesian, it means that you are no longer stationary. This problem is quite easy to fix, by using the maximum function to make sure that speed is either reduced or zero, whichever is larger (you might need to think about that for a minute):

        if (Greenfoot.isKeyDown("down"))
        {
            speed = Math.max(0, speed - 0.1);
        }

Proportional Braking

The alternative to what I’ve called fixed braking (which reduces your speed by a fixed amount each frame) is proportional braking. The idea behind proportional braking is that instead of subtracting a fixed amount from your speed, you instead take off a proportion. So let’s say that each frame you brake should slow you by 2%: you just multiply the speed by 0.98, making the braking code:

        if (Greenfoot.isKeyDown("down"))
        {
            speed = speed * 0.98;
        }

Problem: Coming To A Complete Stop

One problem with proportional braking is that it gets a bit irritating as you come close to stopping. There is a thought experiment (not to be confused with Zeno’s paradox) that runs: “if every second you halve the distance between you and your destination, will you ever get there?”. And so it is in proportional braking: if your speed always reduces by 2%, will it ever actually hit zero — will you ever actually stop? In mathematics, the answer is: no (\forall n. 0.98^n > 0). In computing the answer is: eventually (because eventually the number will become so small it will be represented by zero).

Practically speaking (ignoring all this theory fluff!), the best thing to do is add a special case that once your speed is close enough to zero, just set it to zero. Not elegant, but effective:

        if (Greenfoot.isKeyDown("down"))
        {
            speed = speed * 0.98;
        }
        if (speed <= 0.05)
        {
            speed = 0;
        }

You can have a play with the two different styles of braking in this scenario. The bus with an “F” on it is controlled by WASD and does fixed braking, while the bus with a “P” on it is controlled by the arrow keys and does proportional braking. There’s no single right answer to which is a better system, but having played around, I prefer the fixed braking — the proportional braking takes a long time to come to a complete halt.

Where Am I Headed?

This post is about calculating your direction and overall speed from your X and Y speeds and restricting maximum speed, or: cartesian and polar coordinates.

In our last post, we saw how to implement drifting. With drifting, your direction is independent from your ship’s current rotation: once you’re moving, you can let go of the up key and just hold left or right to spin round without affecting your trajectory. However, it can be useful to know the current speed (as a single number) and the current direction, rather than separate X and Y speeds.

Calculating Direction

So, we have an X speed and a Y speed, and we want to calculate the direction (angle) in which we are heading. This sounds familiar — we’ve already seen how to calculate an angle from an X and Y distance, and this is exactly the same principle:

    private static double calculateDirection(double x, double y)
    {
        return Math.toDegrees(Math.atan2(y, x));
    }

Calculating Speed

As well as direction, we also want to calculate the speed as a single number, rather than separate X and Y speeds. The X and Y speed can inevitably form a right-angled triangle, like so:

So we know two sides and need to know the third: this is a case for Pythagoras. The square of the hypotenuse (diagonal line) is equal to the sum of the squares of the other two sides, so we just have to square-root the sum of the squares of the other sides:

    private static double calculateMagnitude(double x, double y)
    {
        return Math.sqrt(x * x + y * y);
    }

(In fact, Java provides this exact function as Math.hypot, but I wanted to show here how it works.)

There… And Back Again

So we’ve seen how to turn X and Y speeds into distance and a single-speed. It’s also possible to do the reverse: turn distance and a single-speed into X and Y speeds. And in fact, we’ve already covered that one, too. So the code is:

    private static double calculateX(double dir, double mag)
    {
        return Math.cos(Math.toRadians(dir)) * mag;
    }
    
    private static double calculateY(double dir, double mag)
    {
        return Math.sin(Math.toRadians(dir)) * mag;
    }

I’m using the more general term magnitude rather than speed here, because actually this function can be used for speeds or positions or any “vector”.

Cartesian and Polar

We’ve seen two different ways of representing our ship’s heading: either as an X and Y velocity, or as a direction and speed. The former representation is known as Cartesian, the latter is known as Polar. We’ve seen that you can convert between the two representations easily. In general, Cartesian is often easier to work with, e.g. adding two Cartesian vectors is easy, whereas adding two Polar vectors requires converting to Cartesian anyway. However, Polar can also be useful, such as implementing a maximum speed, and a few other uses in future posts.

Maximum Speed

In our last post, we implemented drifting, but our spaceship could keep on accelerating forever. It’s better for a game if you have a maximum speed, so you can add a function like this to set a maximum speed:

    private void clampSpeed()
    {
        double dir = calculateDirection(speedX, speedY);
        double speed = calculateMagnitude(speedX, speedY);
        
        final double MAX_SPEED = 10;
        
        if (speed > MAX_SPEED)
        {
            speed = MAX_SPEED;
        }
        
        speedX = calculateX(dir, speed);
        speedY = calculateY(dir, speed);
    }

We first convert to polar, then “clamp” the speed to a maximum of 10, then convert back to cartesian using the same direction and the (potentially altered) speed.

Catch My Drift

This post is about implementing drifting movement (a la asteroids), and draws together several previous posts.

There are various simple movement schemes that you can implement in games. We’ve used one in our previous posts: moving at a fixed speed (with steering). Another is what I will call asteroids-style movement: you have a persistent velocity (or “drift”), and you can apply thrust in a particular direction to change the velocity. It’s hard to describe it simply in text, so the best way to get an idea of what I’m talking about is to go have a play with the scenario.

In this post, we will implement drifting. To do this, we must create a variable to hold the current speed in the X direction, and the current speed in the Y direction. Each frame we add the current speeds onto the current position.

The key aspect of implementing drift-style movement is that when we press a key to move, instead of adding some amount to the current position, we add some amount (known as acceleration) to the current speed, which in turn will be added to the current position in each of the future frames. We’ve already seen this technique in the previous post on falling, so we can form a rough skeleton of our code:

    private double speedX;
    private double speedY;

    public void act() 
    {
        doTurn();

        doMove();
    }

    private void doMove()
    {
        if (Greenfoot.isKeyDown("up"))
        {
            setImage("rocket.png");
            
            speedX = speedX + ???;
            speedY = speedY + ???;
        }
        else
        {
            setImage("rocket_nothrust.png");
        }

        setLocation(getX() + (int)speedX, getY() + (int)speedY);
    }

(We’ll additionally use the doTurn method from our turning example, and also the world-wrapping code from our world-wrapping example.)

So we have persistent variables for speedX and speedY, declared as members of our Spaceship class. We add these speeds to our position each frame, on the last line of the code above. We also have some code to change the image of the rocket when we are accelerating/drifting. The bit that still needs filling in is how to adjust the speed each frame.

We want to add an appropriate acceleration to our speed, based on which direction we’re facing. We want to accelerate a certain amount in the direction we are pointing. This is exactly the same principle as an earlier post, where we wanted to move a certain amount in the direction we are pointing. The only difference is that instead of moving, we add to our speed:

            double radians = Math.toRadians(getRotation());
            double moveSpeed = 0.1;

            double accX = Math.cos(radians) * moveSpeed;
            double accY = Math.sin(radians) * moveSpeed;

            speedX = speedX + accX;
            speedY = speedY + accY;

You can have a play with the finished scenario, or view its full source code in Greenfoot. You may notice that its speed can increase to unlimited amounts — we will fix that in a forthcoming post.

What Goes Up Must Come Down

This post is about making things jump up and fall down (e.g. for platform games), or: modelling acceleration.

When building 2D games and simulations in environments like Scratch or Greenfoot, you can either use the 2D view to provide a top-down view on the world, or a side-on view. Platform games (Sonic, Mario, etc) are the main examples of the latter, and in these games, you often want to make things fall down when there is no solid ground beneath them, and to allow the character to jump around. This post will look at how to do this: it’s actually best to start by implementing falling first, and then implement jumping.

Falling Down

One way to make things fall is to move them down the screen at a constant rate. However, this often doesn’t look quite right — because it’s not the way that the real world works! Gravity produces an acceleration downwards, so when things fall down, they increase speed as they fall. In order to model the effect of acceleration, you must keep track of the current speed in a variable. Here’s the code in Greenfoot:

public class AccFall extends Actor
{
    double speed = 0;
    double exactY;

    public void act() 
    {
        speed += 0.4;
        exactY += speed;
        setLocation(getX(), (int)exactY);
    }    

    protected void addedToWorld(World world)
    {
        exactY = getY();
    }
}

This code is a bit more fiddly than it needs to be, due to Greenfoot using integers to store coordinates, and me using an acceleration to fall at that’s not a whole number (otherwise it’s a bit too fast). To visualise the difference between falling at a constant rate, and falling at an accelerating rate, I’ve coded both in the same scenario. You can watch by clicking the apples below — the green apple on the left-hand side falls at a constant rate, while the red apple on the right-hand side falls at an accelerating rate. Try covering up one side with your hand and just watch the other side, to try to see the difference:

Jumping Up

The other half of implementing a platform game is allowing the character to jump. It turns out that this is quite simple to model: you can make the character jump up by setting the speed variable to be a large negative value (in effect, an instantaneous upwards acceleration). This causes the character to move upwards, and thereafter gravity takes over by reducing the speed (downwards acceleration is equivalent to a slowing of upwards speed) until the speed takes the character downwards again, just like the red apple was falling.

You may not have followed all that in text, so here’s how you can see it in action. I made a scenario with a frog on a log that can jump (press up to jump). If you watch carefully, you can see that the frog slows until it hits the peak of its jump and then accelerates as it comes back down. Greenfoot has a useful “Inspect” feature that allows us to see exactly what is happening.

If you want to play along yourself, open the scenario in Greenfoot. Hit compile in the bottom-right, and make sure you can see the frog on the log. Right-click on the Frog and go to “Inspect”. Move the inspect window so you can see it alongside the main Greenfoot window:

The two variables of interest are “int y” (currently 222) and “private int verticalSpeed” (currently 0). Right-click on the frog and select the method “void jump()” to call it. You’ll notice the frog will jump up (by 30: y becomes 192, and verticalSpeed becomes -30). That’s the instant upwards acceleration. Press the Act button once. You’ll see that verticalSpeed becomes -27, and y becomes 165 (which is 192 + (-27), old position plus vertical speed). Press it again, and you get -24 and 141 and so on. Press Act a few more times in quick succession and you’ll be able to see (looking at the frog) that the speed of the frog’s rise slows until it hits the peak of its jump, when verticalSpeed is zero. After that, verticalSpeed increases and the frog falls faster and faster downwards (keep clicking Act) until it hits the log again, when the speed is set back to zero.

So the whole time, the verticalSpeed increases at a rate of +3. On the way up this causes the negative speed to get closer to zero, slowing the frog, but on the way down it increases the positive speed, causing the frog to fall faster and faster. This can seem a bit magical, but this is simply the effect of a constant acceleration from gravity, causing the arc of the frog’s jump. If you don’t quite understand, I encourage you to play more with the scenario to get a feel for it.

Follow

Get every new post delivered to your Inbox.

Join 246 other followers