5
\$\begingroup\$

I'm currently coding a 2D top-down car game which will be turn-based. And since it's turn-based, the cars won't be controlled directly (i.e. with a simple velocity vector that adjusts its angle when the player wants to turn), but instead it's movement path has to be planned beforehand, and then the car needs to follow the path when the turn ends (think Steambirds).

This question has some interesting information, but its focus is on homing-missile behaviour, which I kinda had figured out, but doesn't really apply to my case, I think, since I need to show a preview of the path when the player is planning his turn, then have the car follow that path. In that same question, there's an excellent answer by Andrew Russel which mentions Equations of Motion and Bézier's Curve. Some of his other suggestions of implementation are specific to XNA though, so they don't help much (I'm using Marmalade SDK).

If I assume Bézier's Curve as the solution of choice, I'm left with one specific problem: I'll have the car's position (the first endpoint) and the target/final position (the last endpoint), but what should I use as the control point (assuming a square/quadratic curve)?

And whether I use Bézier's Curve or another parametric equation, I'd still be left with another issue: the car can't just follow the curve, it must turn (i.e. adjust its angle) accordingly. So how can I figure out which way the car should be pointing to at any given point in the curve?

\$\endgroup\$
2
  • 1
    \$\begingroup\$ You may find this question useful. It shows you how to get uniform movement on a Bézier curve. And the direction pointed by the car is what's inside the length() calls. \$\endgroup\$ Oct 3, 2012 at 7:19
  • \$\begingroup\$ Thanks, I actually had come across that question, but I hadn't tried to implement it yet, since I figure I'd need a few lessons in Khan Academy first, to be able to do it :P @Arthur's solution seems a lot simpler though, so I'm thinking about giving it a shot instead. \$\endgroup\$
    – Vexille
    Oct 4, 2012 at 1:02

1 Answer 1

5
\$\begingroup\$

My answer will cover the following topics:

  • How to form a suitable Bezier curve for turning a character or a car smoothly.
  • How to maintain the same speed across the curve.
  • How to change the angle smoothly during motion.

This is suitable for any character in any 2d game with a top down view and with minor adjustments could be used for 3d games as well.

Some images for intuition:

Top down overview

Car waiting to take the turn

Waypoints

Move the car in a linear path(straight line) from the bottom to the 'Start' point. Now use a Bezier to get the car to the 'End' point through the 'Control' point. Use another linear path moving straight from the 'End' to the last point on the left.

Waypoints across the path

The resulting path

Resulting motion path

Intro:

Quadratic Bezier function:

q(t) = (1-t) * ((1-t) * start + t * control) + t * ((1-t) * continue + t * end) =
(1-t) * (1-t) * start + t * t * end + (1-t) * t * control + t * (1-t) * control =
(t^2 - 2t + 1) * start + t^2 * end + 2 * t * (1 - t) * control = 

You know the starting point(when you begin to turn) and the end point(when you are done turning and continue normally).

Creating a suitable Bezier:

To calculate the control point, use the x-value of the point that is directly bellow or above the turning point and the y-value of the point directly to the left or right of the turning point.

If the roads do not form a straight angle, simple draw an imaginary line through both roads and use the intersection as the control point. You can take a smoother turn if you pull the start & end points away from the control point.

Finding out the correct angle:

Break the curve into 10 - 100 waypoints depending on the length of the turn, the car should always point towards the next waypoint.

To do this, pick a step size:

stepSize = 0.01;

We start in the begining of the curve.

lastPoint = 0;
lastPointPos = q(lastPoint);

We move towards the next waypoint on the curve.

nextPoint = lastPoint + stepSize;
nextPointPos = q(nextPoint);

And the angle is the one between these two points:

angle = Math.atan2(nextPointPos.y - lastPointPos.y, nextPointPos.x, lastPointPos.x);

this link explains what Math.atan2 means:

http://msdn.microsoft.com/en-us/library/system.math.atan2.aspx

How to maintain your desired speed through the curve:

//Calculate how much the car can move this frame
moveAmount = carSpeed * deltaTime;
//Calculate distance from next waypoint on the Bezier Curve
distance = sqrt((car.x - nextPointPos.x)^2 + (car.y - nextPointPos.y));
While(moveAmount > 0)
//If it is close enough, move the car there and subtract the distance from the amount you can move during this frame
if(distance < moveAmount)
{
    moveAmount -= distance;

    lastPoint = nextPoint;
    lastPointPos = q(lastPoint);

    nextPoint += stepSize;
    mextPointPos = q(nextPoint);
}
//Otherwise move as much as you can towards the destination
else
{
    car.x += moveAmount * Math.cos(angle);
    car.y += moveAmount * Math.sin(angle);
    moveAmount = 0;
}
\$\endgroup\$
4
  • \$\begingroup\$ Thanks for the answer, Arthur! In your Bezier function, is continue the control point? It's a bit different from what I'm currently doing, which is (1 - t)² * p0 + 2 * (1 - t) * t * p1 + t² * p2, p1 being the control point. Also, I was aware of the problem of uniform movement in the curve, and I your solution seems very interesting, But how can I know it was too close for a whole step? Do I need to get the length of Q(currentVal) - Q(currentVal+stepSize)? \$\endgroup\$
    – Vexille
    Oct 4, 2012 at 0:51
  • \$\begingroup\$ I am editing the answer to clear things up. The equation you wrote down is the same as the one above. Continue is a typo -> control. You can tell if it's to close by the distance from the current position of the car and Q(currentVal + stepSize) \$\endgroup\$
    – AturSams
    Oct 4, 2012 at 21:30
  • \$\begingroup\$ Thanks, Arthur, that cleared things up. Although I've figured I'll implement the desired speed a bit differently. Since I'll need all the points in the curve to draw the line anyway (the line will indicate the path the car will follow once you end your turn), I'll just build a class to represent the curve and store all the necessary points. Whenever the distance between two points isn't long enough, I'll just get the next one until it's enough. \$\endgroup\$
    – Vexille
    Oct 5, 2012 at 23:54
  • 1
    \$\begingroup\$ That is pretty similar :) The only difference is I added 'interpolation' to make it appear smoother if you use less data. en.wikipedia.org/wiki/Interpolation_%28computer_programming%29 \$\endgroup\$
    – AturSams
    Oct 6, 2012 at 6:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .