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();
}
}
}