Need help clamping rotations to a range
So to start off this problem has existed for months, and I'm no closer to fixing it now than I was long ago.
I've got a GIF of the problem for you guys to watch:
And video:
Anyway, the code I've written tries to clamp the angle to a range. I think the problem is with the SwivelGun.cs script. The problem? When the guns cross the X or Y screen axis they "spazz" out. Its unpredictable and impossible to really tell whats happening because it occurs so suddenly and at a quick rate.
My code: (SwivelGuns.cs)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class SwivelGuns : MonoBehaviour
{
List<GameObject> barrels = new List<GameObject>(); //Refers to gun barrel objects
List<GameObject> bases = new List<GameObject>(); //Refers to gun base objects
List<GameObject> bulletSpawns = new List<GameObject>(); //Refers to barrel bullet spawn points
List<GameObject> gunLookers = new List<GameObject>(); //Refers to the invisible (empty) game objects which look directly at the target whose rotations are used as a base to tell the guns where to aim.
GunController shipsTarget; //I forgot
public float xMin, xMax, yMin, yMax; //Range of rotation for these guns on which (local) axes
public float angleDelta; //How fast they turn
Gun[] gunScript; //Scripts attached to each gun. They do stuff. :P
// Use this for initialization
void Awake()
{
bases = GunController.getChildObjectsWithTag(gameObject, "GunBase");
barrels = GunController.getChildObjectsWithTag(gameObject, "GunBarrel");
bulletSpawns = GunController.getChildObjectsWithTag(gameObject, "BulletSpawn");
gunScript = gameObject.GetComponentsInChildren<Gun>();
gunLookers = GunController.getChildObjectsWithTag(gameObject, "GunLooker");
shipsTarget = gameObject.GetComponentInParent<GunController>() as GunController;
if(xMin < 0f) //If negative
{
xMin = 360f + xMin; //Fix to positive (at same unit circle position)
}
if (xMax > 360f) //If greater than one full circle
{
xMax -= 360f;
}
if (yMin < 0f) //If negative
{
yMin = 360f + yMin; //Fix to positive (at same unit circle position)
}
if (yMax > 360f) //If greater than one full circle
{
yMax -= 360f;
}
}
public static float clampAngle (float ang, float min, float max)
{ //Clamps the angle to the specified range! DOESN'T work perfectly
bool angIsPositive = ang >= 0f && ang <= min;
bool angIsNegative = ang >= max && ang <= 360f;
if (angIsPositive)
{
return Mathf.Clamp(ang, 0f, min); //Clamp the angle to between zero and min
} else if (angIsNegative)
{
return Mathf.Clamp(ang, max, 360f); //Clamp angle between max and 360
} else //Outside of range!
{
print("Angle exceeded acceptable range!");
return 0f;
}
}
/* OLD CODE float n = Mathf.Round(ang / 360f); //Find how ang goes in to 360
// print("Angle is: " + ang); //ENABLE this code to see the bug.
if (ang >= 360f || ang <= -360f) //If above 360
{
if (ang < 0f) //If negative
{
ang += n * 360f; //Add
} else //If positive
{
ang -= (360f * n); //Force the value down to less than 360
}
// print("Angle was multiple of 360. Value is now: " + ang);
}
if (ang <= max && ang >= min) //If within the range of the min and max
{
return ang;
} else
{
// print("BEFORE CHANGING ANGLE: "+ ang);
if (ang >= max)
{
ang = max;
}
else if (ang <= min)
{
ang = min;
} //If the angle value is further along than half way assign it the max value, otherwise give it the min.
// print("AFTER changing angle: " + ang);
return ang;
}*/
// Update is called once per frame
void Update()
{
swivelGuns();
}
void swivelGuns()
{
float x, y; //X and Y values may be different SIGNS for different gun mounting positions (but there are only four possible positions)
Utilities.MOUNT_NORMAL gunNormal; //The MOUNT_NORMAL is an ENUM which specifies which way a given turret is mounted locally (UP, DOWN, LEFT, RIGHT)
for (int i = 0; i != bulletSpawns.Count; i++) //Iterate through each gun changing its aim and stuff
{
gunNormal = gunScript[i].normal; //Get the normal for this gun
bulletSpawns[i].transform.LookAt(shipsTarget.worldTarget);
gunLookers[i].transform.LookAt(shipsTarget.worldTarget);
x = gunLookers[i].transform.localEulerAngles.x; //X value here is up/down from camera's perspective!
y = gunLookers[i].transform.localEulerAngles.y; //Y is left/right
//print("Weapon: " + bulletSpawns[i].name + "====" );
// print("[A] X= " + x + " Y= " + y);
if (gunNormal == Utilities.MOUNT_NORMAL.UP)
{
// print("[--ATTN--] Aiming guns for upwards mount!");
// print("[BEFORE] Value of x is: " + x + "Value of y is: " + y);
x = clampAngle(x, xMin, xMax);
y = clampAngle(y, yMin, yMax);
//print("[AFTER] Value of x is: " + x + "Value of y is: " + y);
bases[i].transform.localEulerAngles = Vector3.Slerp(bases[i].transform.localEulerAngles,
new Vector3(0f, y, 0f), Time.deltaTime * angleDelta);
barrels[i].transform.localEulerAngles = Vector3.Slerp(barrels[i].transform.localEulerAngles,
new Vector3(x, 0f, 0f), Time.deltaTime * angleDelta);
}
else if (gunNormal == Utilities.MOUNT_NORMAL.RIGHT)
{
// print("[ATTN] Aiming guns for rightwards mount!");
x = clampAngle(x, xMin, xMax);
y = clampAngle(y, yMin, yMax);
bases[i].transform.localEulerAngles = Vector3.Slerp(bases[i].transform.localEulerAngles,
new Vector3( 0f, y,0f), Time.deltaTime * angleDelta);
barrels[i].transform.localEulerAngles = Vector3.Slerp(barrels[i].transform.localEulerAngles,
new Vector3(x, 0f, 0f), Time.deltaTime * angleDelta);
}
else if (gunNormal == Utilities.MOUNT_NORMAL.LEFT)
{
// print("[ATTN] Aiming guns for leftwards mount!");
x = clampAngle(x, xMin, xMax);
y = clampAngle(-y, yMin, yMax);
bases[i].transform.localEulerAngles = Vector3.Slerp(bases[i].transform.localEulerAngles,
new Vector3(0f, y, 0f), Time.deltaTime * angleDelta);
barrels[i].transform.localEulerAngles = Vector3.Slerp(barrels[i].transform.localEulerAngles,
new Vector3(x, 0f, 0f), Time.deltaTime * angleDelta);
}
else if (gunNormal == Utilities.MOUNT_NORMAL.DOWN)
{
// print("[ATTN] Aiming guns for downwards mount!");
// print("[BEFORE] Value of x is: " + x + "Value of y is: " + y);
x = clampAngle(-x, xMin, xMax);
y = clampAngle(-y, yMin, yMax);
// print("[AFTER] Value of x is: " + x + "Value of y is: " + y);
bases[i].transform.localEulerAngles = Vector3.Slerp(bases[i].transform.localEulerAngles,
new Vector3(0f, y, 0f), Time.deltaTime * angleDelta);
barrels[i].transform.localEulerAngles = Vector3.Slerp(barrels[i].transform.localEulerAngles,
new Vector3(x, 0f, 0f), Time.deltaTime * angleDelta);
}
// print("[B] X= " + x + " Y= " + y);
}
}
}
The only gun normals in use in this particular ship configuration are UP and DOWN.
If you need more information, please ask.
I believe the problem occurs when the guns are pointed "straight" ahead. I mean that is when they are at or close to zero. On the unit circle zero is essentially 360 degrees, which makes me believe they're getting reset to either of those values any time they reach that position.
Hard to tell, but: a common problem is you can't set the rotation, and then read eulerAngles. If you want to directly monitor x,y,z rotation, keep them as global floats, change them directly, and use them only to set rotation -- never read them back.
The problem is a rotation has more than one legal eulerAngles. It won't always give you back the same numbers x,y,z you used to set it.
Hey I found a way to at least get the spazmatic behavior of my guns to be prevented, though clamping the rotations seems to be above my head at the moment.
I'm pretty proud of it too. I managed to solve the turret ai$$anonymous$$g problem without copying select values from other objects and assigning them, ins$$anonymous$$d I do some simple but specific trigonometry to program where the guns ought to aim! Its a problem I never thought I'd be good enough to solve.
Yeah I kinda figured that was the case in one way or another.
Is there ANY way to do this though? I just need to make the guns restricted to a range of angles.