缩放与旋转周期结束时如何暂停游戏及协程?
Unity游戏无法暂停问题排查与修复
问题描述
我尝试在缩放旋转完成后暂停游戏,先后试过以下操作但均无效:
- 在代码末尾添加
Time.timeScale = 0f; yield break; - 将
yield return null;替换为yield return new WaitForEndOfFrame(); - 单独使用
Time.timeScale = 0f;,后续添加break;和StopAllCoroutines();
完整脚本如下:
using System.Collections; using Unity.VisualScripting; using UnityEngine; public class Scaling : MonoBehaviour { public GameObject objectToScale; public float duration = 1f; public Vector3 minSize; public Vector3 maxSize; public bool scaleUp = false; public Coroutine scaleCoroutine; public bool automatic = false; public bool coroutineIsRunning = false; private Quaternion endRotationScaleDown; bool rotationAndScalingComplete = false; private void Start() { } private void Update() { if (automatic) { if (!coroutineIsRunning) { Scale(); } } else { if (Input.GetKeyDown(KeyCode.G)) { Scale(); } } } private void Scale() { scaleUp = !scaleUp; if (scaleCoroutine != null) { StopCoroutine(scaleCoroutine); } scaleCoroutine = StartCoroutine(ScaleOverTime(objectToScale, scaleUp ? maxSize : minSize, duration, scaleUp)); } private IEnumerator ScaleOverTime(GameObject targetObj, Vector3 toScale, float duration, bool isScalingUp) { float counter = 0; Vector3 startScaleSize = targetObj.transform.localScale; Quaternion startRotation = targetObj.transform.rotation; Quaternion endRotationScaleUp = Quaternion.Euler(0, 0, 0); // Example end rotation when scaling up Quaternion endRotationScaleDown = Quaternion.Euler(0, 180, 0); // Example end rotation when scaling down // Generate a random angle (0 or 1) int randomAngle = Random.Range(0, 2); // Set the rotation angle based on the random value float t = (randomAngle == 0) ? 180f : -180f; // Choose the end rotation based on the scaling direction. Quaternion endRotation = isScalingUp ? endRotationScaleUp : Quaternion.Euler(0, t, 0); targetObj.transform.rotation = endRotation; coroutineIsRunning = true; while (counter < duration) { counter += Time.deltaTime; float lerpFactor = counter / duration; targetObj.transform.localScale = Vector3.Lerp(startScaleSize, toScale, lerpFactor); targetObj.transform.rotation = Quaternion.Lerp(startRotation, endRotation, lerpFactor); if (counter >= duration) { coroutineIsRunning = false; // Ensuring the final state matches exactly targetObj.transform.localScale = toScale; targetObj.transform.rotation = endRotation; rotationAndScalingComplete = true; } yield return null; } if (rotationAndScalingComplete) { Time.timeScale = 0f; yield break; } } }
问题根源
- 成员变量状态未重置:
rotationAndScalingComplete是类级成员变量,一旦被设为true后不会自动重置,后续协程会直接触发暂停逻辑,但实际第一次循环内就已经将其设为true,导致逻辑时序混乱。 - 暂停逻辑位置错误:暂停代码放在
while循环之后,但循环结束时counter >= duration已经成立,此时协程已经完成大部分逻辑,且变量状态异常导致暂停逻辑执行时机不对。
修复方案
方案1:改用局部变量并调整暂停逻辑位置
将rotationAndScalingComplete改为协程内的局部变量,并在缩放旋转完成时直接执行暂停逻辑:
private IEnumerator ScaleOverTime(GameObject targetObj, Vector3 toScale, float duration, bool isScalingUp) { float counter = 0; Vector3 startScaleSize = targetObj.transform.localScale; Quaternion startRotation = targetObj.transform.rotation; Quaternion endRotationScaleUp = Quaternion.Euler(0, 0, 0); // 放大时目标旋转 // 随机生成旋转角度(180°或-180°) float t = Random.Range(0, 2) == 0 ? 180f : -180f; Quaternion endRotation = isScalingUp ? endRotationScaleUp : Quaternion.Euler(0, t, 0); coroutineIsRunning = true; while (counter < duration) { counter += Time.deltaTime; float lerpFactor = counter / duration; targetObj.transform.localScale = Vector3.Lerp(startScaleSize, toScale, lerpFactor); targetObj.transform.rotation = Quaternion.Lerp(startRotation, endRotation, lerpFactor); if (counter >= duration) { coroutineIsRunning = false; // 确保最终状态完全匹配目标值 targetObj.transform.localScale = toScale; targetObj.transform.rotation = endRotation; // 缩放旋转完成后立即暂停 Time.timeScale = 0f; yield break; // 终止协程 } yield return null; } }
方案2:保留成员变量但重置状态
如果必须使用类成员变量rotationAndScalingComplete,需要在协程启动时强制重置其状态:
private IEnumerator ScaleOverTime(GameObject targetObj, Vector3 toScale, float duration, bool isScalingUp) { rotationAndScalingComplete = false; // 每次协程启动时重置状态 // 剩余原有代码... }
额外优化建议
Time.timeScale = 0仅会暂停依赖时间的逻辑(如Time.deltaTime驱动的动画),但Update函数和协程仍会按帧率执行。如果需要完全屏蔽输入,可添加全局暂停标志:
public bool isPaused = false; private void Update() { if (isPaused) return; // 暂停时不执行任何交互逻辑 // 原有Update代码... }
在设置Time.timeScale = 0时同步设置isPaused = true即可。
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

