| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- using System;
- using System.Collections;
- using AppUI.Manager.View;
- using SmartBowSDK;
- using UnityEngine;
- using WildAttack;
- namespace AppUI.Bluetooth
- {
- /// <summary>
- /// BluetoothHolder 统一入口:蓝牙、设备档案及后续红外等 SDK 能力。
- /// AppUI 层请通过 <see cref="ins"/> 访问,不要直接引用 BluetoothAim / AimHandler 单例。
- /// </summary>
- [DefaultExecutionOrder(-100)]
- public class SmartBowDeviceHub : MonoBehaviour
- {
- public static SmartBowDeviceHub ins;
- public SmartBowDeviceState State { get; private set; }
- public event Action<SmartBowDeviceState> OnStateChanged;
- BluetoothAim _ble;
- AimHandler _aim;
- SmartBowHelper _subscribed1P;
- SmartBowHelper _subscribed2P;
- Coroutine _postInitBatteryRefresh;
- bool _isScreenPositioned;
- [Tooltip("九轴校准数据走游戏 WebSocket/业务服(SDK Host 模式);关闭则使用 SDK 默认 Remote HTTP。")]
- [SerializeField] bool useHostCalibrateStorage = true;
- [Header("设备展示(DeviceView 等设备图/标题)")]
- [SerializeField] DeviceTypeDisplayEntry[] deviceDisplayEntries;
- public bool IsReady => _ble != null && _aim != null;
- /// <summary>设备档案(1P/2P 本地配置)。</summary>
- public AimHandler Aim => _aim;
- /// <summary>蓝牙连接模块;AppUI 业务请用 Hub 公开 API,勿在 View 层直接访问。</summary>
- public BluetoothAim Ble => _ble;
- public bool NeedDecryption
- {
- get => _ble != null && _ble.NeedDecryption;
- set { if (_ble != null) _ble.NeedDecryption = value; }
- }
- /// <summary>确保 BluetoothHolder 已实例化(DontDestroyOnLoad)。</summary>
- public static void EnsureReady()
- {
- if (ins != null) return;
- GameObject prefab = ViewManager.GetPrefabByType(UIPrefabType.SmartBowDeviceHub);
- if (prefab != null)
- Instantiate(prefab);
- }
- void Awake()
- {
- if (ins != null && ins != this)
- {
- Destroy(gameObject);
- return;
- }
- ins = this;
- DontDestroyOnLoad(gameObject);
- _ble = GetComponentInChildren<BluetoothAim>(true);
- _aim = GetComponentInChildren<AimHandler>(true);
- if (_ble == null || _aim == null)
- Debug.LogError("[SmartBowDeviceHub] 预制体缺少子节点 BluetoothAim 或 AimHandler。");
- }
- void Start()
- {
- if (_aim != null)
- _aim.aimDeviceInfoChangeEvent += OnDeviceInfoChanged;
- RegisterExistingHelpers();
- RefreshState();
- }
- void OnDestroy()
- {
- UnsubscribeHelper(ref _subscribed1P);
- UnsubscribeHelper(ref _subscribed2P);
- if (_aim != null)
- _aim.aimDeviceInfoChangeEvent -= OnDeviceInfoChanged;
- if (ins == this) ins = null;
- }
- void OnDeviceInfoChanged() => RefreshState();
- public void RegisterHelper1P(SmartBowHelper helper)
- {
- if (helper == null) return;
- if (_subscribed1P == helper) return;
- UnsubscribeHelper(ref _subscribed1P);
- _subscribed1P = helper;
- _subscribed1P.OnBluetoothStatusChanged += OnHelperStatusChanged;
- _subscribed1P.OnBluetoothModuleInited += OnHelperModuleInited;
- ConfigureHelperCalibrateStorage(_subscribed1P);
- SyncArrowWeights(BluetoothPlayer.FIRST_PLAYER);
- RefreshState();
- }
- public void RegisterHelper2P(SmartBowHelper helper)
- {
- if (helper == null) return;
- if (_subscribed2P == helper) return;
- UnsubscribeHelper(ref _subscribed2P);
- _subscribed2P = helper;
- _subscribed2P.OnBluetoothStatusChanged += OnHelperStatusChanged;
- _subscribed2P.OnBluetoothModuleInited += OnHelperModuleInited;
- ConfigureHelperCalibrateStorage(_subscribed2P);
- SyncArrowWeights(BluetoothPlayer.SECOND_PLAYER);
- RefreshState();
- }
- void RegisterExistingHelpers()
- {
- if (_ble == null) return;
- RegisterHelper1P(_ble.getSmartBowHelper1P());
- RegisterHelper2P(_ble.getSmartBowHelper2P());
- }
- void OnHelperStatusChanged(SmartBowSDK.BluetoothStatusEnum oldStatus, SmartBowSDK.BluetoothStatusEnum newStatus) => RefreshState();
- /// <summary>模块初始化完成(含首次电量请求)后刷新 Hub 快照,供 SideSlip / DeviceView 等订阅方更新电量。</summary>
- void OnHelperModuleInited()
- {
- RefreshState();
- if (_postInitBatteryRefresh != null)
- StopCoroutine(_postInitBatteryRefresh);
- _postInitBatteryRefresh = StartCoroutine(RefreshStateAfterBatteryDelay());
- }
- IEnumerator RefreshStateAfterBatteryDelay()
- {
- // InitWhenConnected 发完 "b" 后电量包可能略晚于 OnBluetoothModuleInited 回调
- yield return new WaitForSecondsRealtime(0.5f);
- RefreshState();
- yield return new WaitForSecondsRealtime(1f);
- RefreshState();
- _postInitBatteryRefresh = null;
- }
- public void NotifyStateChanged() => RefreshState();
- public void RefreshState()
- {
- SmartBowDeviceState next = BuildState();
- if (State.Equals(next)) return;
- State = next;
- try { OnStateChanged?.Invoke(State); }
- catch (Exception e) { Debug.LogError(e); }
- }
- SmartBowDeviceState BuildState()
- {
- SmartBowHelper helper1P = _ble != null ? _ble.getSmartBowHelper1P() : null;
- SmartBowHelper helper2P = _ble != null ? _ble.getSmartBowHelper2P() : null;
- return new SmartBowDeviceState
- {
- IsHubReady = IsReady,
- ActivePlayer = _ble != null ? _ble.getBLEPlayer() : BluetoothPlayer.FIRST_PLAYER,
- Status1P = helper1P != null ? helper1P.GetBluetoothStatus() : SmartBowSDK.BluetoothStatusEnum.None,
- Status2P = helper2P != null ? helper2P.GetBluetoothStatus() : SmartBowSDK.BluetoothStatusEnum.None,
- Battery1P = helper1P != null ? helper1P.GetBattery() : 0,
- Battery2P = helper2P != null ? helper2P.GetBattery() : _ble?.get2PBattery() ?? 0,
- IsScreenPositioned = _isScreenPositioned,
- };
- }
- public void SetScreenPositioned(bool positioned)
- {
- if (_isScreenPositioned == positioned) return;
- _isScreenPositioned = positioned;
- RefreshState();
- }
- public BluetoothPlayer GetActivePlayer() => _ble != null ? _ble.getBLEPlayer() : BluetoothPlayer.FIRST_PLAYER;
- public void SetActivePlayer(BluetoothPlayer player)
- {
- _ble?.setBLEPlayer(player);
- RefreshState();
- }
- public SmartBowSDK.BluetoothStatusEnum GetSdkStatus(BluetoothPlayer player)
- {
- return State.GetSdkStatus(player);
- }
- public bool IsConnected(BluetoothPlayer player) => State.IsPlayerConnected(player);
- public bool IsConnecting(BluetoothPlayer player) => State.IsPlayerConnecting(player);
- public int GetBattery(BluetoothPlayer player)
- {
- return player == BluetoothPlayer.SECOND_PLAYER ? State.Battery2P : State.Battery1P;
- }
- public string GetStatusTextKey(BluetoothPlayer player)
- {
- return State.GetBLE2StatusTextKey(GetSdkStatus(player));
- }
- public void Connect1P() => _ble?.DoConnect();
- public void Connect2P() => _ble?.DoConnect2P();
- public void ConnectActivePlayer()
- {
- if (_ble == null) return;
- if (GetActivePlayer() == BluetoothPlayer.SECOND_PLAYER)
- Connect2P();
- else
- Connect1P();
- }
- public void CancelConnecting(global::BluetoothStatusEnum status = global::BluetoothStatusEnum.ConnectFail)
- {
- _ble?.onCancelAllConnecting(status);
- RefreshState();
- }
- /// <summary>DeviceView 手动选型/连接时抑制 HomeView 延迟自动连接,避免与 SideSlip 冲突。</summary>
- public void SetSuppressHomeAutoConnect(bool suppress) => _ble?.SetSuppressHomeAutoConnect(suppress);
- /// <summary>兼容旧版 onCancelAllConnecting 无参调用。</summary>
- public void CancelConnecting() => CancelConnecting(global::BluetoothStatusEnum.ConnectFail);
- public void SetConnectCanceled(bool canceled)
- {
- if (_ble != null) _ble.connectCanceled = canceled;
- }
- public void Set2PNoNeedToReconnect(bool value)
- {
- if (_ble != null) _ble.bNoNeedToReconnect = value;
- }
- public SmartBowHelper GetHelper(BluetoothPlayer player)
- {
- if (_ble == null) return null;
- return player == BluetoothPlayer.SECOND_PLAYER ? _ble.getSmartBowHelper2P() : _ble.getSmartBowHelper1P();
- }
- public void SubscribeShooting(BluetoothPlayer player, SmartBowHelper.ShootingEvent handler)
- {
- SmartBowHelper helper = GetHelper(player);
- if (helper == null || handler == null) return;
- helper.OnShooting += handler;
- }
- public void UnsubscribeShooting(BluetoothPlayer player, SmartBowHelper.ShootingEvent handler)
- {
- SmartBowHelper helper = GetHelper(player);
- if (helper == null || handler == null) return;
- helper.OnShooting -= handler;
- }
- public void SubscribeBleDeviceState(BluetoothAim.BleDeviceEvent handler)
- {
- if (_ble == null || handler == null) return;
- _ble.OnBleDeviceState += handler;
- }
- public BluetoothDeviceStatus GetBleDeviceState() {
- if (_aim == null) return BluetoothDeviceStatus.None;
- return _aim.bluetoothDeviceStatus;
- }
- public BluetoothDeviceStatus SetBleDeviceState(BluetoothDeviceStatus deviceStatus)
- {
- if (_aim == null) return BluetoothDeviceStatus.None;
- _aim.bluetoothDeviceStatus = deviceStatus;
- return _aim.bluetoothDeviceStatus;
- }
- public void UnsubscribeBleDeviceState(BluetoothAim.BleDeviceEvent handler)
- {
- if (_ble == null || handler == null) return;
- _ble.OnBleDeviceState -= handler;
- }
- public void SubscribeDeviceAndSystemInfoEvent(BluetoothAim.DeviceAndSystemInfoEvent handler)
- {
- if (_ble == null || handler == null) return;
- _ble.OnDeviceAndSystemInfoEvent += handler;
- }
- public void UnsubscribeDeviceAndSystemInfoEvent(BluetoothAim.DeviceAndSystemInfoEvent handler)
- {
- if (_ble == null || handler == null) return;
- _ble.OnDeviceAndSystemInfoEvent -= handler;
- }
- /// <summary>触发一次当前缓存的设备类型 / 连接平台信息。</summary>
- public void NotifyDeviceAndSystemInfo() => _ble?.GetDeviceAndSystemInfoEvent();
- /// <summary>
- /// 将 <see cref="UserSettings.ins.actualArrowWeight"/> 与 <see cref="CommonConfig.arrowWeight"/>
- /// 同步到 SDK 红外射箭速度换算(1P + 2P)。
- /// </summary>
- public void SyncArrowWeights()
- {
- SyncArrowWeights(BluetoothPlayer.FIRST_PLAYER);
- SyncArrowWeights(BluetoothPlayer.SECOND_PLAYER);
- }
- /// <summary>
- /// 将当前用户箭重设置同步到指定玩家的 SDK Helper。
- /// </summary>
- public void SyncArrowWeights(BluetoothPlayer player)
- {
- SmartBowHelper helper = GetHelper(player);
- if (helper == null) return;
- float gameWeight = UserSettings.ins != null ? UserSettings.ins.actualArrowWeight : 20f;
- float solidWeight = CommonConfig.arrowWeight > 0f ? CommonConfig.arrowWeight : 75f;
- helper.SetGameArrowWeight(gameWeight);
- helper.SetSolidArrowWeight(solidWeight);
- }
- /**
- * // 分开设置
- hub.SetGameArrowWeight(BluetoothPlayer.FIRST_PLAYER, 27f);
- hub.SetSolidArrowWeight(BluetoothPlayer.FIRST_PLAYER, 75f);
- // 读取
- float game = hub.GetGameArrowWeight(BluetoothPlayer.FIRST_PLAYER);
- float solid = hub.GetSolidArrowWeight(BluetoothPlayer.FIRST_PLAYER);
- */
- /// <summary>设置指定玩家 SDK 游戏里箭重(克)。</summary>
- public void SetGameArrowWeight(BluetoothPlayer player, float gameArrowWeight)
- {
- GetHelper(player)?.SetGameArrowWeight(gameArrowWeight);
- }
- /// <summary>设置指定玩家 SDK 实体模具箭重(克)。</summary>
- public void SetSolidArrowWeight(BluetoothPlayer player, float solidArrowWeight)
- {
- GetHelper(player)?.SetSolidArrowWeight(solidArrowWeight);
- }
- /// <summary>读取指定玩家 SDK 游戏里箭重;Helper 未就绪时回退 UserSettings。</summary>
- public float GetGameArrowWeight(BluetoothPlayer player)
- {
- SmartBowHelper helper = GetHelper(player);
- if (helper != null) return helper.GetGameArrowWeight();
- return UserSettings.ins != null ? UserSettings.ins.actualArrowWeight : 20f;
- }
- /// <summary>读取指定玩家 SDK 实体模具箭重;Helper 未就绪时回退 CommonConfig。</summary>
- public float GetSolidArrowWeight(BluetoothPlayer player)
- {
- SmartBowHelper helper = GetHelper(player);
- if (helper != null) return helper.GetSolidArrowWeight();
- return CommonConfig.arrowWeight > 0f ? CommonConfig.arrowWeight : 75f;
- }
- public float GetArrowSpeed(BluetoothPlayer player)
- {
- SmartBowHelper helper = GetHelper(player);
- if (helper == null) return 0f;
- return helper.GetArrowSpeed();
- }
- /// <summary>模块是否初始化完成(Demo:此后再调传感器/校准 API)。</summary>
- public bool IsModuleInited(BluetoothPlayer player)
- {
- SmartBowHelper helper = GetHelper(player);
- return helper != null && helper.IsBluetoothModuleInited();
- }
- /// <summary>
- /// 对齐 DemoStarter.OnBluetoothModuleInited:开启九轴 + 射箭传感。
- /// 连接态 Connected 但 moduleInited 未完成时返回 false。
- /// </summary>
- public bool EnsureNineAxisSensorsStarted(BluetoothPlayer? player = null)
- {
- if (_ble == null) return false;
- BluetoothPlayer p = player ?? GetActivePlayer();
- SmartBowHelper helper = GetHelper(p);
- if (helper == null) return false;
- if (helper.GetBluetoothStatus() != SmartBowSDK.BluetoothStatusEnum.Connected)
- return false;
- if (!helper.IsBluetoothModuleInited())
- return false;
- SyncArrowWeights(p);
- helper.StartRotationSensor();
- helper.StartShootingSensor();
- return true;
- }
- public bool IsMainConnectToInfraredDevice() =>
- IsMainConnectToHOUYIPRO() || IsMainConnectToARTEMISPRO();
- /// <summary>1P 档案为 HOUYIPRO 且已连接(<see cref="CommonConfig.bDisableBluetooth"/> 时仅看设备类型)。</summary>
- public bool IsMainConnectToHOUYIPRO() => IsMainConnectToDeviceType(AimDeviceType.HOUYIPRO);
- /// <summary>1P 档案为 ARTEMISPRO 且已连接(<see cref="CommonConfig.bDisableBluetooth"/> 时仅看设备类型)。</summary>
- public bool IsMainConnectToARTEMISPRO() => IsMainConnectToDeviceType(AimDeviceType.ARTEMISPRO);
- /// <summary>1P 档案为枪类设备且已连接(<see cref="CommonConfig.bDisableBluetooth"/> 时仅看设备类型)。</summary>
- public bool IsMainConnectToGun()
- {
- return GetMainConnectGunType().isGun;
- }
- /// <summary>1P 枪类连接信息:是否满足枪模式 + 具体枪型。</summary>
- public (bool isGun, AimDeviceType gunType) GetMainConnectGunType()
- {
- AimDeviceType gunType = AimDeviceType.NONE;
- if (_aim == null)
- return (false, gunType);
- int type = _aim.GetDeviceType(BluetoothPlayer.FIRST_PLAYER);
- if (!IsGunDeviceType(type))
- return (false, gunType);
- gunType = (AimDeviceType)type;
- if (CommonConfig.bDisableBluetooth)
- return (true, gunType);
- return (IsConnected(BluetoothPlayer.FIRST_PLAYER), gunType);
- }
- static bool IsGunDeviceType(int type) =>
- type == (int)AimDeviceType.Gun
- || type == (int)AimDeviceType.PistolM17
- || type == (int)AimDeviceType.RifleM416;
- bool IsMainConnectToDeviceType(AimDeviceType deviceType)
- {
- if (_aim == null)
- return false;
- if (_aim.GetDeviceType(BluetoothPlayer.FIRST_PLAYER) != (int)deviceType)
- return false;
- if (CommonConfig.bDisableBluetooth)
- return true;
- return IsConnected(BluetoothPlayer.FIRST_PLAYER);
- }
- void UnsubscribeHelper(ref SmartBowHelper helper)
- {
- if (helper == null) return;
- SdkCalibrateHostBridge.Unbind(helper);
- helper.OnBluetoothStatusChanged -= OnHelperStatusChanged;
- helper.OnBluetoothModuleInited -= OnHelperModuleInited;
- helper = null;
- }
- void ConfigureHelperCalibrateStorage(SmartBowHelper helper)
- {
- if (helper == null) return;
- if (useHostCalibrateStorage)
- SdkCalibrateHostBridge.Bind(helper);
- else
- {
- SdkCalibrateHostBridge.Unbind(helper);
- helper.calibrateDataStorageMode = CalibrateDataStorageMode.Remote;
- }
- }
- /// <summary>按 <see cref="AimDeviceType"/> 取 DeviceView 等设备展示图。</summary>
- public Sprite GetDeviceDisplaySprite(AimDeviceType deviceType) =>
- TryGetDeviceDisplay(deviceType, out Sprite sprite, out _) ? sprite : null;
- /// <summary>按 <see cref="AimDeviceType"/> 取 DeviceView 等设备展示标题。</summary>
- public string GetDeviceDisplayName(AimDeviceType deviceType) =>
- TryGetDeviceDisplay(deviceType, out _, out string displayName) ? displayName : deviceType.ToString();
- public bool TryGetDeviceDisplay(AimDeviceType deviceType, out Sprite sprite, out string displayName)
- {
- sprite = null;
- displayName = null;
- if (deviceDisplayEntries == null) return false;
- for (int i = 0; i < deviceDisplayEntries.Length; i++)
- {
- DeviceTypeDisplayEntry entry = deviceDisplayEntries[i];
- if (entry == null || entry.deviceType != deviceType) continue;
- sprite = entry.sprite;
- displayName = string.IsNullOrEmpty(entry.displayName)
- ? deviceType.ToString()
- : entry.displayName;
- return sprite != null || !string.IsNullOrEmpty(displayName);
- }
- return false;
- }
- public void ResetAim(BluetoothPlayer player) => GetHelper(player)?.ResetAim();
- public void SetMainConnectDeviceType() => _ble.SetMainConnectDeviceType();
- }
- }
|