Turret rotation

Started by
21 comments, last by nobodynews 11 years, 3 months ago

I have a turret in my 2D game, a turret capable of firing projectiles at its target(often the main character).

The turret is rotating so it is "pointing" at the target, if it can see it.

However, the rotation speed is instant. For example, the target is at the bottom-right-edge of the map, now the target somehow teleported to the top-left-edge of the map. You wouldnt see the turret rotating towards the top-left-edge. Instead, it would just immediately change.

So I want to implement some sort of rotation speed mechanism.

Here is an idea I came up with:

The turret is associated with an object that is moving in a circular path. Lets call this circular-moving object for c.

c is following the target. Since c is restricted to move in a circular path, it cant follow the target. So by follow, I mean whether it should move clockwise or counter clockwise. How to detect this, I haven no idea.

Then, we use an algorithm explained here: http://www.gamedev.net/topic/636343-get-a-points-coordinate/ , check if the target is intersecting with this line. If it is, we set c's movespeed to 0 and the turret can fire.

Advertisement

The easiest way to do this, is using vectors and the angle-between-two-vectors algorithm.

Suppose you know the turret's location and the target's, then you can find a vector to the target.

And suppose you know the turret's current angle, you can find a vector pointing "forwards" from the turret.

Normalise these two vectors, then you can find the dot-product, which gives you the angle.

But how do you know which way to turn? This can be done by doing the same thing with the "right" vector of the turret. If you dot the target with the right vector, then a + answer means you should turn "right" (i.e. clockwise), -ve, means you should turn left.

So:

* Find the "right" vector of the turret

* Find the vector to the target

* Assuming both are normalised, the angle between them is arc-sine of the dot-product.

You can then decide whether to turn the turret right,left, or shoot the gun. Presumably the turret has a maximum turn rate, so you'll clamp the turn rate at that. You might also want to model accelerator and/or movement delay for the turret.

I am more interested in your approach. My way was just an overkill.
So this is what I came up with.


public static double getAngle(float x1, float y1, float x2, float y2)
{
float deltaX = x2 - x1;
float deltaY = y2 - y1;


return Math.atan2(deltaY, deltaX) * 180 / Math.PI;
}

public static float rotateTowardsPoint(float x, float y, float targetX, float targetY, float speed)
{
Point2D.Float normalized = normalize(x, y, targetX, targetY);
double angle = getAngle(x, y, normalized.x, normalized.y);


return (float) clamp(angle, -speed, speed);
}


public static double clamp (double i, double low, double high) 
{
   return java.lang.Math.max (java.lang.Math.min (i, high), low);
}


public static Point2D.Float normalize(float x1, float y1, float x2, float y2)
{
float dx = x1 - x2;
float dy = y1 - y2;
double length = Math.sqrt( dx*dx + dy*dy );
dx /= length;
dy /= length;


return new Point2D.Float(dx,dy);
}

The results ingame, is that the unit that is suppose to rotate, rotates, but just to the same angle every frame. And the rotation is not even pointing at the target.

source: http://thadeusb.com/weblog/2009/3/9/rotate_sprite_towards_point

(I dont want to edit my prev post because this site keeps fucking the text in the code tags whenever I edit my post)

I like to use complex numbers to do computations in 2D geometry. Applying a rotation is represented by multiplication by a complex number whose modulus is 1. If I have a vector A and I am trying to make it point towards vector B, the rotation I need to apply is B/A normalized. I can then limit that rotation to something close to 1 (1 being no rotation, since I am multiplying).

This code is in C++, but hopefully I managed to make it reasonably clear. Notice how the loop doesn't have any trigonometric functions at all. There are also basically no special cases.
#include <iostream>
#include <complex>
#include <cmath>

typedef std::complex<float> C;

float const pi = std::atan(1.0f)*4.0f;

C normalize(C z) {
  return z/std::abs(z);
}

C limited_rotation_towards(C original, C target, C max_rotation) {
  C rotation = normalize(target / original);
  if (rotation.real() < max_rotation.real())
    return rotation.imag() > 0.0f ? max_rotation : conj(max_rotation);
  return rotation;
}

int main() {
  C z = 1.0f;
  C target (-1.0f, -1.0f);
  float max_angle = pi*0.125f;
  C max_rotation(std::cos(max_angle), std::sin(max_angle));

  for (int i=0; i<20; ++i) {
    std::cout << z << '\n';
    z *= limited_rotation_towards(z, target, max_rotation);
  }
}

The output is this:
(1,0)
(0.92388,-0.382683)
(0.707107,-0.707107)
(0.382683,-0.92388)
(-1.49012e-07,-1)
(-0.382684,-0.923879)
(-0.707107,-0.707107)
(-0.707107,-0.707107)
(-0.707107,-0.707107)
(-0.707107,-0.707107)
(-0.707107,-0.707107)
(-0.707107,-0.707107)
(-0.707107,-0.707107)
(-0.707107,-0.707107)
(-0.707107,-0.707107)
(-0.707107,-0.707107)
(-0.707107,-0.707107)
(-0.707107,-0.707107)
(-0.707107,-0.707107)
(-0.707107,-0.707107)

Thanks, but I cant understand that code.

typedef std::complex<float> C;?

Why do I need a max rotation?

Why doesnt it return a float/double?

Thanks, but I cant understand that code.

typedef std::complex<float> C;?

Why do I need a max rotation?

Why doesnt it return a float/double?

typedef is used to create alias for types in c and c++. In all places after this where you see C, you can substitute that for std::complex<float>.

Max rotation is basically the speed at which you can rotate.

It returns the angle as a complex number. If you really want an angular rotation represented by degrees you'll need to use trig to get that information. When the complex number is normalized then the real component represents the cosine of the angle of rotation and the imaginary component represents the sine of the angle of rotation. To get the angle of rotation as a float then you just take the inverse cosine of the real or the inverse sine of the imaginary. If you don't know if the complex number you have is normalized you can also use arctan of the real and imaginary numbers.

You might want to read this guide about complex numbers. It could help explain more of the math Alvaro does and it's also pretty informative in general.

C++: A Dialog | C++0x Features: Part1 (lambdas, auto, static_assert) , Part 2 (rvalue references) , Part 3 (decltype) | Write Games | Fix Your Timestep!

Here is the code from tank game I developed. It rotates the turret clockwise or counter-clockwise (depeding on which path is shorter) with desired speed.


destinationRotation = Math.Atan2(turret.Y - target.Y, turret.X - target.X) + Math.PI;

if (destinationRotation != currentRotation)
{
    if (destinationRotation > currentRotation)
    {
        if (currentRotation < destinationRotation - Math.PI)
            currentRotation -= rotationSpeed;
        else
            currentRotation += rotationSpeed;
    }
    else if (destinationRotation  < topRotation)
    {
        if (currentRotation > destinationRotation + Math.PI)
            currentRotation += rotationSpeed;
        else
            currentRotation -= rotationSpeed;
    }

    if (currentRotation > Math.PI * 2.0f) currentRotation = 0;
    if (currentRotation < 0) currentRotation = Math.PI * 2.0f;
}

Thanks, that code make a lot more sense, Just one thing, what do topRotation represent and how do I get that value?

Ah, sorry - this topRotation should be currentRotation. I was renaming the variables and missed this one.

This topic is closed to new replies.

Advertisement