views:

782

answers:

1

I would like to use Cocos2d on the iPhone to draw a 2D car and make it steer from left to right in a natural way.

Here is what I tried:

  1. Calculate the angle of the wheels and just move it to the destination point where the wheels point to. But this creates a very unnatural feel. The car drifts half the time

  2. After that I started some research on how to get a turning circle from a car, which meant that I needed a couple of constants like wheelbase and the width of the car.

After a lot of research, I created the following code:

float steerAngle = 30; // in degrees
float speed = 20;
float carWidth = 1.8f; // as in 1.8 meters
float wheelBase = 3.5f; // as in 3.5 meters

float x = (wheelBase / abs(tan(steerAngle)) + carWidth/ 2);
float wheelBaseHalf = wheelBase / 2;
float r = (float) sqrt(x * x + wheelBaseHalf * wheelBaseHalf);

float theta = speed * 1 / r;
if (steerAngle < 0.0f)
    theta = theta * -1;

drawCircle(CGPointMake(carPosition.x - r, carPosition.y),
           r, CC_DEGREES_TO_RADIANS(180), 50, NO);

The first couple of lines are my constants. carPosition is of the type CGPoint. After that I try to draw a circle which shows the turning circle of my car, but the circle it draws is far too small. I can just make my constants bigger, to make the circle bigger, but then I would still need to know how to move my sprite on this circle.

I tried following a .NET tutorial I found on the subject, but I can't really completely convert it because it uses Matrixes, which aren't supported by Cocoa.

Can someone give me a couple of pointers on how to start this? I have been looking for example code, but I can't find any.

EDIT After the comments given below I corrected my constants, my wheelBase is now 50 (the sprite is 50px high), my carWidth is 30 (the sprite is 30px in width).

But now I have the problem, that when my car does it's first 'tick', the rotation is correct (and also the placement), but after that the calculations seem wrong.

The middle of the turning circle is moved instead of kept at it's original position. What I need (I think) is that at each angle of the car I need to recalculate the original centre of the turning circle. I would think this is easy, because I have the radius and the turning angle, but I can't seem to figure out how to keep the car moving in a nice circle.

Any more pointers?

+1  A: 

You have the right idea. The constants are the problem in this case. You need to specify wheelBase and carWidth in units that match your view size. For example, if the image of your car on the screen has a wheel base of 30 pixels, you would use 30 for the WheelBase variable.

This explains why your on-screen circles are too small. Cocoa is trying to draw circles for a tiny little car which is only 1.8 pixels wide!

Now, for the matter of moving your car along the circle:

The theta variable you calculate in the code above is a rotational speed, which is what you would use to move the car around the center point of that circle:

Let's assume that your speed variable is in pixels per second, to make the calculations easier. With that assumption in place, you would simply execute the following code once every second:

// calculate the new position of the car
newCarPosition.x = (carPosition.x - r) + r*cos(theta);
newCarPosition.y = carPosition.y + r*sin(theta);

// rotate the car appropriately (pseudo-code)
[car rotateByAngle:theta];

Note: I'm not sure what the correct method is to rotate your car's image, so I just used rotateByAngle: to get the point across. I hope it helps!

update (after comments):

I hadn't thought about the center of the turning circle moving with the car. The original code doesn't take into account the angle that the car is already rotated to. I would change it as follows:

...
if (steerAngle < 0.0f)
    theta = theta * -1;

// calculate the center of the turning circle,
// taking int account the rotation of the car
circleCenter.x = carPosition.x - r*cos(carAngle);
circleCenter.y = carPosition.y + r*sin(carAngle);

// draw the turning circle
drawCircle(circleCenter, r, CC_DEGREES_TO_RADIANS(180), 50, NO);

// calculate the new position of the car
newCarPosition.x = circleCenter.x + r*cos(theta);
newCarPosition.y = circleCenter.y + r*sin(theta);

// rotate the car appropriately (pseudo-code)
[car rotateByAngle:theta];
carAngle = carAngle + theta;

This should keep the center of the turning circle at the appropriate point, even if the car has been rotated.

e.James
I tried to integrate your comments, but it is still not working. I tried to explain this in my original post. Hope you can give me some more tips?
Wim Haanstra
I added some code to fix the problem with the center of the circle moving around. Give it a shot!
e.James
Thanks, it still needs some tweaking, but this seems like a very good start.. Will update my start post the minute I got it completely working!
Wim Haanstra
Glad to hear it. Good luck with the project :)
e.James