判断while语句触发状态及Arduino输入速度同步逻辑问题
Arduino速度输入检测逻辑优化问题
我正在编写一个函数,用于检测两个Arduino控制器传入的速度值是否发生变化(通过Arduino控制游戏内速度)。在Update函数中,我通过检测布尔值的true/false切换来决定是否启动协程更新玩家对象的速度,但存在以下困惑:
- 控制while语句触发的布尔值应在哪里修改?是否要放在while语句内?
- _lastPlayerSpeeds变量应在何处更新?
- 以及如何正确启动整个流程?
原代码
using System.Collections; using UnityEngine; namespace BikeRacers { public class ArduinoInputToSpeed : MonoBehaviour { public static ArduinoInputToSpeed Instance; [Header("Player Speeds")] [Range(0.000f, 5.000f)] public float Player1InputSpeed = 0.000f; [Range(0.000f, 5.000f)] public float Player2InputSpeed = 0.000f; private float _lastPlayer1Speed = 0.000f; private float _lastPlayer2Speed = 0.000f; private bool _player1SpeedChanged = false; private bool _player2SpeedChanged = false; void Awake() { if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } } void Start() { StartCoroutine(ArduinoPlayer1SpeedInput()); StartCoroutine(ArduinoPlayer2SpeedInput()); } void Update() { if (Player1InputSpeed != _lastPlayer1Speed) { _player1SpeedChanged = true; } else { _player1SpeedChanged = false; } if (Player2InputSpeed != _lastPlayer2Speed) { _player2SpeedChanged = true; } else { _player2SpeedChanged = false; } } public IEnumerator ArduinoPlayer1SpeedInput() { while (_player1SpeedChanged) { //Player1Follow.GetComponent<PlayerFollow>().speed = Player1Speed / SpeedAdjustmentRatio; Debug.Log("Player 1 Speed: " + Player1InputSpeed); yield return new WaitForSeconds(0.1f); } } public IEnumerator ArduinoPlayer2SpeedInput() { while (_player2SpeedChanged) { //Player2Follow.GetComponent<PlayerFollow>().speed = Player2Speed / SpeedAdjustmentRatio; Debug.Log("Player 2 Speed: " + Player2InputSpeed); yield return new WaitForSeconds(0.1f); } } } }
问题解答与修正方案
1. 布尔值的修改位置
原逻辑的核心问题是布尔值的状态管理混乱:Update每帧切换布尔值,而协程的while循环会因为布尔值的频繁变化出现异常。
正确做法:
- 布尔值仅用于标记"是否需要触发更新",在Update检测到速度变化时设为true;
- 协程执行完速度更新操作后,立即将布尔值重置为false,避免重复触发;
- 不建议用布尔值控制协程的while循环,更可靠的方式是让协程成为持续监听的循环,或在触发后执行单次更新。
2. _lastPlayerSpeeds变量的更新时机
_lastPlayer1Speed和_lastPlayer2Speed必须在完成速度更新操作后赋值,也就是在协程内部执行完玩家速度修改(或Debug输出)后,将当前的PlayerXInputSpeed赋值给对应的_last变量,这样下次检测时才能准确判断是否有新的变化。
3. 流程的正确启动方式
原代码在Start中启动协程,但初始布尔值为false,协程会直接结束,后续速度变化无法触发。推荐两种方案:
方案一:持续监听式协程(推荐)
让协程自身循环检测速度变化,不需要依赖外部布尔值,逻辑更简洁稳定:
using System.Collections; using UnityEngine; namespace BikeRacers { public class ArduinoInputToSpeed : MonoBehaviour { public static ArduinoInputToSpeed Instance; [Header("Player Speeds")] [Range(0.000f, 5.000f)] public float Player1InputSpeed = 0.000f; [Range(0.000f, 5.000f)] public float Player2InputSpeed = 0.000f; private float _lastPlayer1Speed = 0.000f; private float _lastPlayer2Speed = 0.000f; void Awake() { if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } } void Start() { // 启动持续监听的协程 StartCoroutine(MonitorPlayer1Speed()); StartCoroutine(MonitorPlayer2Speed()); } private IEnumerator MonitorPlayer1Speed() { while (true) { if (Player1InputSpeed != _lastPlayer1Speed) { // 执行玩家速度更新逻辑 // Player1Follow.GetComponent<PlayerFollow>().speed = Player1InputSpeed / SpeedAdjustmentRatio; Debug.Log("Player 1 Speed Updated: " + Player1InputSpeed); // 更新上次记录的速度 _lastPlayer1Speed = Player1InputSpeed; } // 保持原逻辑的0.1秒检测间隔 yield return new WaitForSeconds(0.1f); } } private IEnumerator MonitorPlayer2Speed() { while (true) { if (Player2InputSpeed != _lastPlayer2Speed) { // 执行玩家速度更新逻辑 // Player2Follow.GetComponent<PlayerFollow>().speed = Player2InputSpeed / SpeedAdjustmentRatio; Debug.Log("Player 2 Speed Updated: " + Player2InputSpeed); // 更新上次记录的速度 _lastPlayer2Speed = Player2InputSpeed; } yield return new WaitForSeconds(0.1f); } } } }
方案二:触发式协程(保留原逻辑思路)
添加协程运行状态标记,避免重复启动,同时在协程结束后更新_last变量:
using System.Collections; using UnityEngine; namespace BikeRacers { public class ArduinoInputToSpeed : MonoBehaviour { public static ArduinoInputToSpeed Instance; [Header("Player Speeds")] [Range(0.000f, 5.000f)] public float Player1InputSpeed = 0.000f; [Range(0.000f, 5.000f)] public float Player2InputSpeed = 0.000f; private float _lastPlayer1Speed = 0.000f; private float _lastPlayer2Speed = 0.000f; // 标记协程是否正在运行 private bool _isPlayer1CoroutineRunning = false; private bool _isPlayer2CoroutineRunning = false; void Awake() { if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } } void Update() { // 检测到速度变化且协程未运行时,启动协程 if (Player1InputSpeed != _lastPlayer1Speed && !_isPlayer1CoroutineRunning) { StartCoroutine(ArduinoPlayer1SpeedInput()); } if (Player2InputSpeed != _lastPlayer2Speed && !_isPlayer2CoroutineRunning) { StartCoroutine(ArduinoPlayer2SpeedInput()); } } public IEnumerator ArduinoPlayer1SpeedInput() { _isPlayer1CoroutineRunning = true; // 执行速度更新 // Player1Follow.GetComponent<PlayerFollow>().speed = Player1InputSpeed / SpeedAdjustmentRatio; Debug.Log("Player 1 Speed: " + Player1InputSpeed); // 更新上次速度记录 _lastPlayer1Speed = Player1InputSpeed; yield return new WaitForSeconds(0.1f); // 标记协程结束 _isPlayer1CoroutineRunning = false; } public IEnumerator ArduinoPlayer2SpeedInput() { _isPlayer2CoroutineRunning = true; // 执行速度更新 // Player2Follow.GetComponent<PlayerFollow>().speed = Player2InputSpeed / SpeedAdjustmentRatio; Debug.Log("Player 2 Speed: " + Player2InputSpeed); // 更新上次速度记录 _lastPlayer2Speed = Player2InputSpeed; yield return new WaitForSeconds(0.1f); _isPlayer2CoroutineRunning = false; } } }
内容的提问来源于stack exchange,提问作者greyBow
相关产品推荐
相关产品推荐

