You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity长按按键时持续消耗Stamina的实现问题求助

解决Unity长按按键持续消耗耐力(Stamina)的冻结问题

问题根源

你遇到的冻结是因为协程中无暂停的循环会阻塞主线程,另外你对按键检测的理解有误——Update是每帧执行的,完全可以用来检测按键的长按状态,之前的单次触发逻辑不适合持续消耗场景。

解决方案

我们需要:

  • 在Update中检测按键的按住状态,启动/停止持续消耗的协程
  • 用带暂停的协程实现平滑的耐力持续减少
  • 确保消耗与回复逻辑互斥,避免冲突

修改后的完整代码

using System.Collections;
using UnityEngine;
using UnityEngine.UI;

public class Stamina : MonoBehaviour
{
    public Slider staminaBar;
    private int maxStamina = 100;
    private int currentStamina;
    public static Stamina instance;
    
    // 耐力消耗与回复的时间间隔(控制平滑度)
    private WaitForSeconds consumeTick = new WaitForSeconds(0.1f);
    private WaitForSeconds regenTick = new WaitForSeconds(0.1f);
    
    // 记录当前的消耗/回复协程,防止重复启动
    private Coroutine activeConsumeCoroutine;
    private Coroutine activeRegenCoroutine;

    private void Awake()
    {
        instance = this;
    }

    void Start()
    {
        currentStamina = maxStamina;
        staminaBar.maxValue = maxStamina;
        staminaBar.value = maxStamina;
    }

    void Update()
    {
        // 检测Shift键长按:按住时启动消耗协程,松开时停止
        if (Input.GetKey(KeyCode.LeftShift))
        {
            if (activeConsumeCoroutine == null && currentStamina > 0)
            {
                activeConsumeCoroutine = StartCoroutine(ConsumeStaminaOverTime(5));
            }
        }
        else
        {
            if (activeConsumeCoroutine != null)
            {
                StopCoroutine(activeConsumeCoroutine);
                activeConsumeCoroutine = null;
                
                // 停止消耗后启动回复协程
                StartRegenStamina();
            }
        }
    }

    // 持续消耗耐力的协程
    private IEnumerator ConsumeStaminaOverTime(int amountPerTick)
    {
        while (currentStamina > 0)
        {
            currentStamina -= amountPerTick;
            // 防止耐力低于0
            currentStamina = Mathf.Max(currentStamina, 0);
            staminaBar.value = currentStamina;
            
            // 每0.1秒扣一次,保证主线程不阻塞
            yield return consumeTick;
        }
        
        activeConsumeCoroutine = null;
        Debug.Log("耐力耗尽!");
        
        // 耗尽后停止消耗,启动回复
        StartRegenStamina();
    }

    // 启动耐力回复(带延迟)
    private void StartRegenStamina()
    {
        if (activeRegenCoroutine != null)
        {
            StopCoroutine(activeRegenCoroutine);
        }
        activeRegenCoroutine = StartCoroutine(RegenStamina());
    }

    private IEnumerator RegenStamina()
    {
        // 回复前的延迟
        yield return new WaitForSeconds(2);

        while (currentStamina < maxStamina)
        {
            // 每次回复1点(或按比例调整)
            currentStamina += maxStamina / 100;
            currentStamina = Mathf.Min(currentStamina, maxStamina);
            staminaBar.value = currentStamina;
            yield return regenTick;
        }
        
        activeRegenCoroutine = null;
    }
}

关键修改点说明

  • 按键检测逻辑:移到Update中,通过Input.GetKey检测长按状态,GetKey会在按键按住的每帧返回true,完美适配持续消耗场景
  • 持续消耗协程:ConsumeStaminaOverTime中每0.1秒扣一次耐力,通过yield return让主线程有时间处理其他逻辑,彻底解决冻结问题
  • 协程管理:用activeConsumeCoroutine和activeRegenCoroutine记录当前运行的协程,防止重复启动导致的逻辑混乱
  • 边界处理:添加Mathf.Max和Mathf.Min保证耐力值不会超出0~max的范围

内容的提问来源于stack exchange,提问作者dumprogramor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 08:15:34