My problem is I want to be able to rotate a cylinder 9 times. 360/9 is 40 so I all I should need to do is rotate by 40 degrees 9 times. This doesn’t work however as when I rotate the cylinder for the first time instead of by 40 degrees it rotates by 39.99 degrees. This happens at other rotations as well.
I’m rotating by doing this.
if(Input.GetKeyUp("i"))
transform.GetChild(m_Section).Rotate(0,40.0f,0);
I have unity version 3.4 it is not pro and I’m coding in C#.
Any help appreciated as I have just started trying to learn unity.
Unity3D stores rotations in a pretty abstract mathematical representation called quaternion. Tranformation from and to Euler angles (what you see in Unity editor) involves a cascade of trigonometric functions and thus is prone to rounding errors especially for the simple float type.
To get around this problem in your case I recommend storing the initial Quaternion object before starting to rotate and set it at the end of the process. Some pseudo-code:
public class RotationController : MonoBehaviour {
Quaternion rotationAtStart;
int numOfRotations = 0;
void rotate () {
numOfRotations++;
if (numOfRotations == 1) {
rotationAtStart = transform.rotation;
} else if (numOfRotations < 9) {
transform.Rotate (new Vector3 (0,40.0f,0));
} else if (numOfRotations == 9) {
transform.rotation = rotationAtStart;
}
}
void Update () {
if (numOfRotations < 9) {
rotate ();
}
}
}
The special situation of 360° makes this approach stable. For less than 360° you have to live with small rounding errors. For this case I would recommend calculating the target quaternion Quaternion.RotateTowards and set it in the last step analog to the 360 case.
Another useful thing for you are Animations. You can define an animation as smooth or in discrete steps and just call GameObject.animation.Play("MyRotation") if "i" is pressed. Use an AnimationEvent at the end to get informed when the animation is terminated.
And at last Mathf contains a function Approximately that deals with the problem of floating point imprecision.
Have a look at the answer of Kungfooman in this post, he descripes the problem with the rotation over 90 degree or at 90 degree as well as 270 degree. He provides an Extension which will always calculate the correct pendant of the Quaternion for the value you want to set. Have a look here:
using UnityEngine;
using System.Collections;
public class ExtensionVector3 : MonoBehaviour {
public static float CalculateEulerSafeX(float x){
if( x > -90 && x <= 90 ){
return x;
}
if( x > 0 ){
x -= 180;
} else {
x += 180;
}
return x;
}
public static Vector3 EulerSafeX(Vector3 eulerAngles){
eulerAngles.x = CalculateEulerSafeX(eulerAngles.x);
return eulerAngles;
}
}
And I used it like this:
Quaternion newQuat = m_directionalLight.transform.rotation;
Vector3 nL = new Vector3(ExtensionVector3.CalculateEulerSafeX(xNewValueLight),
0,
0);
newQuat.eulerAngles = nL;
m_directionalLight.transform.rotation =
Quaternion.Lerp(m_directionalLight.transform.rotation,
newQuat,
Time.deltaTime);
Related
I wanted to a cube do not rotate more than 60 degrees so it dosn't rotate to more
I tried to use this
if(gameObject.GetComponent<Transform>().rotation.x >= 60 || gameObject.GetComponent<Transform>().rotation.x <= -60 || gameObject.GetComponent<Transform>().rotation.z >= 60 || gameObject.GetComponent<Transform>().rotation.z <= -60)
{
gameObject.GetComponent<Transform>().rotation = new Quaternion(0, 0, 0, 0);
}
to check the rotation.
It didn't work so I print what rotation is getting in the x coordinate
and it said cordinates like:
5.98324, 7.39482, -1.983495
and I was just moving the x position not the rotation and It change it.
So how do I get the x rotation that it sais in the component transform?
I think the way you approached is fine but not suitable for the scenario. Euler Angles should be handled in order to control the Transformation.
simple understanding be like:
// declare your cube rotation
float moveSpeed = 5f // speed in which cube rotates
float rotatecube = moveSpeed* Time.deltaTime* 10;
//Your condition be like
if(transform.eulerAngles.z < (.....declare the requirement.....))
transform.Rotate(vector3.forward * rotateCube);
If you still face any issue related to angles console.log() is the better way for self understandings.
Hope it clarifies..
You are interested in the euler angles of the rotation, not the quaternion components of the rotation. You should start by referring to transform.eulerAngles.x instead of transform.rotation.x or transform.eulerAngles.z instead of transform.rotation.z.
By the way, it's best to call GetComponent as few times as you can get away with as it's an expensive operation. You should rather call it once and assign the result to a variable such as this:
Transform cubeTransform = gameObject.GetComponent<Transform>();
if(cubeTransform.eulerAngles.x ...)
{
...
}
Additionally, you don't even need to use GetComponent to access the transform of the gameObject the script is attached to. Instead, you can just use transform, e.g.:
if ( transform.eulerAngles.x >= 60 || transform.eulerAngles.x <= -60
|| transform.eulerAngles.z >= 60 || transform.eulerAngles.z <= -60)
{
transform.rotation = Quaternion.identity;
}
Basicaly, you want to make limit of rotation of any axis - x,y, or z.
if yes, then here is the solution :
Program:
Rotating A cube not more that 60 degree in x and z only.
var rotValue += Time.deltaTime;
Cube.transform.rotaion = Quaternion.Eular(Mathf.Clamp(rotaValue,0,60), 0, Mathf.Clamp(rotaValue,0,60));
you can use Mathf.clamp for Clamping any Float value Between min and max.
hope, problem solved.
Skip all the above comments. I found a way to get the exact rotation values as in INSPECTOR. I am showing for angle (x). The code is as follows:
public Transform GameObject;
void Update()
{
float Rotation;
if(GameObject.eulerAngles.x <= 180f)
{
Rotation = GameObject.eulerAngles.x;
}
else
{
Rotation = GameObject.eulerAngles.x - 360f;
}
}
This question already has an answer here:
Unity3d floating point precision limitations
(1 answer)
Closed 3 years ago.
I am trying to make a game about flying a spaceship to different planets and moons. Something like Kerbal Space Program but in 2d. When I go more than 100000 units from the center of the world under the transform of the rocket there would appear a message/error(Due to floating point precision limitations, it is recommended to bring the world coordinates of the gameobject within a smaller range) and the script for calculating the distance to the nearest planet would stop working and display a 0. Is there a way to make 2d worlds larger than 100000 units?
The Script for the distance to the nearest planet;
{
GameObject Planet;
Vector2 Direction;
public float Height;
public int HeightINT;
public Text text;
public bool isKM;
void Start()
{
Planet = GameObject.FindGameObjectWithTag("PlanetCentre");
}
void Update()
{
Direction = (Planet.transform.position - transform.position);
RayCastHeight();
if(Height >= 1000)
{
text.text = (HeightINT/1000).ToString() + " KM";
}
if(Height < 1000)
{
text.text = HeightINT.ToString() + " M";
}
}
void RayCastHeight()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, Direction);
Height = (hit.distance * 5);
HeightINT = Mathf.RoundToInt(Height);
}
} ```
First what comes to my mind, I wuld maybe recommend you to store your distance as string, and then parsing it to int. But I'm not sure, how it will all work. Hope this helps a bit.
I'm a bit confused about some documentation for Unity pertaining to Euler angles. I just want to know if I'm not understanding a difference, or the sample does not follow the best practice. The documentation here
states:
Only use this variable to read and set the angles to absolute values. Don't increment them, as it will fail when the angle exceeds 360 degrees. Use Transform.Rotate instead.
Meanwhile, the code sample appears to be using increments that could exceed 360 degrees:
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour {
public float yRotation = 5.0F;
void Update() {
yRotation += Input.GetAxis("Horizontal");
transform.eulerAngles = new Vector3(10, yRotation, 0);
}
void Example() {
print(transform.eulerAngles.x);
print(transform.eulerAngles.y);
print(transform.eulerAngles.z);
}
}
Wouldn't incrementing a variable and then using that variable to set the value absolutely still run the risk of exceeding 360 degrees if the variable is over 360 degrees?
When you rotate your Object using euler angles then when it reaches 360 and try to exceed further, it becomes (minus)-360 and gradually increase from -359 to -1.
After executing following code your values will not exceed from 360 and will remain positive.
float rotateAngle = rotateObject.transform.localEulerAngles.y;
// Convert negative angle to positive angle
rotateAngle = (rotateAngle > 180) ? rotateAngle - 360 : rotateAngle;
rotateObject.transform.localEulerAngles = new Vector3(rotateObject.transform.localEulerAngles.x, rotateAngle, rotateObject.transform.localEulerAngles.z);
There are differences. When doing:
transform.eulerAngles.y += 1F;
You are invoking the += operator in Vector3.
However, when you set eulerAngles this way:
float newY = transform.eulerAngles.y + 1F;
transform.eulerAngles = new Vector3(.., newY, ..);
You are invoking a setter in Transform, and inside this setter, it probably includes the action of updating Transform.rotation.
The difference is that Unity can implement the updating logic in Transform class instead of the Vector3 class, which makes much more sense there.
We can verify this further below in the documentation:
Do not set one of the eulerAngles axis separately (eg. eulerAngles.x =
10; ) since this will lead to drift and undesired rotations. When
setting them to a new value set them all at once as shown above. Unity
will convert the angles to and from the rotation stored in
Transform.rotation.
I want to rotate an object with the left arrow key and I want to bound the rotation to 30 deg. My code is:
if (Input.GetKey(KeyCode.LeftArrow) && transform.localEulerAngles.z <= 30)
transform.Rotate(0,0,1);
Why rotation stops to 31 deg?
Obviously my problem is more complex than this, I have different bounds and I need precision. The reason of this example is to simply say that rotations are not precise if managed in this way.
I think the reason is that Unity3D internally uses quaternions and acting on degrees is just an approximation. I'm right? In this last case how can I cope to this?
For example, how can I use quaternions to bound of 30 degs a rotation on an axis?
By the way if the problem is not this, do you have other solutions?
I don't know how unity manage the rotation, but here your problem seem more simple.
In your if you use the '<=' comparison, so when your object is at 30 degree, you enter a last time in the if and rotate 1 more degree, use a '<' to stop at the right moment
Get the current rotation in the Start() function then use it to find an offset that will be used to perform the if statement. This should do it:
public GameObject gameObjectToRotate;
Vector3 defaultAngle;
float minRot = 30f;
float maxRot = 30f;
// Use this for initialization
void Start()
{
defaultAngle = gameObjectToRotate.transform.eulerAngles;
}
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.LeftArrow))
{
float offset = defaultAngle.z - gameObjectToRotate.transform.eulerAngles.z;
//Check if we are within min, max rot
if (offset < minRot && offset > -maxRot)
{
gameObjectToRotate.transform.Rotate(0, 0, 1);
Debug.Log("Rotating!");
}
}
}
The precision is 30.016. This is much more better than what you are getting now.
I am using the Leap Motion device to get usable data on the position and orientation of my hand. At the moment I am having trouble with the orientation segment of the coding. The Leap API only has code for frame by frame rotation, however, it also provides a normal vector (normal to the palm of the hand) and a pointer vector (pointing in the direction from the palm outwards towards the fingers). These two vectors are perpendicular.
The vectors:
Leap.Vector normal = current.PalmNormal;
Leap.Vector pointer = current.Direction;
More information can be found on the Leap Hand API: https://developer.leapmotion.com/documentation/Languages/CSharpandUnity/API/class_leap_1_1_hand.html
Converting to Unity Vector3 class:
Vector3 normalU = new Vector3();
normalU.x = normal.x;
normalU.y = normal.y;
normalU.z = normal.z;
Vector3 pointerU = new Vector3();
pointerU.x = pointer.x;
pointerU.y = pointer.y;
pointerU.z = pointer.z;
I use these to vectors to calculate the Euler Angles orientation of my hand (a rotation of theta_x degrees about the x-axis, theta_y degrees about the y-axis, and theta_z degrees about the z-axis) using the code:
float rXn = Mathf.Atan (normalU.y/normalU.z);
rXn *= (180f/Mathf.PI);
if ((normalU.y < 0 && normalU.z < 0) || (normalU.y > 0 && normalU.z < 0))
{
rXn += 180f;
}
float rZn = Mathf.Atan (normalU.y/normalU.x);
rZn *= (180f/Mathf.PI);
if ((normalU.y < 0 && normal.x > 0) || (normalU.y > 0 && normalU.x > 0))
{
rZn += 180f;
}
float rYn = Mathf.Atan (pointerU.x/pointerU.z);
rYn *= (180f/Mathf.PI);
if ((pointerU.x > 0 && pointerU.z > 0) || (pointerU.x < 0 && pointerU.z > 0))
{
rYn += 180f;
}
The Euler Angles are then converted to a Quaternion and implemented using the code:
Quaternion rotation = Quaternion.Euler (-rZn+90, -rYn, rXn+90);
rigidbody.MoveRotation (rotation);
More information on the Unity Quaternion class can be found here: http://docs.unity3d.com/Documentation/ScriptReference/Quaternion.html
As I coded this, I tested each axis of rotation individually, commenting out the others (setting them to 0), and they worked properly. However, when I implemented all three at once, the behaviors of rotations around an individual axis changed, which confuses me. Why would including recognition of rotation about the y-axis change the way rotation about the x-axis occurs?
As each individual axis of rotation worked when the others were commented out (and set to 0), I think the problem lies in the way the Euler Angles are converted to a Quaternion. I do not have a great understanding of the way Quaternions are used to represent rotations, however I am confused as to why changing the value of the angle of rotation about the y-axis would change the angle of rotation about the x-axis.
Thanks for your help.
The order of rotation is relevant, and this might be what causes your confusion. Imagine a point on the x-axis at (1, 0, 0). When we now do a rotation of 90° around the x axis, nothing happens. Then we do a rotation of 90° around the y axis, which makes the point lie on the positive z-axis. If we change the order of rotation, the point will end on the y axis. Depending on the way your functions are implemented, they require a certain order of rotation to get the expected results.
It's not perfect, but I am getting pretty good results with:
private void UpdateHandNormal( Hand hand, Transform marker )
{
float y = Mathf.Atan( hand.Direction.x / hand.Direction.z );
if( float.IsNaN( y ) ) y = 0;
marker.localRotation = new Quaternion( hand.PalmNormal.z, -y, hand.PalmNormal.x, 1 ) ;
}
Where hand is the Hand instance from the leap controller and marker is a simple rectangle representing the hand rotation.
I was getting NaN for y so I added the set to 0 check.
ath
J.