Turret Rotation in 3D

I’m making a game that’s designed to teach kids about angles. In the game, you aim a turret in the middle of the screen in an arc similar to that of a protractor (half circle). Ideally, the gun would rotate to the position they click and display the angle on the turret. Is there any good ways of doing this? I’ve seen some things with lookAt, and quaternions, but I’m having trouble figuring them out. I have a turret that has bones, so you would only have to adjust the y rotation of the gun, but I’m having difficulty coming up with a way to make it rotate to the correct angle.

Thanks.

So you are rotating this gun in 3D? That could be more complicated especially if you are clicking into an empty scene. When you click on an empty point on the screen in 3D space you are actually clicking on a infinite line of points that all collapse to that screen point.

Camera.main.ScreenToWorldPosition(Vector3);

will return a valid Vector3 in world space to point at, but your Vector3 you pass in will be the X,Y co-ordinates of the mouse:

Input.mousePosition;

but you will need to supply the Z position of how far away you want if you don’t have a reference.

That is pretty easy to do if you just want to say whever I click give me the co-ordines of that space 30 units away:

public float zDistance =30f;

void Update()
{
       if (Input.GetMouseButtonUp(0))
       {
              Vector3 spot = Input.mousePosition;
              spot.z = zDistance;
              Vector3 position = Camera.main.ScreenToWorldPoint(spot);
               transform.LookAt(position);
         }
}
1 Like

So it’s in 3D Space, but it essentially should be acting as if it was in 2D. What’s happening is that asteroids are falling from various angles with respect to the turret, and the turret points directly to where you click, and fires. If I just want it to aim at the point on the screen I click on, would that z value be 0?

I think most cameras have a near and far clip plane. usually the default near is set to 0.3f So I would make the Z Distance be something small but bigger than 0.3f; Though if the asterioids are falling, all the all falling in a a 2D XY plane at some specified Z Distance. Then you should use whatever value your dropping the asteroids at. So if your dropping the Asteroids at (x,y,0) and your camera is sitting at (0,1,-10) just set your ZDistance to 10

Regardless of what I put in for z, it seems to not adjust what degree the turret rotates to look at a spot in 3D space. Additionally, when I add a Debug.log and print out the z value of the vector3 I’m getting from Camera.main.ScreenToWorldPoint(spot);, I end up with something other than what I put in. All I’m really looking to do is adjust the y component of the gun barrel so it will rotate to the angle of the mouse click. The second pictures shows what happens when I click.
2844864--207733--2016-11-07.jpg 2844864--207734--2016-11-07 (1).jpg

Just so we are clear it looks to me like that picture is rotating about the Y-Axis. If you rotate the Y component you barrel spins in place… it won’t move up or down (X-Axis rotation) or spin around like a dryer tumbling (z-Axis rotation)

I think your problem is the initial position of the object you’ve imported is facing left. It looks like from the first picture when the rotation is 0,0,0 its pointing directly left. So its actually the middle of your turret that is facing forward in world Space. You could take the code you have now and after the lookAt do this:

Vector3 eulerAngles = transform.rotation.eulerAngles;
eulerAngles.y+=90;
transform.rotation = Quaternion.Euler(eulerAngles);

This will make it look at the mouse position, which in your case is making the middle of the turret look at the mouse position, then turn it 90 degrees right so the actual gun is pointing that way.

Or you could reImport your image so its facing the correct way when you put it in the scene. The gun barrel should be facing straight forward when its rotation is 0,0,0.

I tried this, but it seems to make it go even further off, even if I try adjusting the initial rotation of the turret.

To make sure we’re on the same page, I’m basically trying to have the gun rotate on one axis as if you were moving the barrel along a protractor like the one attached. I don’t need to worry about any of the rest of it turning to face the mouse.

2844981--207741--protractor.gif

Oh thats rotating around the z-Axis then. if you are working in world space:
X-Axis = will tilt your gun up and down
Y-Axis = will spin it like a ballerina twilrling
Z-Axis turns it along the protractor

I think in that case its easier to ignore the world position of the mouse and instead get the screen position of your turret and use trig (specifically ArcTangent to get the angle betweeen the turret and the mouse position) Though again your turret starts pointing due left so we’ll have to take that into account.

This algorithm does this:

  • Find the screen Position of theTurret
  • Start with an Angle Rotation of 180, so our turret is lying on the positive X-Axis
  • Find the Angle between our turret and the mouse Position going counterclockWise from the X-Axis (Like normal Highschool math)
  • Subtract that angle from 180 and rotate our turret to that position
void Update()
{
       if (Input.GetMouseButtonUp(1))
       {
             float angle = 180f;
             angle -= GetAngleWithMousePosition();
             Vector3 eulerAngles = transform.rotation.eulerAngles;
             eulerAngles.z  = angle;
             transform.rotation = Quaternion.Euler(eulerAngles);
          }
}

// returns an angle based on Positive X-Axis going
// Going counterclockwise with the mouse position
// Normal HighSchool trig angle on Cartesian co-ordinates
float GetAngleWithMousePosition()
{
             // note if you never move your Camera or your turret, you can get this value one time in Awake or Start
             Vector2 screenPosition = Camera.main.WorldToScreenPoint(transform.position);
             float deltaX = Input.mousPosition.x - screenPosition.x;
             float deltaY = Input.mousePosition.y - screenPositin.y;
           
             if (deltaY <=0)
            {
                  // if its in the 4th quadrant just return 0
                  // we don't want to rotate the turret down
                  if (deltaX >=0)
                        return 0f;
                  // if it in the 3rd Quadrant just return 180
                  else
                      return 180f;
             }
             // Can't divide by 0
             if (Mathf.Approximately(deltaX,0f)
                   return 90;
             return Mathf.Atan(deltaY/deltaX);
}

Note this code just instantly rotates to the correct position. Once you get it working so it rotates to where you want you can work on a Coroutine to slowly rotate it.

1 Like