I have a script where I wish to rotate something on the X-axis, and I'd expect the other two axes to stay put and not be modified(they flip between 0 and 180), and only X to change.
Below you can see the code intended to do just that.
public class TestScript : MonoBehaviour
{
private void Start()
{
//this line is meant to show that I'm reseting the rotation before starting
transform.eulerAngles = Vector3.zero;
Debug.Log($"start rotation: {transform.eulerAngles}");
}
private void Update()
{
float time = (Time.time - 1f) * 10f;
if (time < 0f || time > 1f)
{
return;
}
Vector3 rotation = transform.eulerAngles;
float x = Mathf.Lerp(170f, 0, time);
rotation.x = x;
transform.eulerAngles = rotation;
Debug.Log($"rotation: {transform.eulerAngles}, x: {x}");
}
}
The output from the console. You can clearly see that the rotation does not go from 170 to 0, but from 0 to 90 and then back to 0.
Now, I'm pretty sure this has something to do with quaternions and their identity, but not sure how can this be avoided
PS: Before you answer please read this
- I do not want to rotate the whole object by using a Vector. I only want to rotate one axis at a time.
- I know that this is due to the fact that quaternions represent a rotation, and there are multiple ways to represent a rotation when converting into euler angles, but that doesn't help me, because I do only want to do one axis rotation and the others not to be modified. At all.
- I'm actually trying to do this for the other 2 axes too.
- The rotation has to be from one value to another during a specific period of time, rather than rotate a certain amount over an unspecified amount of time.
- This script is not actually what I'm trying to achieve but a simplified version of my issue. If any of this is not that clear, check these 2 scripts, where I'm actually trying to achieve this. Abstract Axis Rotate
CodePudding user response:
I would store a Vector3 where all 1-3 TestScript varieties can view and edit it, such that no Transform has more than one of these vector3 associated with it. This could be stored in a dictionary, for instance, where each Transform could be used as a key. There are other ways this could be done but that is out of scope. I'll call the value of this vector3 cache for the sake of simplicity.
You will also need a way for each script to determine which components of the euler angles are not controlled. This can be done in a manner similar to cache and how to implement is also out of scope for this question.
Have each TestScript update cache so that the vector component it controls is set to its current interpolation value. Then have each component assign that cache to the eulerAngles, or optionally, have only the final TestScript make that assignment.
In terms of your question, a good one btw, I'd say 181, 0, 360. I don't really care what you do with the other axes and if your quaternion messes up the rotation.
Since you would want to have this internal state vector not override uncontrolled vector components in the event any outside change occurs, here's what you could do about that:
When the first TestScript variety in a frame executes, compare (using e.g. Quaternion.Angle(a,b) < 0.00001f so that it treats 540,0,0 the same as 180,0,0) the transform's current rotation with the quaternion produced by Quaternion.Euler(cache). If the rotations are determined to be different, assign the uncontrolled components of the transform's eulerAngles to cache and then continue with the usual operation.
Pseudocode, underscore_case stuff is VERY pseudo:
Transform myTransform;
Vector3 startEulers = myTransform.eulerAngles;
Vector3 cache = get_cache(myTransform);
if (is_this_TestScript_first_to_go_this_frame())
{
if (Quaternion.Angle(myTransform.rotation, Quaternion.Euler(cache)) < 0.0001f)
{
if (is_x_uncontrolled(myTransform))
cache.x = startEulers.x;
if (is_y_uncontrolled(myTransform))
cache.y = startEulers.y;
if (is_z_uncontrolled(myTransform))
cache.z = startEulers.z;
set_cache(myTransform, cache);
}
}
switch(my_axis)
{
case X:
cache.x = get_current_interp_val();
break;
case Y:
cache.y = get_current_interp_val();
break;
case Z:
cache.z = get_current_interp_val();
break;
default:
throw exception;
}
myTransform.eulerAngles = cache;
This will get you started toward what you want.
CodePudding user response:
This cannot be achieved due to how Unity interprets quaternions. If you want to do this you might want to go a different route. transform.Rotate(Vector3) did the trick for me. This is not the solution I'm using but this is how the script I originally posted would have to be modified to achieve the same thing.
public class TestScript : MonoBehaviour
{
private float lastX;
private void Start()
{
transform.eulerAngles = Vector3.zero;
lastX = transform.eulerAngles.x;
Debug.Log($"start rotation: {transform.eulerAngles}");
}
private void Update()
{
float time = Time.time - 1f;
if (time < 0 || time > 1f)
{
return;
}
float x = Mathf.Lerp(0f, -170f, time);
Vector3 rotation = new Vector3(x - lastX, 0f, 0f);
lastX = x;
transform.Rotate(rotation);
Debug.Log($"rotation: {transform.eulerAngles}, x: {x}");
}
}

