Car rotation problem in top down

Hahahahaha I actually thought about this in the beginning, seeing as it is how it is done in one of my Java books that I own and occasionally use as a reference. I guess I just really wanted to see if I could have used a separate file as it seems like my game class will be huge with the controller included inside it. Also having made the change I feel like the code really IS much neater and easier to understand with the usage of the entity class of course.

On a side note: I really hate going back to the move function but to be totally honest I really need clarity. I was playing around with it and I just want to know if you guys can tell me why it isn’t working the way I want it to. Easy example is I want the to travel 100 pixels in 4 seconds. This is the current way that its being done, and odviously the wrong way. It is really making me angry as to why I can not get such a simple concept through my head.


double oldTime = 0;
private void move(int deltaTime) {
        double deltaSeconds = deltaTime / 1000.0; // seconds since last update
		
	if (up)
		acceleration = (100/4) * deltaSeconds;
	if (down)
		acceleration = (100/4) * deltaSeconds;
	if (right)
		carAngle += rotationStep * (vx/topspeed);
	if (left)
		carAngle -= rotationStep * (vy/topspeed);
		
	vx = Math.min((vx + acceleration) * ( 1 - friction),6);
	vx = Math.abs(vx) < 0.09? 0 : vx;
	vy = Math.min((vy + acceleration) * ( 1 - friction),6);
	vy = Math.abs(vy) < 0.09? 0 : vy;
		
		   
	double ax = Math.sin(Math.toRadians(carAngle));
	double ay = -Math.cos(Math.toRadians(carAngle));
	x += ax * vx;
	y += ay * vy;
}

To my knowledge acceleration = deltavelocity / deltatime. So (100/4) * deltaTime should be the correct formula.

Do you mean that you want the car to cruise at a maximum speed of (100/4=)25 pixels per second? Or that you want the car to accelerate from zero to its maximum speed in 4 seconds, and to have travelled 100 pixels while doing so?

[quote]To my knowledge acceleration = deltavelocity / deltatime. So (100/4) * deltaTime should be the correct formula.
[/quote]
The first statement is correct. But I don’t know what you mean by the second statement. If (100/4) is the speed, then (100/4)*deltaTime is the distance travelled…?

I’m going to claim that the following code will do what you want if you set the correct values for acceleration, topSpeed and rotationSpeed. I think you should probably convince yourself that everything else is working before including friction, etc. (Two specific problems with your old code. First, the two separate speed variables vx and vy didn’t make sense. Second, if your function is using the time step length deltaTime, then when that equals zero the function should do nothing - your code was failing that sanity check.)


final private static double acceleration = ???; // (pixels per second per second)
final private static double topSpeed = ???; // (pixels per second)
final private static double rotationSpeed = ???; // (degrees per second)

private double x, y; // position (pixels)
private double v; // speed (pixels per second)
private double carAngle; // direction (degrees)

private void move(int deltaTime) {
  double deltaSeconds = deltaTime / 1000.0; // seconds since last update

  if (up)
    v = Math.min(+topSpeed, v + acceleration*deltaSeconds);
  if (down)
    v = Math.max(-topSpeed, v - acceleration*deltaSeconds);

  if (right)
    carAngle += rotationSpeed * deltaSeconds * (v/topSpeed);
  if (left)
    carAngle -= rotationSpeed * deltaSeconds * (v/topSpeed);

  double ax = Math.sin(Math.toRadians(carAngle));
  double ay = -Math.cos(Math.toRadians(carAngle));
  x += ax * v * deltaSeconds;
  y += ay * v * deltaSeconds;
}

Simon

Alright finally that really clears it up. I guess I was just being confused all around with friction and multiple velocities instead of just using a single velocity for both x and y. Here is what it comes out to. Note that I did add friction ;D. I am really pleased with how that turned out, and hopefully it is done the correct way ???. I’m glad to say I understand what is actually happening now that its done step by step.


private double friction = 0.3;
	
private double acceleration = 100/2.5; // (pixels per second)
private double currentSpeed = 0.0; //speed (pixels per second)
private double topspeed = 220;
private double rotationSpeed = 90;

private double carAngle = 0;
	
private void move(int deltaTime) {
	double deltaSeconds = deltaTime / 1000.0;// seconds since last update
		
	if (up) {
		currentSpeed = Math.min(+topspeed, currentSpeed + acceleration * deltaSeconds);
	}
	if (down) {
		currentSpeed = Math.max(-topspeed, currentSpeed - acceleration * deltaSeconds);
	}
	if (right)
	  carAngle += rotationSpeed * deltaSeconds * (currentSpeed/topspeed);
	if (left)
	  carAngle -= rotationSpeed * deltaSeconds * (currentSpeed/topspeed);
		
		
	currentSpeed *= Math.max(0.0, (1 - friction * deltaSeconds));
	currentSpeed = Math.abs(currentSpeed) < 0.9 ? 0 : currentSpeed;
		
	double ax = Math.sin(Math.toRadians(carAngle));
	double ay = -Math.cos(Math.toRadians(carAngle));
	x += ax * currentSpeed * deltaSeconds;
	y += ay * currentSpeed * deltaSeconds;
}

To me it does seem strange though that I need to have a rotationSpeed of 90 for it to rotate at a decent rate. I suppose it is one of those constants that you just have to be comfortable with?

And on a kind of related note, I’m having troubles with collision detection because of the rotation. I have taken a look at http://www.java-gaming.org/index.php/topic,23694.0.html and http://www.java-gaming.org/index.php?topic=23741.0 but I’m not totally sure if I need to use a shape or if there is anyway to rotate the rectangle around my image. For example to get the bounds of my image I use


public Rectangle getBounds() {
	return new Rectangle((int)Math.round(x), (int) Math.round(y), carImage.getWidth(null), carImage.getHeight(null));
}

and I bet that in itself this is the problem because we can’t rotate the bounds of the object? My render function for the car class is as follows.

	
public void render(Graphics2D g) {
	g.setColor(Color.WHITE);
	g.drawRect(Map.getGameArea().x, Map.getGameArea().y, Map.getGameArea().width, Map.getGameArea().height);
		
	g.setColor(Color.GRAY);
	g.drawRect(getBounds().x, getBounds().y, getBounds().width, getBounds().height);
	g.rotate(Math.toRadians(carAngle),x,y);
	g.drawImage(carImage, (int) Math.round(x), (int)Math.round(y), null);
		
}

I would also appreciate it if anyone could tell me exactly what the difference is between using the graphics object or using an AffineTranform object. Lastly I would like to say that I apologize for all the jumping around between the code I’ve been doing. Can’t seem to stick around one area long enough to complete it! I guess once I get confirmation that the move method is now correct I can say at least one task is complete.

[quote=“deadly72,post:63,topic:36326”]
The Graphics2D object is where you put the graphics (think bitmap) and the AffineTranform says how you put them there.
So you load an image, rotate it (AffineTranform) and draw it on a piece of paper (Graphics).
I doubt using Graphics2D.rotate() is very efficient - I’d use Graphics2D.drawImage(image, affineTrans… instead.
Also, ditch all the toRadians() stuff! Work in radians: 360 degrees = 2PI radians, 90’=1.571 rad . (blush I still have a circular chart on my wall showing degrees/radians conversion).

Nah toRadians() is simpler ;D

A rotation speed of 90 degrees per second means it will take 4 second for the car to turn all the way around (360 degrees). Which doesn’t seem unreasonable.

[quote]And on a kind of related note, I’m having troubles with collision detection because of the rotation.
[/quote]
Start with a Rectangle2D object. Create an Area object from it. Apply an AffineTransform (rotation) to that object. (You may need to browse the JDK documentation for some of this…) Then use it to check for collisions.

Here’s the important bit: Use Graphics2D.draw() to display the Area object each frame, or you won’t have a clue what’s going on. (I guess I didn’t need to say that though, because you’re doing it already with the bounding boxes.)

[quote]I guess once I get confirmation that the move method is now correct I can say at least one task is complete.
[/quote]
If you’re happy with it, then I think everyone’s happy with it. :slight_smile:
Simon

Alright so I mostly did what you told me with a few modifications I suppose? I know it’s probably done the wrong way but it works! :). I would like to perfect it though.

Initialize the shape with the car constructor


private Area carArea;

public Car(String name, String imagePath) {
	...
	carArea = new Area(getBounds());
}

This is the part I’m not too sure if it is done the right way


public void update(int deltaTime) {
	if ((Map.getGameArea().contains(carArea.getBounds()))) {
		move(deltaTime);
	}

        ...
        car = new Area(getBounds());

I’m not sure why you said to use a Rectangle2D while Rectangle could do the same ???


public Rectangle getBounds() {
	return new Rectangle((int)Math.round(x), (int) Math.round(y), carImage.getWidth(null), carImage.getHeight(null));
}

And finally the render which I’m pretty sure there is something wrong with too - efficiency wise?


public void render(Graphics2D g) {
		...

		AffineTransform savedAT = new AffineTransform();
		savedAT = g.getTransform();
		AffineTransform newAT = new AffineTransform();
		newAT.rotate(Math.toRadians(carAngle),x,y);
		carArea.transform(newAT);
		g.draw(carArea);
		
		
		g.setColor(Color.GRAY);
		g.setTransform(newAT);
		g.drawImage(carImage, (int) Math.round(x), (int)Math.round(y), null);
		g.setTransform(savedAT);
		
}

As I mentioned the above works, but I would like to perfect / do it the right way? To me it seems that creating a new area object each update isn’t the way to go?

You don’t need to create a new Area object each time, just rotate it when you rotate the car.

Rectangle2D is more accurate (uses double instead of int) and the API is less painful to look at. ;D

I’d suggest creating (and rotating) a new Area object each frame. The cost in terms of efficiency is negligible (unless your game features a very large number of cars ;)). And it’s better to always transform from fresh each time, rather than accumulating transforms, because otherwise numerical rounding errors will mean that the Area will slowly (admittedly very slowly, but let’s not risk it) drift away from the car’s true (x,y,carAngle) location.

Something like:


public Car(...) {
  ...
  carArea = makeArea();
}

private void move(...) {
  ...
  carArea = makeArea();
}

private Area makeArea() {
  Area a = new Area( getBounds() );
  AffineTransform tr = AffineTransform.getRotateInstance( Math.toRadians(carAngle), x, y );
  a.transform(tr);
  return a;
}

private static final boolean debugMode = true;
public void render(Graphics2D g) {
  ...
  if (debugMode) {
    g.setColor(Color.MAGENTA);
    g.draw(carArea);  
  }
}

So you rotate the Area (rectangle) object when you create it, not just when you draw it. And you can see that everything is working because (fingers crossed!) the rotated rectangle will follow the car around the screen. (Obviously you’d disable that effect in the released version of the game.)

Does that make some kind of sense?
Simon

Ah yes good point.

You can also use a bunch of Polygon objects for your collision. I typically know where the 4 vertices are so it’s easy for me.

Even better, if your car isn’t super long compared to its width, you can just use a single radius collision for everything. That is almost always what I do in games that are not meant to be physics-heavy. It’s the cheapest check possible and conceptually it’s easy to think about. None of this dealing with Polygons or Areas or anything. A cartoony-looking car usually would have a bounding box where the height is about 1.5 times the width, or less. That means a bounding circle with a radius of about 1.25 times the width will work great, and just making it the height also works great too (you don’t usually want a car to be exactly next to another one anyway).

I can say this works from experience, because I took this exact approach for cars in a professional game.

Thanks dishmoth, that fixed the problem with the rotation and now collision detection will be much simpler to handle as I have a rotating bounding box! ;D.

I was actually thinking of using polygons for all my objects that way I can have any shape really, but it isn’t really a necessity right now. I will definitely keep it in mind though!

Thanks for all the help everybody! on to collision detection! :wink:

Glad to help ;D

Agreed. Generally, bounding circles or axis-aligned (unrotated) bounding boxes are the best place to start. I was probably a bit hasty jumping in with the stuff about Areas. Ah well, so long as it works.
Simon