using System; using LightGlue.Unity.Bridge; using LightGlue.Unity.Config; using LightGlue.Unity.Networking; using LightGlue.Unity.Python; using UnityEngine; namespace LightGlue.Unity.Game { /// /// LightGlue 全局管理器(跨场景单例)。 /// - 负责管理 HardwareToPythonUdpBridge 与 PythonProcessController 的生命周期(DontDestroyOnLoad)。 /// - Bridge 统一管理:硬件 JPEG 接收、转发 Python、硬件 0x40 控制、Python 结果接收。 /// - 提供统一的结果访问接口(带丢包平滑处理)及暂停/恢复图像传输、启动/停止 Python 的高层封装。 /// public sealed class LightGlueManager : MonoBehaviour { /// /// 全局实例(跨场景唯一)。 /// public static LightGlueManager Instance { get; private set; } [Header("核心组件引用")] [Tooltip("Bridge(硬件接收/转发Python/硬件控制/结果接收)")] public HardwareToPythonUdpBridge bridge; [Tooltip("Python 进程控制器(启动/停止 Python 子进程)")] public PythonProcessController pythonController; [Header("结果丢包与平滑处理")] [Tooltip("当当前帧没有新结果或结果无效时,是否保留上一帧有效结果以减缓UDP丢包带来的抖动。")] public bool holdLastValidOnPacketLoss = true; [Tooltip("在未收到新结果的情况下,最多保留上一帧有效结果的时间(秒)。0 表示永不过期。")] [Min(0f)] public float maxHoldDurationSeconds = 0.5f; [Header("图像传输控制(可选)")] [Tooltip("初始默认图传配置(仅 Awake 时推送给 Bridge 一次)。若场景中有 ImageTransmissionUIController,其 Start 会再覆盖为 UI 的配置;运行时以 Bridge.transmissionConfig 为准,此处仅作缺省值。")] public ImageTransmissionConfig defaultTransmissionConfig; // Bridge 级别的“上一帧有效结果”记录(供丢包平滑使用) private LightGlueResult _lastValidResult; private bool _hasLastValidResult; private float _lastValidTime; // Manager 统一拉取并分发的“当前可用结果”缓存 private LightGlueResult _latestResultFromManager; private bool _hasLatestResultFromManager; /// /// 当 Manager 成功获取到一个可用结果(包括保留的上一帧有效结果)时触发。 /// 由 Manager 在 Update 中统一从 Bridge 拉取一次结果并分发,订阅方无需也不应再直接调用 bridge.TryGetLatestResult。 /// public event Action OnResultUpdated; /// /// 最近一次由 Manager 拉取并确认可用的算法结果是否存在。 /// public bool HasLatestResult => _hasLatestResultFromManager; /// /// 最近一次由 Manager 拉取并确认可用的算法结果内容。 /// public LightGlueResult LatestResult => _latestResultFromManager; private void Awake() { // 单例 + 跨场景常驻:若已有实例则视为重复,先禁用再销毁,避免同帧内 OnEnable 再次绑定端口导致 SocketException if (Instance != null && Instance != this) { gameObject.SetActive(false); Destroy(gameObject); return; } Instance = this; DontDestroyOnLoad(gameObject); // 自动补齐组件引用(推荐把 Bridge、PythonController 放在同一 GameObject 上) if (bridge == null) bridge = GetComponent(); if (pythonController == null) pythonController = GetComponent(); if (bridge == null) { Debug.LogWarning("[LightGlueManager] HardwareToPythonUdpBridge 未设置或未挂载在同一 GameObject 上。"); } // 可选:在启动时将默认图像传输配置同步到 Bridge //if (defaultTransmissionConfig != null && bridge != null) //{ // bridge.SetTransmissionConfig(defaultTransmissionConfig); //} } private void OnDestroy() { if (Instance == this) { Instance = null; } } private void OnApplicationQuit() { // 应用退出时统一停止 Python 与相关 UDP 组件 try { if (bridge != null && bridge.enabled) { bridge.enabled = false; } } catch { // ignore } try { if (pythonController != null && pythonController.IsRunning) { pythonController.StopPython(); } } catch { // ignore } } /// /// 统一的结果获取接口(在 Bridge 基础上增加“上一帧有效结果保留”逻辑)。 /// - 优先返回本帧最新有效结果; /// - 若没有新结果或结果无效,在启用 holdLastValidOnPacketLoss 时返回上一帧有效结果(在 maxHoldDurationSeconds 时间窗口内)。 /// public bool TryGetLatestResult(out LightGlueResult result) { result = LightGlueResult.CreateInvalid(); if (bridge == null) { return false; } // 从 Bridge 获取原始结果(队列非阻塞) bool hasNew = bridge.TryGetLatestResult(out var raw); if (hasNew) { if (raw.IsValid) { // 记录最新有效结果 _lastValidResult = raw; _hasLastValidResult = true; _lastValidTime = Time.time; result = raw; return true; } // 有新结果但标记为无效:根据配置决定是否退回到上一帧有效结果 } // 没有新结果或新结果无效时,按需返回上一帧有效结果 if (holdLastValidOnPacketLoss && _hasLastValidResult) { if (maxHoldDurationSeconds <= 0f || (Time.time - _lastValidTime) <= maxHoldDurationSeconds) { result = _lastValidResult; return true; } } // 没有可用的历史有效结果,则返回原始结果(可能是无效结果或完全无数据) if (hasNew) { result = raw; return true; } return false; } private void Update() { // 由 Manager 统一从 Bridge 拉取一次结果,并通过事件分发给所有订阅方, // 避免多个脚本分别消费 UDP 队列导致拿到的帧不一致。 if (bridge == null) return; if (TryGetLatestResult(out var result)) { _latestResultFromManager = result; _hasLatestResultFromManager = true; OnResultUpdated?.Invoke(result); } } /// /// 暂停图像传输(推荐在主菜单/暂停菜单调用:只关闭 Unity->Python 图像转发,保留进程与端口)。 /// 修改的是 Bridge.transmissionConfig.enableImageTransmission;若存在图传 UI,其显示不会自动刷新。 /// public void PauseImageTransmission() { if (bridge == null) return; var config = bridge.transmissionConfig; if (config == null) { // 若尚未有配置,创建一个默认配置并关闭传输 config = new ImageTransmissionConfig(); } config.enableImageTransmission = false; bridge.SetTransmissionConfig(config); } /// /// 恢复图像传输(重新开启 Unity->Python 图像转发)。 /// 修改的是 Bridge.transmissionConfig.enableImageTransmission;若存在图传 UI,其显示不会自动刷新。 /// public void ResumeImageTransmission() { if (bridge == null) return; var config = bridge.transmissionConfig ?? new ImageTransmissionConfig(); config.enableImageTransmission = true; bridge.SetTransmissionConfig(config); } /// /// 启动 Python 进程(资源优先模式下,可在进入游戏场景时显式调用)。 /// public void StartPythonProcess() { if (pythonController == null) { Debug.LogWarning("[LightGlueManager] PythonProcessController 未设置,无法启动 Python 进程。"); return; } pythonController.StartPython(); } /// /// 停止 Python 进程(例如在主菜单或退出点释放资源)。 /// public void StopPythonProcess() { if (pythonController == null) { return; } pythonController.StopPython(); } } }