Piotr Buda
Piotr Buda

Reputation: 353

Smooth movement and turning of a game object using libgdx

I'm trying to learn some game development and while I'm recollecting everything about linear algebra and vector maths I learned in my life I also started playing with libgdx.

The first thing I wanted to start with is a simple movement with rotation and I came up with an idea how this may be done by calculating angles from current direction to the desired direction. While it worked for still sprite, adding movement to it proved it wrong, as the sprite is doing a U kind of turn (not going to the point, but the direction of the point)

The example is implemented here https://github.com/pbuda/libgdx-movement in Scala. The code in question is located in the Spaceship class.

Just for quick reference, here is the main part of the class:

def update(delta: Float) {
  rotate(delta)
  move(delta)
}

private def move(delta: Float) {
  velocity.set(direction).scl(movementSpeed)
  position.add(velocity.scl(delta))
}

private def rotate(delta: Float) {
  val current = direction.angle()
  val target = targetDirection.angle()
  if (current != target) {
    val rotateAngle = rotationSpeed * delta
    val left = distance(current, target)
    val right = distance(target, current)
    if (left < right) rotateLeft(current, left, rotateAngle)
    else rotateRight(current, right, rotateAngle)
  }
}

private def distance(start: Float, end: Float): Float = {
  val angle = start - end
  if (angle < 0) angle + 360 else if (angle > 360) angle - 360 else angle
}

private def rotateLeft(current: Float, target: Float, angle: Float) {
  if (target - angle < 0) direction.set(targetDirection)
  else direction.setAngle(direction.angle() - angle)
}

private def rotateRight(current: Float, target: Float, angle: Float) {
  if (target - angle < 0) direction.set(targetDirection)
  else direction.setAngle(direction.angle() + angle)
}

Now, I feel there's a much cleaner solution for that using some vectors (position, velocity, direction and probably destination) but I'm kind of stuck and don't know where to go from here.

I want a behaviour you would imagine from an rts space game where after setting destination for a ship it would go that way but not just switch direction but make a smooth turn and go exactly to the point from the mouse click.

Any help is really appreciated.

Upvotes: 0

Views: 1464

Answers (2)

Piotr Buda
Piotr Buda

Reputation: 353

I found this page http://www.euclideanspace.com/ which helped me to understand how this should work. I replaced my naive implementation with vector algebra and here is the result

private def rotate(delta: Float) {
  val targetHeading = destination.cpy.sub(position).nor
  val angle = angleBetween(heading, targetHeading)
  if (angle != 0) {
    if (Math.abs(angle) < rotationSpeed / 50) heading.set(targetHeading)
    else {
      val rotation: Float = Math.max(Math.abs(angle), rotationSpeed) * delta
      if (angle > 0) heading.rotate(rotation)
      else heading.rotate(-rotation)
    }
  }
}

My problem was that the targetHeading (previously targetDirection) was calculated when setting the destination point instead of updating it in each update loop (where rotate is called). The angleBetween method is as follows

private def angleBetween(v1: Vector2, v2: Vector2): Float = {
  val result = Math.toDegrees(Math.atan2(v2.y, v2.x) - Math.atan2(v1.y, v1.x)).toFloat
  if (result < -180) result + 360
  else if (result > 180) result - 360
  else result
}

so it just calculates an angle between two vectors. The important thing is that the result is in range (-180, 180) which dictates the direction of the turn.

Now when the ship arrives at destination it starts to circle around destination, but implementing a stop behaviour would be trivial (just guards against position).

Upvotes: 0

Kevin Workman
Kevin Workman

Reputation: 42176

You need to have a heading, defined as an angle in radians or degrees. Let's say a heading of 0 points straight up, 90 points to the right, 180 points straight down, and 270 points left.

Use basic trig to figure out your deltaX and deltaY each time, based on your heading and your speed.

Figure out your target position, and again use basic trig to figure out a goal heading that points from your ship to the target position.

Move your current heading towards the goal heading by some small increment each frame.

If you have trouble on a specific part of the above process, post an MCVE (not your whole project) that demonstrates what you have so far, and we'll go from there.

Upvotes: 0

Related Questions