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更新代码,且未考虑速度作为填充速率的设计。
修复代码
- 在
CharacterData中新增时间条进度变量:
public float timeGaugeProgress; // 时间条当前进度,范围0-100
- 修改
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; // 初始化进度 }
- 调整
CharacterUIData.Init中的时间条最大值:
// Limit and Time Bar setup. physicUI.limitSlider.maxValue = maxLimit; physicUI.limitSlider.value = 0f; physicUI.speedSlider.maxValue = 100f; // 固定进度上限为100 physicUI.speedSlider.value = 0f;
- 重写
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; } }
- 修改
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
相关产品推荐
相关产品推荐

