Solved LibGDX Top down car rotation very weird and buggy

Hi, I’ve been working on a top down 2D racing game in Java with LibGDX. I’ve spent the last several days trying to implement some kind of rotation system for the car, following several forum posts and websites. I’m using an X and Y 2D Vector for velocity, and the Up and Down arrow keys add acceleration to the velocity X and Y. The Left arrow key subtracts the rotation angle from the rotationStep, and the Right arrow key adds it. Then friction is accounted for and the positions for the car are set based on the rotation angle and the velocity. Right now, pressing up makes the car go forward at a 45 degree angle, and pressing down makes the car move at a 225 degree angle, but still forward. However, if you press the left or right arrow keys to rotate, everything gets crazy; the car rotates way too fast (which I’ve tried lowering the rotationStep but it doesn’t do much), and when the car rotates, the angle changes as indicated by some print calls, but it moves in a small circle for a time and when you press the up or down arrows the rotation angle goes back to 45 degrees or 225 degrees. If someone can figure out what the heck is going on, it would be much appreciated! Thanks!

Also, the car is oriented from west to east, with the front of the car pointing towards the west; 180 degrees. Maybe that has something to do with anything? :clue:

Here’s the Track1 class that has all the movement code for the car in it.


import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.math.Vector2;

public class Track1 implements Screen {
	
	private CORGame game;
	private OrthographicCamera cam;
	
	private Texture background;
	private Texture trackTex;
	private Texture playerCarTex;
	
	private Sprite track;
	private Sprite playerCar;
	
	private float trackX;
	private float trackY;
	
	private Vector2 velocity;
	private float acc; // acceleration
	private float friction;
	private float rotation;
	private float rotationStep;
	private float topVelocity;
	
	public Track1(CORGame game) {
		this.game = game;
		
		cam = new OrthographicCamera(game.getScreenWidth(), game.getScreenHeight());
		cam.update();
		
		loadAssets();
		
		track = new Sprite(trackTex);
		playerCar = new Sprite(playerCarTex);
		
		trackX = (-track.getWidth()/2) - 90;
		trackY = (game.getScreenHeight()/2) - (playerCar.getHeight()/2);
		
		track.setPosition(trackX, trackY);
		playerCar.setPosition((game.getScreenWidth()/2) - (playerCar.getWidth()/2), 
				(game.getScreenHeight()/2) - (playerCar.getHeight()/2));
		
		velocity = new Vector2(0, 0);
		acc = 0.1f;
		friction = 0.01f;
		rotation = 0;
		rotationStep = 0.1f;
		topVelocity = 50;
	}

	@Override
	public void show() {
		
	}

	@Override
	public void render(float delta) {
		Gdx.gl.glClearColor(0/255f, 0/255f, 0/255f, 0/255f);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		
		game.batch.begin();
		
		game.batch.setProjectionMatrix(cam.combined);
		
		game.batch.draw(background, trackX, trackY, background.getWidth(), background.getHeight());
		game.batch.draw(track, trackX, trackY);
		game.batch.draw(playerCar, playerCar.getX(), playerCar.getY(), playerCar.getOriginX(),
				playerCar.getOriginY(), playerCar.getWidth(), playerCar.getHeight(), 1, 1, playerCar.getRotation());
		
		game.batch.end();
		
		update(delta);
	}
	
	private void update(float delta) {
		
		if(Gdx.input.isKeyPressed(Input.Keys.UP)) {
			velocity.x += acc;
			velocity.y += acc;
		}
		
		if(Gdx.input.isKeyPressed(Input.Keys.DOWN)) {
			velocity.x -= acc;
			velocity.y -= acc;
		}
		
		if(Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
			rotation -= rotationStep;
			velocity.rotate(rotation);
			playerCar.rotate(velocity.angle());
		}
		
		if(Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
			rotation += rotationStep;
			velocity.rotate(rotation);
			playerCar.rotate(velocity.angle());
		}
		
		if(Math.abs(velocity.x) < 0.1 || Math.abs(velocity.y) < 0.1) {
			velocity.x = 0;
			velocity.y = 0;
		}
		
		velocity.x *= (1 - friction);
		velocity.y *= (1 - friction);
		
		playerCar.setOriginCenter();
		
		playerCar.setX((float) (playerCar.getX() + (Math.cos(velocity.angle()) * velocity.x)));
		playerCar.setY((float) (playerCar.getY() + (Math.sin(velocity.angle()) * velocity.y)));
		
		cam.position.set(playerCar.getX() + (playerCar.getWidth()/2), playerCar.getY() + (playerCar.getHeight()/2), 0);
		cam.update();
	}

	@Override
	public void resize(int width, int height) {
		game.viewport.update(width, height);
	}

	@Override
	public void pause() {
		
	}

	@Override
	public void resume() {
		
	}

	@Override
	public void hide() {
		
	}

	@Override
	public void dispose() {
		
	}
	
	private void loadAssets() {
		background = game.mgr.get("img/backgrnd_1.png", Texture.class);
		trackTex = game.mgr.get("img/track1.png", Texture.class);
		playerCarTex = game.mgr.get("img/car1.png", Texture.class);
	}

}

Hi elaguy ,

You’re currently setting the both x and y velocity when the the player presses up or down. This is causing your car to move in those angles.

Instead of setting the velocity of the car directly you could update the car speed from the user input and then calculate the velocity based on the angle of rotation of the car. e.g:


private void update(float delta) {
      
      if(Gdx.input.isKeyPressed(Input.Keys.UP)) {
    	  speed+=1f ;
    	  if(speed > 2){
    		  speed = 1;
    	  }
      }
      
      if(Gdx.input.isKeyPressed(Input.Keys.DOWN)) {
    	  speed-=1f;
    	  if(speed < 0){
    		  speed = 0;
    	  }
      }
      
      if(Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
         playerCar.rotate(+3);
      }
      
      if(Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
    	  playerCar.rotate(-3);
      }
      
      playerCar.setOriginCenter();
      
      Vector2 vel = getVelocity(speed,playerCar.getRotation());
      playerCar.setX(playerCar.getX() + vel.x);
      playerCar.setY(playerCar.getY() + vel.y);
      
      System.out.println("Rotation:"+playerCar.getRotation());
      
      cam.position.set(playerCar.getX() + (playerCar.getWidth()/2), playerCar.getY() + (playerCar.getHeight()/2), 0);
      cam.update();
   }
   
   private Vector2 getVelocity(float sp, float rotation){
	   Vector2 vel = new Vector2();
	   float vx = (float) Math.cos(Math.toRadians(rotation)) * sp;
	   float vy = (float) Math.sin(Math.toRadians(rotation)) * sp;
	   vel.x = vx;
	   vel.y = vy;
	   return vel;
   }

Thank you, dfour! That clears everything up nicely. I’ll try that when I can and see what happens.

Alright, so I implemented everything and now rotation works like a charm. I had to fix a few things regarding friction but now my car is moving just fine now. Thanks again.