LibGDX - Move camera to cursor location when dragged

Howdy gents and ladies

I seem to be having issue in getting my camera to move to the cursor location in a smooth fashion. If you have ever played an RTS game then you may be aware of the type of movement I am looking for. When a user presses the left mouse button down, I want the camera to slowly move to the location of the cursor.

Now… here is what I have so far.

I am taking the cursor cords and un-projecting them to the world cords system
I then take those positions and add then to the camera.position.x and camera.position.y
I then multiple the movement by the speed variable to get a movement that will be more smooth

There seems to be multiple issues with this approach. The first is that the camera just moves extremely fast and I can’t seem to slow it down. The other issue is likely the way I am handling the actually movement of the camera as I could see 2 different ways of doing this. I could move the camera to the cursor position no matter where on the screen it is, however I would need to have a tolerance check to stop any horrible rendering flickering. The other way is to check if the cursor position is at the left/right/top/bottom of the screen and then move to that location slowly. I personally like the first approach.

Just wondering if anyone had any idea to what I am doing wrong?

You could try using acceleration and velocity. Just setting an velocity would be like setting off an explosion, you go from standing still to insane speeds, that feels unnatural.

Instead start slow and increase the velocity with time (exponentially) until you reach the maximum speed, then do the opposite for stopping.

Another approach would be to let the user drag the map directly by holding the mouse button down and moving the mouse. This is instant but feels good because you have direct and unimpaired control over the movement.

Ah yes, good idea. Sorry, I’ve been away from game dev for a little over a year and I think I understand what I’m doing wrong.

I’m going to now create a Vector2 that points in the direction of the mouse (and normalized) and then add a speed to that. Hopefully that will solve my instant movement problems. I will report back! :slight_smile:

Interesting… So I have the movement of the camera working as expected but it’s strange what I had to do to get it work… I’ve included the code below! :point:


public void mapMapCamera(OrthographicCamera camera) {
	Vector3 input = camera.unproject(new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0));
	Vector2 direction = new Vector2(input.x - camera.position.x, input.y - camera.position.y);
		
	direction.nor();
		
	float newCameraX = camera.position.x + direction.x * 150 * Gdx.graphics.getDeltaTime();
	float newCameraY = camera.position.y + direction.y * 150 * Gdx.graphics.getDeltaTime();
		
	camera.position.set(new Vector3(newCameraX, newCameraY, 0));
}

It seems that I have to first un-project the input cords before I start using them. I then create a vector that points to the point in space. I just want to add some logic now that means the camera only moves if the user’s mouse is near the edge of the screen :slight_smile:

That is nice, you are still using instant velocity though, while something like this would look nicer:

You start with no speed, ease in, quickly reach full camera speed, ease out and reach target.
The ease in/out needs to be fluid but very swift.
A linear ease in/out will feel bad, use an appropriate function from the LibGDX Interpolator class!

Thank you for your insight :slight_smile: My next task is to implement what you have described above.

I’m leaving my camera movement (RTS style) code below so anyone who wishes to use/improve it can do so! :slight_smile:


public void mapMovement(OrthographicCamera camera, Viewport viewport) {
	Vector3 input = camera.unproject(new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0)); // Input to world cords
	Vector2 direction = new Vector2(input.x - camera.position.x, input.y - camera.position.y); // Direction to the cursor in world cords
	
	// This create a bounds tolerance so that the cursor will only move when outside of the area
	float boundsToleranceWidth = 20f / 100f * App.GAME_DEFAULT_WIDTH;
	float boundsToleranceHeight = 20f / 100f * App.GAME_DEFAULT_HEIGHT;
	
	// We create a movementBounds variable that will be used for creating an invisible box that the mouse can only move outside of
	Vector3 movementBounds = camera.unproject(new Vector3(0, Gdx.graphics.getHeight(), 0));
 
        // This creates the bounding box
	Rectangle boundsTolerance = new Rectangle(movementBounds.x + boundsToleranceWidth / 2, movementBounds.y + boundsToleranceHeight / 2, 
            App.GAME_DEFAULT_WIDTH -    boundsToleranceWidth, App.GAME_DEFAULT_HEIGHT - boundsToleranceHeight);
	
	// Debug rendering code for the box
	ShapeRenderer render = new ShapeRenderer();
	render.setAutoShapeType(true);
	render.setProjectionMatrix(camera.combined);
	render.begin();
	render.rect(boundsTolerance.x, boundsTolerance.y, boundsTolerance.getWidth(), boundsTolerance.getHeight());
	render.end();
	
        // If the cursor location in the world cords is outside of the box then the camera can move to the location	
	if (!boundsTolerance.contains(input.x, input.y)) {
		direction.nor();
			
		float newCameraX = camera.position.x + direction.x * 150 * Gdx.graphics.getDeltaTime();
		float newCameraY = camera.position.y + direction.y * 150 * Gdx.graphics.getDeltaTime();
			
		camera.position.set(new Vector3(newCameraX, newCameraY, 0));
	}
}