Unity 3D Quaternion Rotations:帮助我避免 Gimbal lock 并进行平滑旋转
Unity 3D Quaternion Rotations: Help me to avoid Gimbal lock and make a smoth rotation
我目前正在开发一款手机游戏,它是一个相当简单的核心机制,你从一个 3D 立方体开始,每一轮你都会得到一个新的立方体,需要将其放置或捕捉到一个人的脸上场景中的立方体。您可以 select 场景中立方体的不同面,预览立方体将在该面附近生成,因此您可以旋转它并找到所需的位置。当您对自己的选择感到满意时,只需点击 "build cube button" 预览立方体就会消失,具有相同值和旋转度的新立方体将出现在 selected 面上。
我只需要将对象围绕 selected 轴旋转 90 或 -90 度。就是这样,但是没用呵呵,如果有人可以帮助我,那将是很多,这困扰了我好几天了。谢谢!
public class CubeBehaviour : MonoBehaviour
{
private Movement _movement;
private void Awake()
{
if (!GetComponent<Movement>())
Debug.LogWarning("Please add a movement script to the gameobject.");
_movement = GetComponent<Movement>();
}
public void Move(Vector3[] positions, Action callBack)
{
_movement.StartMove(positions,callBack);
}
[ContextMenu("Rotate X Positive")]
public void RotateAround_xAxisPositive()
{
Rotate(RotationAxis.X, true);
}
[ContextMenu("Rotate X Negative")]
public void RotateAround_xAxisNegative()
{
Rotate(RotationAxis.X, false);
}
[ContextMenu("Rotate Y Positive")]
public void RotateAround_yAxisPositive()
{
Rotate(RotationAxis.Y, true);
}
[ContextMenu("Rotate Y Negative")]
public void RotateAround_yAxisNegative()
{
Rotate(RotationAxis.Y, false);
}
[ContextMenu("Rotate Z Positive")]
public void RotateAround_zAxisPositive()
{
Rotate(RotationAxis.Z, true);
}
[ContextMenu("Rotate Z Negative")]
public void RotateAround_zAxisNegative()
{
Rotate(RotationAxis.Z, false);
}
public void Rotate(RotationAxis axis, bool positiveRotation)
{
StartCoroutine(DoRotation(axis,90, positiveRotation));
}
IEnumerator DoRotation(RotationAxis axis,float angles, bool positiveRotation)
{
float time = 1;
float elapsedTime = 0;
Quaternion destinationRotation = Quaternion.identity;
Vector3 anglesToRotate = Vector3.zero;
switch (axis)
{
case RotationAxis.X:
if (positiveRotation)
anglesToRotate = new Vector3(90, 0, 0);
else
anglesToRotate = new Vector3(-90, 0, 0);
break;
case RotationAxis.Y:
if (positiveRotation)
anglesToRotate = new Vector3(0, 90, 0);
else
anglesToRotate = new Vector3(0, -90, 0);
break;
case RotationAxis.Z:
if (positiveRotation)
anglesToRotate = new Vector3(0, 0, 90);
else
anglesToRotate = new Vector3(0, 0, -90);
break;
default:
Debug.LogError("You must assign a rotation axis!");
break;
}
destinationRotation *= this.transform.rotation * Quaternion.Euler(anglesToRotate);
Quaternion yRotation = Quaternion.identity;
Quaternion xRotation = Quaternion.identity;
Quaternion zRotation = Quaternion.identity;
while (elapsedTime < time)
{
yRotation = Quaternion.AngleAxis(anglesToRotate.y * Time.deltaTime, Vector3.up);
xRotation = Quaternion.AngleAxis(anglesToRotate.x * Time.deltaTime, Vector3.right);
zRotation = Quaternion.AngleAxis(anglesToRotate.z * Time.deltaTime, Vector3.forward);
this.transform.rotation = yRotation * xRotation * zRotation * this.transform.rotation;
elapsedTime += Time.deltaTime;
yield return null;
}
//this.transform.rotation = yRotation * xRotation * zRotation;
//this.transform.rotation = destinationRotation;
}
}
public enum RotationAxis
{
X,
Y,
Z
}
你永远不应该像这样设置旋转,它根本不起作用,Unity 文档也会对此发出警告。
this.transform.rotation = yRotation * xRotation * zRotation * this.transform.rotation;
如果您一次旋转一个轴,则可以使用。
transform.Rotate(90.0f ,0.0f,0.0f,Space.World);
https://docs.unity3d.com/ScriptReference/Transform.Rotate.html
您也可以使用 LookAt
transform.LookAt(targetCube);
https://docs.unity3d.com/ScriptReference/Transform.LookAt.html
您也可以使用Quaternion.Lerp
https://docs.unity3d.com/ScriptReference/Quaternion.Lerp.html
一般来说,Unity 中的旋转可能很棘手,尤其是当它们在检查器中看起来很容易时。
public class TestROtate : MonoBehaviour
{
[SerializeField] float duration = 1; // seconds, must be >0.0f
Quaternion targetRotation = Quaternion.identity;
[ContextMenu("Rotate X Positive")]
public void RotateAround_xAxisPositive()
{
targetRotation *= Quaternion.Euler(90, 0, 0);
StopAllCoroutines();
StartCoroutine(DoRotation());
}
[ContextMenu("Rotate Y Positive")]
public void RotateAround_yAxisPositive()
{
targetRotation *= Quaternion.Euler(0, 90, 0);
StopAllCoroutines();
StartCoroutine(DoRotation());
}
[ContextMenu("Rotate Z Positive")]
public void RotateAround_zAxisPositive()
{
targetRotation *= Quaternion.Euler(0, 0, 90);
StopAllCoroutines();
StartCoroutine(DoRotation());
}
// other rotate methods
IEnumerator DoRotation()
{
float currentTime = 0;
Quaternion startRotation = transform.rotation;
while (currentTime < 1)
{
currentTime += Time.deltaTime / duration;
transform.rotation = Quaternion.Slerp(startRotation, targetRotation,
currentTime);
yield return null;
}
transform.rotation = targetRotation;
}
}
我目前正在开发一款手机游戏,它是一个相当简单的核心机制,你从一个 3D 立方体开始,每一轮你都会得到一个新的立方体,需要将其放置或捕捉到一个人的脸上场景中的立方体。您可以 select 场景中立方体的不同面,预览立方体将在该面附近生成,因此您可以旋转它并找到所需的位置。当您对自己的选择感到满意时,只需点击 "build cube button" 预览立方体就会消失,具有相同值和旋转度的新立方体将出现在 selected 面上。 我只需要将对象围绕 selected 轴旋转 90 或 -90 度。就是这样,但是没用呵呵,如果有人可以帮助我,那将是很多,这困扰了我好几天了。谢谢!
public class CubeBehaviour : MonoBehaviour
{
private Movement _movement;
private void Awake()
{
if (!GetComponent<Movement>())
Debug.LogWarning("Please add a movement script to the gameobject.");
_movement = GetComponent<Movement>();
}
public void Move(Vector3[] positions, Action callBack)
{
_movement.StartMove(positions,callBack);
}
[ContextMenu("Rotate X Positive")]
public void RotateAround_xAxisPositive()
{
Rotate(RotationAxis.X, true);
}
[ContextMenu("Rotate X Negative")]
public void RotateAround_xAxisNegative()
{
Rotate(RotationAxis.X, false);
}
[ContextMenu("Rotate Y Positive")]
public void RotateAround_yAxisPositive()
{
Rotate(RotationAxis.Y, true);
}
[ContextMenu("Rotate Y Negative")]
public void RotateAround_yAxisNegative()
{
Rotate(RotationAxis.Y, false);
}
[ContextMenu("Rotate Z Positive")]
public void RotateAround_zAxisPositive()
{
Rotate(RotationAxis.Z, true);
}
[ContextMenu("Rotate Z Negative")]
public void RotateAround_zAxisNegative()
{
Rotate(RotationAxis.Z, false);
}
public void Rotate(RotationAxis axis, bool positiveRotation)
{
StartCoroutine(DoRotation(axis,90, positiveRotation));
}
IEnumerator DoRotation(RotationAxis axis,float angles, bool positiveRotation)
{
float time = 1;
float elapsedTime = 0;
Quaternion destinationRotation = Quaternion.identity;
Vector3 anglesToRotate = Vector3.zero;
switch (axis)
{
case RotationAxis.X:
if (positiveRotation)
anglesToRotate = new Vector3(90, 0, 0);
else
anglesToRotate = new Vector3(-90, 0, 0);
break;
case RotationAxis.Y:
if (positiveRotation)
anglesToRotate = new Vector3(0, 90, 0);
else
anglesToRotate = new Vector3(0, -90, 0);
break;
case RotationAxis.Z:
if (positiveRotation)
anglesToRotate = new Vector3(0, 0, 90);
else
anglesToRotate = new Vector3(0, 0, -90);
break;
default:
Debug.LogError("You must assign a rotation axis!");
break;
}
destinationRotation *= this.transform.rotation * Quaternion.Euler(anglesToRotate);
Quaternion yRotation = Quaternion.identity;
Quaternion xRotation = Quaternion.identity;
Quaternion zRotation = Quaternion.identity;
while (elapsedTime < time)
{
yRotation = Quaternion.AngleAxis(anglesToRotate.y * Time.deltaTime, Vector3.up);
xRotation = Quaternion.AngleAxis(anglesToRotate.x * Time.deltaTime, Vector3.right);
zRotation = Quaternion.AngleAxis(anglesToRotate.z * Time.deltaTime, Vector3.forward);
this.transform.rotation = yRotation * xRotation * zRotation * this.transform.rotation;
elapsedTime += Time.deltaTime;
yield return null;
}
//this.transform.rotation = yRotation * xRotation * zRotation;
//this.transform.rotation = destinationRotation;
}
}
public enum RotationAxis
{
X,
Y,
Z
}
你永远不应该像这样设置旋转,它根本不起作用,Unity 文档也会对此发出警告。
this.transform.rotation = yRotation * xRotation * zRotation * this.transform.rotation;
如果您一次旋转一个轴,则可以使用。
transform.Rotate(90.0f ,0.0f,0.0f,Space.World);
https://docs.unity3d.com/ScriptReference/Transform.Rotate.html
您也可以使用 LookAt
transform.LookAt(targetCube);
https://docs.unity3d.com/ScriptReference/Transform.LookAt.html
您也可以使用Quaternion.Lerp
https://docs.unity3d.com/ScriptReference/Quaternion.Lerp.html
一般来说,Unity 中的旋转可能很棘手,尤其是当它们在检查器中看起来很容易时。
public class TestROtate : MonoBehaviour
{
[SerializeField] float duration = 1; // seconds, must be >0.0f
Quaternion targetRotation = Quaternion.identity;
[ContextMenu("Rotate X Positive")]
public void RotateAround_xAxisPositive()
{
targetRotation *= Quaternion.Euler(90, 0, 0);
StopAllCoroutines();
StartCoroutine(DoRotation());
}
[ContextMenu("Rotate Y Positive")]
public void RotateAround_yAxisPositive()
{
targetRotation *= Quaternion.Euler(0, 90, 0);
StopAllCoroutines();
StartCoroutine(DoRotation());
}
[ContextMenu("Rotate Z Positive")]
public void RotateAround_zAxisPositive()
{
targetRotation *= Quaternion.Euler(0, 0, 90);
StopAllCoroutines();
StartCoroutine(DoRotation());
}
// other rotate methods
IEnumerator DoRotation()
{
float currentTime = 0;
Quaternion startRotation = transform.rotation;
while (currentTime < 1)
{
currentTime += Time.deltaTime / duration;
transform.rotation = Quaternion.Slerp(startRotation, targetRotation,
currentTime);
yield return null;
}
transform.rotation = targetRotation;
}
}