Unity+C#中如何使左Shift按下/释放时耐力条长度一致?
问题根源分析
你的代码有两个核心问题导致耐力条异常变长:
- 重复启动协程:在
FixedUpdate里每次都调用StartCoroutine(SprintPlayer()),会生成大量并行协程。释放左Shift后,这些协程都会等待1秒,然后同时执行stamina += Time.deltaTime,直接把耐力刷爆甚至超过最大值,导致条变长。 - 协程逻辑错误:原恢复逻辑只执行一次
stamina += Time.deltaTime,没有持续恢复;同时输入检测放在协程里也不靠谱,协程不是每帧都处理输入。
修复方案
按以下步骤修改代码:
1. 调整初始化逻辑
把耐力条的初始化移到Start里,避免每次按Shift重复创建资源:
void Start () { walkSpeed = moveSpeed; runSpeed = walkSpeed * 1.5f; // 初始化耐力条UI staminaRect = new Rect(Screen.width / 10, Screen.height * 9 / 10, Screen.width / 3, Screen.height / 50); staminaTexture = new Texture2D(1, 1); staminaTexture.SetPixel(0, 0, Color.white); staminaTexture.Apply(); }
2. 把输入检测移到Update
FixedUpdate用于处理物理,输入检测放在Update里更合适,同时控制协程只启动一次:
// 新增标记,防止重复启动恢复协程 bool isRecoveringStamina = false; void Update() { if (Input.GetKeyDown(KeyCode.LeftShift)) { SetRunning(true); // 如果正在恢复耐力,停止协程 if (isRecoveringStamina) { StopCoroutine(RecoverStamina()); isRecoveringStamina = false; } } if (Input.GetKeyUp(KeyCode.LeftShift)) { SetRunning(false); // 只有耐力没满且没在恢复时,才启动恢复协程 if (stamina < maxStamina && !isRecoveringStamina) { StartCoroutine(RecoverStamina()); } } // 处理冲刺时的耐力消耗 if (isRunning) { stamina -= Time.deltaTime; stamina = Mathf.Clamp(stamina, 0, maxStamina); // 确保耐力不低于0 if (stamina <= 0) { SetRunning(false); // 耐力耗尽后自动启动恢复 if (!isRecoveringStamina) { StartCoroutine(RecoverStamina()); } } } }
3. 拆分出独立的恢复耐力协程
单独写一个协程处理延迟恢复和持续恢复逻辑:
IEnumerator RecoverStamina() { isRecoveringStamina = true; // 等待1秒 yield return new WaitForSeconds(1); // 持续恢复直到耐力满 while (stamina < maxStamina) { stamina += Time.deltaTime * 2; // 可调整恢复速度,*2让恢复比消耗快 stamina = Mathf.Clamp(stamina, 0, maxStamina); // 确保不超过最大值 yield return null; // 每帧执行一次 } isRecoveringStamina = false; }
4. 删除冗余代码
原FixedUpdate和SprintPlayer逻辑已拆分到Update和新协程里,直接删掉这两部分代码。
5. 优化OnGUI绘制
确保耐力条不会超过最大值:
void OnGUI() { float ratio = Mathf.Clamp01(stamina / maxStamina); // 限制ratio在0-1之间 float rectWidth = ratio * Screen.width / 3; staminaRect.width = rectWidth; GUI.DrawTexture(staminaRect, staminaTexture); }
修改后完整代码
float stamina = 5, maxStamina = 5; float walkSpeed, runSpeed; bool isRunning; Rect staminaRect; Texture2D staminaTexture; bool isRecoveringStamina = false; // 假设moveSpeed是你已定义的变量 float moveSpeed = 5; void Start () { walkSpeed = moveSpeed; runSpeed = walkSpeed * 1.5f; // 初始化耐力条UI staminaRect = new Rect(Screen.width / 10, Screen.height * 9 / 10, Screen.width / 3, Screen.height / 50); staminaTexture = new Texture2D(1, 1); staminaTexture.SetPixel(0, 0, Color.white); staminaTexture.Apply(); } void Update() { if (Input.GetKeyDown(KeyCode.LeftShift)) { SetRunning(true); if (isRecoveringStamina) { StopCoroutine(RecoverStamina()); isRecoveringStamina = false; } } if (Input.GetKeyUp(KeyCode.LeftShift)) { SetRunning(false); if (stamina < maxStamina && !isRecoveringStamina) { StartCoroutine(RecoverStamina()); } } if (isRunning) { stamina -= Time.deltaTime; stamina = Mathf.Clamp(stamina, 0, maxStamina); if (stamina <= 0) { SetRunning(false); if (!isRecoveringStamina) { StartCoroutine(RecoverStamina()); } } } } void SetRunning(bool isRunning) { this.isRunning = isRunning; moveSpeed = isRunning ? runSpeed : walkSpeed; } IEnumerator RecoverStamina() { isRecoveringStamina = true; yield return new WaitForSeconds(1); while (stamina < maxStamina) { stamina += Time.deltaTime * 2; stamina = Mathf.Clamp(stamina, 0, maxStamina); yield return null; } isRecoveringStamina = false; } void OnGUI() { float ratio = Mathf.Clamp01(stamina / maxStamina); float rectWidth = ratio * Screen.width / 3; staminaRect.width = rectWidth; GUI.DrawTexture(staminaRect, staminaTexture); }
修改后可实现:
- 按下左Shift时正常消耗耐力,耐力条正确缩短
- 释放左Shift后延迟1秒开始持续恢复,直到耐力满,不会出现条异常变长的情况
- 耐力始终被限制在0到maxStamina之间,不会溢出
内容的提问来源于stack exchange,提问作者user15365798
相关产品推荐
相关产品推荐

