英文:
My Quaternion rotation coroutine slows down towards the end?
问题
我有一个协程,当用户输入一个触发器然后按下特定的键时启动。整体上旋转是有效的,但在旋转末尾开始变得非常慢。我在使用Vector3.Lerp时遇到过这个问题,但我通过使用speed * Time.deltaTime和另一个变量来使其平滑化来解决了这个问题。我尝试根据Unity文档中的Quaternion.RotateTowards使用一步,但它根本不会旋转。
void Update()
{
if (Input.GetKey(KeyCode.Z))
{
Debug.Log("Raycast.");
if (Physics.Raycast(transform.position, Vector3.down, 1.5f))
{
StartCoroutine(Turn(cubeObject));
}
}
}
IEnumerator Turn(Transform cubeObject)
{
Quaternion targetRotation = Quaternion.identity;
do
{
Debug.Log("Begin rotating.");
Vector3 targetDirection = cubeObject.position - transform.position;
targetRotation = Quaternion.LookRotation(targetDirection);
Quaternion nextRotation = Quaternion.RotateTowards(
transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
transform.localEulerAngles = new Vector3(0, nextRotation.eulerAngles.y, 0);
yield return null;
} while (Quaternion.Angle(transform.rotation, targetRotation) == 0f);
}
如果我将最后一行的最后一个值(Quaternion.Angle)更改为大于0的任何值,旋转永远不会结束。我将被锁定在旋转状态,直到退出运行时。我只想在按下适当的键时旋转一次完全朝向目标。
英文:
I have a coroutine that is initiated when the user enters a trigger and then presses a specific key. The rotating as a whole works, but towards the end of the rotation it begins to rotate much, much slower. I have had this issue when using Vector3.Lerp but I fixed that by using speed * Time.deltaTime and another variable to make it smooth. I tried to use a step as per the Quaternion.RotateTowards unity docs but it would just not rotate at all.
void Update()
{
if (Input.GetKey(KeyCode.Z))
{
Debug.Log("Raycast.");
if (Physics.Raycast(transform.position, Vector3.down, 1.5f))
{
StartCoroutine(Turn(cubeObject));
}
}
}
IEnumerator Turn(Transform cubeObject)
{
Quaternion targetRotation = Quaternion.identity;
do
{
Debug.Log("Begin rotating.");
Vector3 targetDirection = cubeObject.position - transform.position;
targetRotation = Quaternion.LookRotation(targetDirection);
Quaternion nextRotation = Quaternion.RotateTowards(
transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
transform.localEulerAngles = new Vector3(0, nextRotation.eulerAngles.y, 0);
yield return null;
} while (Quaternion.Angle(transform.rotation, targetRotation) == 0f);
}
And if I change the very last value on the last line (Quaternion.Angle) to anything greater than 0, the rotation never ends. I will be locked in rotation until I exit runtime. I'm only trying to rotate fully towards the target just once when the appropriate key is pressed.
答案1
得分: 1
以下是您要翻译的内容:
"As said there is a combination of a couple of issues here
-
First you potentially start a lot of concurrent ("simultaneously" happening) Coroutines! You should only start ONE and either make sure you don't start a second one or interrupt an already running one:
private Coroutine currentRoutine; void Update() { // Use GetkeyDown to check of the first frame the key went down // rather than a continuous holding down of the button if (Input.GetKeyDown(KeyCode.Z)) { Debug.Log("Raycast."); if (Physics.Raycast(transform.position, Vector3.down, 1.5f)) { if (currentRoutine != null) StopCoroutine(currentRoutine); currentRoutine = StartCoroutine(Turn(cubeObject)); } } } IEnumerator Turn(Transform cubeObject) { ... currentRoutine = null; } -
Then you are mixing the global world space direction
targetDirectionand also world spacetransform.rotationwith a local spacetransform.localEulerAngles. You want to either do all calculation in local or global space - don't mix! -
Then I think the slowing down can be explained by the fact that your
targetDirectionmight have an offset in theYaxis.You do
RotateTowardswhich rotates into the target rotation on all three axes - but then throw away X/Z and only apply the Y component.=> The closer your rotation comes to the final
targetRotation(potentially never fully reaching it btw) the more you see the effect since the Y component of the rotation is already almost "correct" so it now tries to rotate more and more on the X/Z components - the ones you ignore.In order t solve this (and in general) I would recommend to stick to
Quaternionas much as possible and rather already fix the target directiontargetDirction.y = 0; targetRotation = Quaternion.LookRotation(targetDirection); transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime); yield return null; -
And finally never use
==for comparingfloat! You could rather useQuaternion ==which uses a reduced precision of1e-5while(transform.rotation != targetRotation); // and then make sure to end up with clean values transform.rotation = targetRotation;or alternatively if you want to stick to the angle an be more precise use
Mathf.Approximatelywhile(!Mathf.Apprximately(Quaternion.Angle(transform.rotation, targetRotation), 0f));which basically equals doing
while(Quaternion.Angle(transform.rotation, targetRotation) > Mathf.Epsilon);"
英文:
As said there is a combination of a couple of issues here
-
First you potentially start a lot of concurrent ("simultaneously" happening) Coroutines! You should only start ONE and either make sure you don't start a second one or interrupt an already running one:
private Coroutine currentRoutine; void Update() { // Use GetkeyDown to check of the first frame the key went down // rather than a continuous holding down of the button if (Input.GetKeyDown(KeyCode.Z)) { Debug.Log("Raycast."); if (Physics.Raycast(transform.position, Vector3.down, 1.5f)) { if (currentRoutine != null) StopCoroutine(currentRoutine); currentRoutine = StartCoroutine(Turn(cubeObject)); } } } IEnumerator Turn(Transform cubeObject) { ... currentRoutine = null; } -
Then you are mixing the global world space direction
targetDirectionand also world spacetransform.rotationwith a local spacetransform.localEulerAngles. You want to either do all calculation in local or global space - don't mix! -
Then I think the slowing down can be explained by the fact that your
targetDirectionmight have an offset in theYaxis.You do
RotateTowardswhich rotates into the target rotation on all three axes - but then throw away X/Z and only apply the Y component.=> The closer your rotation comes to the final
targetRotation(potentially never fully reaching it btw) the more you see the effect since the Y component of the rotation is already almost "correct" so it now tries to rotate more and more on the X/Z components - the ones you ignore.In order t solve this (and in general) I would recommend to stick to
Quaternionas much as possible and rather already fix the target directiontargetDirction.y = 0; targetRotation = Quaternion.LookRotation(targetDirection); transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime); yield return null; -
And finally never use
==for comparingfloat! You could rather useQuaternion ==which uses a reduced precision of1e-5while(transform.rotation != targetRotation); // and then make sure to end up with clean values transform.rotation = targetRotation;or alternatively if you want to stick to the angle an be more precise use
Mathf.Approximatelywhile(!Mathf.Apprximately(Quaternion.Angle(transform.rotation, targetRotation), 0f));which basically equals doing
while(Quaternion.Angle(transform.rotation, targetRotation) > Mathf.Epsilon);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论