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

Unity Slider仅以0.01增量更新,与设定值不符的技术问题

问题描述

我是编程新手,跟着2018年的教程(2023年中上传至YouTube)制作《最终幻想7》风格的回合制战斗系统,使用Unity 2022.3.23f1版本。UI搭建阶段遇到两个异常:

  • 角色速度设为4时,时间条(Time Gauge)仅增加4次0.01左右的增量(约4%)就停止增长;
  • 极限条(Limit Gauge)完全不增长。

手动在Unity编辑器中给滑块赋值时显示正常,但通过代码变量驱动时出现问题。HP和MP滑块工作正常,仅速度和极限滑块使用浮点值curProgress传递更新,怀疑与此有关。试过调整编辑器内滑块参数、开启整数模式等方法均无效,请求技术帮助。

附教程提供的完整代码:

using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CharacterControl : MonoBehaviour
{
    public CharacterData characterData;
    public CharacterControl targetData;

    private void Start()
    {
        characterData.Init();
        characterData._target = targetData.characterData;
        StartCoroutine(characterData.CharacterLoop());
    }
    private void Update()
    {
        if (characterData.ActionReady)
        {
            Debug.Log("Is ready to attack");
            if (characterData.characterTeam == CharacterData.CharacterTeam.Friendly && Input.GetKeyDown(KeyCode.Space))
            {
                Debug.Log(characterData.charName + " did an action");
                if (characterData._target.CanAttackTarget)
                {
                    characterData.Attack();
                }
            }
        }
    }
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[System.Serializable]
public class CharacterData
{
    public string charName;
    [Space(10)]
    public CharacterUIData charUI;
    [Space(10)]
    public int maxHealth, curHealth, maxMagicPoints, curMagicPoints;
    [Space(10)]
    public float maxLimitPoints, curLimitPoints, maxSpeed, curSpeed;
    [Space(10)]
    public CharacterState characterState;
    public CharacterTeam characterTeam;
    [Space(10)]
    public UnityEvent onAttack;
    public UnityEvent onWasAttacked;
    public bool playerAttacked;
    [Space(16)]
    public CharacterData _target;

    public void Init()
    {
        if(characterTeam == CharacterTeam.Friendly)
        charUI.Init(maxHealth, curHealth, maxMagicPoints, curMagicPoints, charName, maxSpeed, maxLimitPoints);

        onAttack.AddListener(CharacterAttack);
        onWasAttacked.AddListener(CharacterWasAttacked);
        characterState = CharacterState.Idle;
    }

    public void Attack()
    {
        onAttack.Invoke();
        _target.Damage(10);
    }

    public void Damage(int damageAmount)
    {
        curHealth -= damageAmount;
        if (curHealth <= 0)
        {
            curHealth = 0;
            characterState = CharacterState.Defeated;
        }

        if (characterTeam == CharacterTeam.Friendly)
        {
            curLimitPoints += 5;
            curLimitPoints = Mathf.Clamp(curLimitPoints, 0, maxLimitPoints);
            charUI.UpdateTimeBar(curLimitPoints);
        }
        onWasAttacked.Invoke();
    }

    public bool CanBeAttacked
    {
        get { return characterState == CharacterState.Idle || characterState == CharacterState.Ready; }
    }
    public bool CanAttackTarget
    {
        get { return _target.characterState == CharacterState.Idle || _target.characterState == CharacterState.Ready; }
    }

    public bool ActionReady
    {
        get { return curSpeed >= maxSpeed; }
    }

    void CharacterAttack()
    {
        Debug.Log(_target.charName + " was attacked.");
        playerAttacked = false;
        curSpeed = 0;
    }

    void CharacterWasAttacked()
    {
        Debug.Log(charName + " was attacked.");
    }

    public IEnumerator CharacterLoop()
    {
        while (characterState != CharacterState.Defeated)
        {
            if (curSpeed >= maxSpeed)
            {
                curSpeed = maxSpeed;
                characterState = CharacterState.Ready;
            }
            else
            {
                curSpeed += Time.deltaTime;
                charUI.UpdateTimeBar(curSpeed);
                characterState = CharacterState.Idle;
            }
            yield return null;
        }
    }

    [System.Serializable]
    public class CharacterUIData
    {
        public RowUI physicUI;
        public int placeInUI = 1;

        public void Init(int maxHealth, int curHealth, int maxMagic, int curMagic, string charName, float maxLimit, float maxSpeed)
        {
            placeInUI = UIManager.curUICount;
            if (placeInUI == 1)
            {
                physicUI = UIManager.Instance.defaultRowUI;
            }
            else
            {
                UIManager.Instance.SpawnRow(out physicUI);
            }

            physicUI.healthSlider.maxValue = maxHealth;
            physicUI.healthSlider.value = curHealth;
            physicUI.healthUI.text = curHealth + "/ " + maxHealth.ToString();

            physicUI.magicSlider.maxValue = curMagic;
            physicUI.magicSlider.value = curMagic;
            physicUI.magicUI.text = curMagic.ToString();

            physicUI.characterUI.text = charName.ToString();

            physicUI.limitSlider.maxValue = maxLimit;
            physicUI.limitSlider.value = 0f;

            physicUI.speedSlider.maxValue = maxSpeed;
            physicUI.speedSlider.value = 0f;

            UIManager.curUICount++;
        }

        public void UpdateTimeBar(float curProgress)
        {
            physicUI.speedSlider.value = curProgress;
        }

        public void UpdateLimitBar(float curProgress)
        {
            physicUI.limitSlider.value = curProgress;
        }
    }

    public enum CharacterTeam
    {
        Friendly,
        Enemy
    }
    public enum CharacterState
    {
        Loading,
        Idle,
        Ready,
        Attacked,
        Attacking,
        Defeated
    }
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class RowUI : MonoBehaviour
{
    public Slider healthSlider;
    public TextMeshProUGUI healthUI;
    public Slider magicSlider;
    public TextMeshProUGUI magicUI;
    public Slider limitSlider;
    public Slider speedSlider;
    public TextMeshProUGUI characterUI;
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class UIManager : MonoBehaviour
{
    public static UIManager Instance;
    public Transform rowHolder;
    public Transform nameHolder;

    [Header("UI Prefabs")]
    public GameObject rowPrefab;
    public GameObject namePrefab;

    [Header("First Player UI")]
    public RowUI defaultRowUI;

    public static int curUICount = 1;

    private void Awake()
    {
        Instance = this;
    }
    public void SpawnRow(out RowUI processedUI)
    {
        GameObject tmpRow = Instantiate(rowPrefab);
        tmpRow.transform.SetParent(rowHolder, false);
        RowUI RowTmpInfo = tmpRow.GetComponent<RowUI>();

        GameObject tmpName = Instantiate(namePrefab);
        tmpName.transform.SetParent(nameHolder, false);
        TextMeshProUGUI txtName = tmpName.GetComponent<TextMeshProUGUI>();

        RowTmpInfo.characterUI = txtName;
        processedUI = RowTmpInfo;
    }
}

问题分析与修复方案

1. 时间条停止增长问题

核心原因

  • 逻辑混淆:把「角色速度属性」当成了「时间条进度上限」,导致curSpeed += Time.deltaTime的增长逻辑完全不符合FF7回合制的时间条机制(应该是速度越快,时间条填充越快);
  • UI更新遗漏:当curSpeed达到maxSpeed进入Ready状态后,不再执行UI更新代码,且未考虑速度作为填充速率的设计。

修复代码

  1. 在CharacterData中新增时间条进度变量:
public float timeGaugeProgress; // 时间条当前进度,范围0-100
  1. 修改Init方法,初始化时间条参数:
public void Init()
{
    if(characterTeam == CharacterTeam.Friendly)
    charUI.Init(maxHealth, curHealth, maxMagicPoints, curMagicPoints, charName, maxLimitPoints, maxSpeed);

    onAttack.AddListener(CharacterAttack);
    onWasAttacked.AddListener(CharacterWasAttacked);
    characterState = CharacterState.Idle;
    timeGaugeProgress = 0; // 初始化进度
}
  1. 调整CharacterUIData.Init中的时间条最大值:
// Limit and Time Bar setup.
physicUI.limitSlider.maxValue = maxLimit;
physicUI.limitSlider.value = 0f;

physicUI.speedSlider.maxValue = 100f; // 固定进度上限为100
physicUI.speedSlider.value = 0f;
  1. 重写CharacterLoop的时间条增长逻辑:
public IEnumerator CharacterLoop()
{
    while (characterState != CharacterState.Defeated)
    {
        if (timeGaugeProgress >= 100)
        {
            timeGaugeProgress = 100;
            characterState = CharacterState.Ready;
            charUI.UpdateTimeBar(timeGaugeProgress); // 确保Ready状态UI同步
        }
        else
        {
            // 按速度属性决定填充速率,速度越快进度增长越快
            timeGaugeProgress += maxSpeed * Time.deltaTime;
            charUI.UpdateTimeBar(timeGaugeProgress);
            characterState = CharacterState.Idle;
        }
        yield return null;
    }
}
  1. 修改ActionReady判断和CharacterAttack的重置逻辑:
public bool ActionReady
{
    get { return timeGaugeProgress >= 100; }
}

void CharacterAttack()
{
    Debug.Log(_target.charName + " was attacked.");
    playerAttacked = false;
    timeGaugeProgress = 0; // 重置时间条进度
    charUI.UpdateTimeBar(timeGaugeProgress);
}

2. 极限条不增长问题

核心原因

在Damage方法中调用了错误的UI更新方法——把更新极限条的逻辑错误调用成了UpdateTimeBar(更新时间条),导致极限条从未被赋值。

修复代码

修改CharacterData.Damage中的极限条更新语句:

//Handling Limit Bar progression.
if (characterTeam == CharacterTeam.Friendly)
{
    curLimitPoints += 5;
    curLimitPoints = Mathf.Clamp(curLimitPoints, 0, maxLimitPoints);
    charUI.UpdateLimitBar(curLimitPoints); // 替换为正确的极限条更新方法
}

内容的提问来源于stack exchange,提问作者Chase Makes Art

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 23:15:53