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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 18:53:17