0
\$\begingroup\$

I'm working on a small project to learn Blender and Unity, and one issue I recently came across is that while I can have the turret of my tank rotate on the z-axis based on mouse movements, I can't for the life of me figure out how to lock it to certain angles.

My current approach was to use Mathf.Clamp to limit the range of values that I could pass to the rotate function, but that doesn't seem to have done anything at all, even though when outputting the value Clamp produces, I can tell it doesn't go over my specification.

The code I have:

enum RotationAxes {MouseX, MouseY, MouseXandY};
var axes : RotationAxes;
private var MouseX = RotationAxes.MouseX;
var sensitivityX : float = 15F;
var minimumZ : float= -35;
var maximumZ : float = 35;
function Update ()
{


    if (axes == MouseX)
    {
        //yRotation += Input.GetAxis("Mouse X");
        //transform.eulerAngles = Vector3(0, 0, yRotation);
        transform.Rotate(0, 0, Mathf.Clamp (Input.GetAxis("Mouse X")*sensitivityX, -30, 30));

    }

}


function Start ()
{

}
\$\endgroup\$
2
  • \$\begingroup\$ Remember that transform.Rotate says "rotate this far from where you are currently" — so it can stack up over the course of many frames. (Eg. If I rotate by 1 degree each frame at 30 fps, then in two minutes I'll have rotated through a full circle). If you want to set an absolute rotation rather than a relative change in rotation, you can assign a value to the transform's rotation or localRotation parameters. (These are quaternions, but you can convert from Euler angles with Quaternion.Euler()) \$\endgroup\$
    – DMGregory
    Jan 13, 2017 at 20:45
  • \$\begingroup\$ You're absolutely right, but do you have any suggestions on how to alter the code to accommodate your solution? \$\endgroup\$ Jan 13, 2017 at 20:54

3 Answers 3

2
\$\begingroup\$

Here's one way to approach it.

// Inspector parameters to control how we rotate.
var axisName     : String  = "Mouse X";
var sensitivity  : float   = 15f;
var minAngle     : float   = -30f;
var maxAngle     : float   = 30f;
var rotationAxis : Vector3 = new Vector3(0, 0, 1);

// Remember the original orientation of the object so we can rotate relative to that.
private var _initialRotation : Quaternion;

// Track the desired rotation angle over time.
// Note that this only works well for one axis.
// If combining multiple axes of rotation we'll usually want to store a Quaternion instead.
private var _currentAngle : float = 0f;

function Start() {
    // Cache the orientation of the object once it's spawned.
    // This lets us rotate relative to its placed orientation in a scene.
    _initialRotation = transform.localRotation;
}

function Update() {
    // Calculate the desired rotation angle by adding this frame's delta:
    _currentAngle += Input.GetAxis(axisName) * sensitivity * Time.deltaTime;

    // Clamp the rotation angle within the allowed range.
    _currentAngle = Mathf.Clamp(_currentAngle, minAngle, maxAngle);

    // Create a rotation about the desired axis using the clamped angle.
    // You could use Quaternion.Euler; I chose this route to support diagonal axes too.
    var rotation : Quaternion = Quaternion.AngleAxis(_currentAngle, rotationAxis);

    // Apply the rotation to our object.
    // Using localRotation means we don't override rotation from the parent hierarchy.
    // Putting `rotation` last means we rotate around the object's local axes.
    transform.localRotation = _initialRotation * rotation;
}
\$\endgroup\$
3
  • \$\begingroup\$ Now if only I could translate it to Javascript :D \$\endgroup\$ Jan 14, 2017 at 11:55
  • \$\begingroup\$ It's a pretty direct replacement, as you can see. No "if only" required. Just take public float foo = 123 and make it var foo : float = 123, add a private for anything that's not public, replace void with function and change string to String. \$\endgroup\$
    – DMGregory
    Jan 14, 2017 at 16:27
  • \$\begingroup\$ Works brilliantly! \$\endgroup\$ Jan 14, 2017 at 18:48
0
\$\begingroup\$

You could have an if statement in update that is like this: if (EulerAngles.x >= maximumx) { EulerAngles.x == maximumx; } then, vise versa for the minimum rotation, but have it at a less than.

\$\endgroup\$
2
0
\$\begingroup\$

You basically have it, but you need to set the rotation instead of adding to it. Try this.

transform.rotation = Quaternion.Euler(0, 0, Mathf.Clamp (transform.eulerAngles.z + Input.GetAxis("Mouse X")*sensitivityX, -30, 30));

Edit: Also, don't forget to multiply the input value by Time.deltaTime to make the rate of rotation framerate independent.

\$\endgroup\$
6
  • 1
    \$\begingroup\$ This is close, but transform.rotation.z gives you the z component of the rotation Quaternion, not the euler angle representation. This can give you unexpected results, since the xyz components of a Quaternion don't directly map to angles. You probably want transform.eulerAngles.z instead. \$\endgroup\$
    – DMGregory
    Jan 14, 2017 at 0:15
  • \$\begingroup\$ Thanks for pointing that out. I've updated my answer accordingly. \$\endgroup\$
    – wenright
    Jan 14, 2017 at 2:27
  • \$\begingroup\$ This gives me interesting output. Turret is pointing upwards in the same direction no matter how I drive the tank, which I think is changed easily enough, but it seems like as soon as I go back to angle 0 the turret clips back to 10 or something, and doesn't want to go to -30. \$\endgroup\$ Jan 14, 2017 at 10:47
  • \$\begingroup\$ transform.rotation = Quaternion.Euler(transform.eulerAngles.x, transform.eulerAngles.y, Mathf.Clamp(transform.eulerAngles.z + xPos, -30, 30)); ------ is what I currently have, and it gives me the same kind of 360 rotation as before. Any ideas? \$\endgroup\$ Jan 14, 2017 at 13:52
  • \$\begingroup\$ xPos = Input.GetAxis("Mouse X")*sensitivityX*Time.deltaTime \$\endgroup\$ Jan 14, 2017 at 14:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .