英文:
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
targetDirection
and also world spacetransform.rotation
with 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
targetDirection
might have an offset in theY
axis.You do
RotateTowards
which 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
Quaternion
as 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-5
while(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.Approximately
while(!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
targetDirection
and also world spacetransform.rotation
with 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
targetDirection
might have an offset in theY
axis.You do
RotateTowards
which 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
Quaternion
as 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-5
while(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.Approximately
while(!Mathf.Apprximately(Quaternion.Angle(transform.rotation, targetRotation), 0f));
which basically equals doing
while(Quaternion.Angle(transform.rotation, targetRotation) > Mathf.Epsilon);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论