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

判断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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 16:23:25