using DragonBones;
using SmartBowSDK;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using SdkAimDeviceInfo = SmartBowSDK.AimDeviceInfo;
namespace AppUI.Bluetooth
{
///
/// AppUI 瞄准处理器:设备信息本地维护,九轴/校准走 SmartBowSDK(Axis9Handler_SDK)。
///
public class AimHandler : MonoBehaviour
{
CameraToLook m_controlObj => CameraToLook.ins;
#region 设备信息(MAC 走 SDK AimDeviceInfo;型号走 PlayerDeviceTypeStore)
public const string SdkUserTags = "smartbow";
/// 当前 SideSlip / 连接上下文使用的玩家槽位(0=1P,1=2P)。
int deviceSelectIndex;
/// 与 SDK 同步的当前槽位档案(只读用途请用 )。
public SdkAimDeviceInfo aimDeviceInfo;
int _tempDeviceType = -1;
public Action aimDeviceInfoChangeEvent;
public static int GetSdkDeviceId(BluetoothPlayer player) =>
player == BluetoothPlayer.SECOND_PLAYER ? 2 : 1;
public static int GetSdkDeviceId(int playerIndex) =>
playerIndex == (int)BluetoothPlayer.SECOND_PLAYER ? 2 : 1;
static string SdkPrefsKey(int sdkDeviceId) => "aim-device-info-" + SdkUserTags + sdkDeviceId;
SmartBowHelper GetHelperForPlayer(BluetoothPlayer player)
{
if (Ble == null) return null;
return player == BluetoothPlayer.SECOND_PLAYER
? Ble.getSmartBowHelper2P()
: Ble.getSmartBowHelper1P();
}
SdkAimDeviceInfo LoadSdkDeviceInfoFromPrefs(int sdkDeviceId)
{
string json = PlayerPrefs.GetString(SdkPrefsKey(sdkDeviceId), "");
if (string.IsNullOrEmpty(json))
return null;
return JsonUtility.FromJson(json);
}
void SaveSdkDeviceInfo(SdkAimDeviceInfo info, int sdkDeviceId)
{
if (info == null) return;
PlayerPrefs.SetString(SdkPrefsKey(sdkDeviceId), JsonUtility.ToJson(info));
}
public void onClearAimDeviceInfosNew()
{
GetHelperForPlayer(BluetoothPlayer.FIRST_PLAYER)?.ClearDeviceInfo(SdkUserTags, 1);
GetHelperForPlayer(BluetoothPlayer.SECOND_PLAYER)?.ClearDeviceInfo(SdkUserTags, 2);
PlayerPrefs.DeleteKey(SdkPrefsKey(1));
PlayerPrefs.DeleteKey(SdkPrefsKey(2));
PlayerDeviceTypeStore.ClearAll();
aimDeviceInfo = null;
aimDeviceInfoChangeEvent?.Invoke();
}
public void onClear2PAimDeviceInfosNew()
{
GetHelperForPlayer(BluetoothPlayer.SECOND_PLAYER)?.ClearDeviceInfo(SdkUserTags, 2);
PlayerPrefs.DeleteKey(SdkPrefsKey(2));
PlayerDeviceTypeStore.ClearPlayer(BluetoothPlayer.SECOND_PLAYER);
aimDeviceInfoChangeEvent?.Invoke();
}
public void OnGetAimDeviceInfos()
{
if (deviceSelectIndex >= 0)
aimDeviceInfo = GetAimDeviceInfo((BluetoothPlayer)deviceSelectIndex);
aimDeviceInfoChangeEvent?.Invoke();
}
public void OnSaveAimDeviceInfos()
{
if (aimDeviceInfo != null)
SaveSdkDeviceInfo(aimDeviceInfo, aimDeviceInfo.id);
aimDeviceInfoChangeEvent?.Invoke();
}
public void onCreateTempAimDeviceInfo()
{
_tempDeviceType = -1;
Debug.Log("onCreateTempAimDeviceInfo deviceSelectIndex:" + deviceSelectIndex);
}
public int GetTempDeviceType() => _tempDeviceType;
public void onCreateAimDeviceInfoById()
{
BluetoothPlayer player = (BluetoothPlayer)deviceSelectIndex;
int sdkDeviceId = GetSdkDeviceId(player);
aimDeviceInfo = GetAimDeviceInfo(player);
if (aimDeviceInfo == null)
{
aimDeviceInfo = new SdkAimDeviceInfo(sdkDeviceId);
SaveSdkDeviceInfo(aimDeviceInfo, sdkDeviceId);
}
aimDeviceInfoChangeEvent?.Invoke();
}
public void SetAimDeviceSelectIndex(int selectIndex)
{
if (selectIndex < 0)
{
Debug.LogWarning("selectIndex不能小于0:" + selectIndex);
return;
}
deviceSelectIndex = selectIndex;
}
public void SetTempAimDeviceType(AimDeviceType _aimDeviceType) => _tempDeviceType = (int)_aimDeviceType;
public void SetAimDeviceType(AimDeviceType _aimDeviceType) => SetAimDeviceType((int)_aimDeviceType);
public void SetAimDeviceType(int _aimDeviceType)
{
BluetoothPlayer player = (BluetoothPlayer)deviceSelectIndex;
PlayerDeviceTypeStore.SetType(player, _aimDeviceType);
aimDeviceInfoChangeEvent?.Invoke();
}
public int GetDeviceType(BluetoothPlayer player) => PlayerDeviceTypeStore.GetType(player);
public int GetActivePlayerDeviceType()
{
BluetoothPlayer player = BluetoothPlayer.FIRST_PLAYER;
if (Ble != null)
player = Ble.getBLEPlayer();
return GetDeviceType(player);
}
public void SetAimDeviceMac(string _mac)
{
BluetoothPlayer player = BluetoothPlayer.FIRST_PLAYER;
if (Ble != null)
player = Ble.getBLEPlayer();
else if (deviceSelectIndex >= 0)
player = (BluetoothPlayer)deviceSelectIndex;
int sdkDeviceId = GetSdkDeviceId(player);
SdkAimDeviceInfo info = GetAimDeviceInfo(player);
if (info == null)
info = new SdkAimDeviceInfo(sdkDeviceId);
info.setInitMac(_mac);
SaveSdkDeviceInfo(info, sdkDeviceId);
if (deviceSelectIndex == (int)player)
aimDeviceInfo = info;
aimDeviceInfoChangeEvent?.Invoke();
}
public void ResetAimDeviceMac()
{
BluetoothPlayer player = BluetoothPlayer.FIRST_PLAYER;
if (Ble != null)
player = Ble.getBLEPlayer();
else if (deviceSelectIndex >= 0)
player = (BluetoothPlayer)deviceSelectIndex;
int sdkDeviceId = GetSdkDeviceId(player);
SdkAimDeviceInfo info = GetAimDeviceInfo(player);
if (info == null)
return;
info.resetInitMac();
SaveSdkDeviceInfo(info, sdkDeviceId);
if (deviceSelectIndex == (int)player)
aimDeviceInfo = info;
aimDeviceInfoChangeEvent?.Invoke();
}
/// 侧滑连接是否为「切换设备页选型覆盖」;false 表示直接连接,沿用已记录 MAC。
bool _sideSlipOverwritesDeviceInfo;
public bool SideSlipOverwritesDeviceInfo => _sideSlipOverwritesDeviceInfo;
public void ClearSideSlipOverwriteFlag() => _sideSlipOverwritesDeviceInfo = false;
public void OverwriteAimDeviceInfoForPlayer(BluetoothPlayer player, AimDeviceType deviceType)
{
SetAimDeviceSelectIndex((int)player);
int sdkDeviceId = GetSdkDeviceId(player);
SdkAimDeviceInfo info = GetAimDeviceInfo(player);
if (info == null)
{
info = new SdkAimDeviceInfo(sdkDeviceId);
}
info.resetInitMac();
SaveSdkDeviceInfo(info, sdkDeviceId);
PlayerDeviceTypeStore.SetType(player, (int)deviceType);
aimDeviceInfo = info;
_tempDeviceType = (int)deviceType;
_sideSlipOverwritesDeviceInfo = true;
aimDeviceInfoChangeEvent?.Invoke();
}
public void PrepareDirectSideSlipConnect(int selectIndex)
{
SetAimDeviceSelectIndex(selectIndex);
onCreateAimDeviceInfoById();
_sideSlipOverwritesDeviceInfo = false;
}
public SdkAimDeviceInfo GetAimDeviceInfo(BluetoothPlayer player)
{
int sdkDeviceId = GetSdkDeviceId(player);
SmartBowHelper helper = GetHelperForPlayer(player);
if (helper != null)
{
SdkAimDeviceInfo info = helper.GetDeviceInfo(SdkUserTags, sdkDeviceId);
if (info != null)
return info;
}
return LoadSdkDeviceInfoFromPrefs(sdkDeviceId);
}
public bool isHOUYIPRO(BluetoothPlayer bluetoothPlayer) =>
GetDeviceType(bluetoothPlayer) == (int)AimDeviceType.HOUYIPRO;
public bool isARTEMISPro(BluetoothPlayer bluetoothPlayer) =>
GetDeviceType(bluetoothPlayer) == (int)AimDeviceType.ARTEMISPRO;
#endregion
public int DeviceType => 9;
[NonSerialized] public int gyrCalibrateCompleteCount;
[NonSerialized] public int gyrCalibrateTotalCount = 2000;
public bool bInitOne;
public OnCrossBtnEventEvent OnCrossBtnEvent;
public delegate void OnCrossBtnEventEvent();
SmartBowHelper _boundSdk1P;
SmartBowHelper _lastPollSdk;
int _sdkAxisReadyAfterFrame = -1;
bool _magCompleted;
[NonSerialized] public bool lerpForRotation = true;
[NonSerialized] public float lerpTimeRate = 7;
Quaternion _targetRotation = Quaternion.identity;
BluetoothAim Ble => SmartBowDeviceHub.ins?.Ble;
void OnDestroy()
{
UnbindSdk1P();
}
public void InvokeOnCrossBtnEvent()
{
try { OnCrossBtnEvent?.Invoke(); }
catch (Exception e) { Debug.LogError(e); }
}
#region SDK 绑定(1P 数据由 SmartBowHelper / Axis9Handler_SDK 处理)
public void BindSdk1P(SmartBowHelper helper)
{
if (helper == null) return;
if (_boundSdk1P == helper) return;
UnbindSdk1P();
_boundSdk1P = helper;
helper.OnShooting += OnShot1P;
helper.OnBleDeviceState += HandleUpdateTheMagazine;
helper.OnRotationUpdate += OnSdkRotationUpdate;
helper.OnFunctionKeyPress += OnSdkFunctionKeyShortPress;
helper.OnFunctionKeyLongPress += OnSdkFunctionKeyLongPress;
helper.OnDeviceAndSystemInfoEvent += HandleDeviceAndSystemInfo;
MarkSdkAxisPending(helper);
}
public void UnbindSdk1P()
{
if (_boundSdk1P == null) return;
_boundSdk1P.OnShooting -= OnShot1P;
_boundSdk1P.OnBleDeviceState -= HandleUpdateTheMagazine;
_boundSdk1P.OnRotationUpdate -= OnSdkRotationUpdate;
_boundSdk1P.OnFunctionKeyPress -= OnSdkFunctionKeyShortPress;
_boundSdk1P.OnFunctionKeyLongPress -= OnSdkFunctionKeyLongPress;
_boundSdk1P.OnDeviceAndSystemInfoEvent -= HandleDeviceAndSystemInfo;
_boundSdk1P = null;
}
SmartBowHelper GetSdk1P()
{
if (_boundSdk1P != null) return _boundSdk1P;
return SmartBowDeviceHub.ins?.Ble?.getSmartBowHelper1P();
}
SmartBowHelper GetSdkForCalibration()
{
BluetoothAim ble = Ble;
if (ble == null) return GetSdk1P();
if (ble.getBLEPlayer() == BluetoothPlayer.SECOND_PLAYER)
return ble.getSmartBowHelper2P();
return GetSdk1P();
}
void OnSdkRotationUpdate(Quaternion rotation)
{
_targetRotation = rotation;
if (!bRuning9Axis()) return;
if (SB_EventSystem.ins && SB_EventSystem.ins.simulateMouseIsAwaked)
SB_EventSystem.ins.MoveSimulateMouse(rotation);
if (m_controlObj == null) return;
if (lerpForRotation)
m_controlObj.localRotation = Quaternion.Lerp(m_controlObj.localRotation, rotation, Time.deltaTime * lerpTimeRate);
else
m_controlObj.localRotation = rotation;
}
void OnSdkFunctionKeyShortPress() => HandleFunctionKeyShortPress();
void OnSdkFunctionKeyLongPress() => HandleFunctionKeyLongPress();
#endregion
#region 九轴 / 校准(委托 SDK,不再使用本地 Axis9Handler)
public void ReinitAxisHandler()
{
if (!UsesNineAxisDevice())
return;
Debug.Log("[AppUI.AimHandler] 九轴设备 HOUYI/HOUYI2/ARTEMIS,旋转与校准走 SDK Axis9Handler。");
}
public void SetMsOldDefault() { }
public void DoIdentity()
{
GetSdkForCalibration()?.ResetAim();
if (!bRuning9Axis()) return;
if (m_controlObj) m_controlObj.localRotation = _targetRotation;
}
public void NotifyAxisOnShot() { }
public void CalibrateGyr(bool calibration)
{
if (!UsesNineAxisDevice()) return;
var sdk = GetSdkForCalibration();
if (sdk == null || !CanPollSdkAxisCalibration(sdk)) return;
if (calibration) sdk.StartGyrCalibration();
else sdk.StopGyrCalibration();
}
public void InitGyr(string record) { }
public void InitMag(string record) { }
public void ResetGyr()
{
if (!UsesNineAxisDevice()) return;
var sdk = GetSdkForCalibration();
if (sdk == null || !CanPollSdkAxisCalibration(sdk)) return;
sdk.StopGyrCalibration();
}
public void ResetMag()
{
if (!UsesNineAxisDevice()) return;
TryStartMagCalibration();
}
public void ApplyImpreciseMag() { }
public bool IsGyrCompleted()
{
if (!UsesNineAxisDevice()) return false;
var sdk = GetSdkForCalibration();
return sdk != null && CanPollSdkAxisCalibration(sdk) && sdk.IsGyrCompleted();
}
public bool IsMagCompleted()
{
if (!UsesNineAxisDevice()) return false;
var sdk = GetSdkForCalibration();
return sdk != null && CanPollSdkAxisCalibration(sdk) && sdk.IsMagCompleted();
}
public bool IsGyrCalibrating()
{
if (!UsesNineAxisDevice()) return false;
var sdk = GetSdkForCalibration();
return sdk != null && CanPollSdkAxisCalibration(sdk) && sdk.IsGyrCalibrating();
}
public int GetGyrProgressPercent()
{
if (!UsesNineAxisDevice()) return 0;
var sdk = GetSdkForCalibration();
if (sdk == null || !CanPollSdkAxisCalibration(sdk)) return 0;
return Mathf.Clamp(Mathf.FloorToInt(sdk.GetGyrProgress() * 100f), 0, 100);
}
/// 校准页专用:仅校验 SDK 连接与轮询就绪,不重复判断设备档案类型。
public bool IsSdkGyrCompleted()
{
var sdk = GetSdkForCalibration();
return sdk != null && CanPollSdkAxisCalibration(sdk) && sdk.IsGyrCompleted();
}
/// 校准页专用:仅校验 SDK 连接与轮询就绪,不重复判断设备档案类型。
public bool IsSdkMagCompleted()
{
var sdk = GetSdkForCalibration();
return sdk != null && CanPollSdkAxisCalibration(sdk) && sdk.IsMagCompleted();
}
public bool TryStartMagCalibration()
{
var sdk = GetSdkForCalibration();
if (sdk == null || !CanPollSdkAxisCalibration(sdk))
return false;
sdk.StartMagCalibration();
return true;
}
/// 对齐 DemoStarter.OnClick_CalibrateGyr:校准中则停止,否则开始。
public bool TryToggleGyrCalibration()
{
var sdk = GetSdkForCalibration();
if (sdk == null || !CanPollSdkAxisCalibration(sdk))
return false;
if (sdk.IsGyrCalibrating())
sdk.StopGyrCalibration();
else
sdk.StartGyrCalibration();
return true;
}
public bool TryStopGyrCalibration()
{
var sdk = GetSdkForCalibration();
if (sdk == null || !CanPollSdkAxisCalibration(sdk))
return false;
sdk.StopGyrCalibration();
return true;
}
public bool IsSdkGyrCalibrating()
{
var sdk = GetSdkForCalibration();
return sdk != null && CanPollSdkAxisCalibration(sdk) && sdk.IsGyrCalibrating();
}
public int GetSdkGyrProgressPercent()
{
var sdk = GetSdkForCalibration();
if (sdk == null || !CanPollSdkAxisCalibration(sdk)) return 0;
return Mathf.Clamp(Mathf.FloorToInt(sdk.GetGyrProgress() * 100f), 0, 100);
}
public IEnumerator SaveGyr()
{
yield return null;
}
public IEnumerator SaveMag()
{
yield return null;
}
public void ResumeCalibrateRecord(string record)
{
if (string.IsNullOrEmpty(record)) return;
GetSdkForCalibration()?.ResumeCalibrateRecord(record);
}
public void CorrectMagCompleted(bool value) { _magCompleted = value; }
public void Ban9AxisCalculate(bool ban) { }
public bool bRuning9Axis()
{
return GlobalData.MyDeviceMode == DeviceMode.Archery
&& Ble != null
&& !Ble.isMainConnectToInfraredDevice()
&& UsesNineAxisDevice();
}
/// HOUYI / HOUYI2 / ARTEMIS 走九轴;Pro/枪等不走本地九轴校准轮询。
public bool UsesNineAxisDevice()
{
BluetoothPlayer player = BluetoothPlayer.FIRST_PLAYER;
if (Ble != null && Ble.getBLEPlayer() == BluetoothPlayer.SECOND_PLAYER)
player = BluetoothPlayer.SECOND_PLAYER;
return IsNineAxisArcheryType(GetDeviceType(player));
}
static bool IsNineAxisArcheryType(int type)
{
return type == (int)AimDeviceType.HOUYI
|| type == (int)AimDeviceType.HOUYI2
|| type == (int)AimDeviceType.ARTEMIS;
}
public static bool IsInfraredDeviceType(int type)
{
return type == (int)AimDeviceType.HOUYIPRO
|| type == (int)AimDeviceType.Gun
|| type == (int)AimDeviceType.ARTEMISPRO
|| type == (int)AimDeviceType.PistolM17
|| type == (int)AimDeviceType.RifleM416;
}
public bool IsActivePlayerInfraredDevice() =>
IsInfraredDeviceType(GetActivePlayerDeviceType());
public bool IsActivePlayerNineAxisDevice() =>
IsNineAxisArcheryType(GetActivePlayerDeviceType());
public SdkAimDeviceInfo GetActivePlayerAimDeviceInfo()
{
BluetoothPlayer player = BluetoothPlayer.FIRST_PLAYER;
if (Ble != null)
player = Ble.getBLEPlayer();
SdkAimDeviceInfo info = GetAimDeviceInfo(player);
if (info == null && aimDeviceInfo != null && aimDeviceInfo.id == GetSdkDeviceId(player))
return aimDeviceInfo;
return info;
}
void MarkSdkAxisPending(SmartBowHelper sdk)
{
if (sdk == null) return;
_lastPollSdk = sdk;
_sdkAxisReadyAfterFrame = Time.frameCount;
}
bool CanPollSdkAxisCalibration(SmartBowHelper sdk)
{
if (sdk == null) return false;
if (sdk.GetBluetoothStatus() != SmartBowSDK.BluetoothStatusEnum.Connected)
return false;
// AimHandler_SDK 在 Start 里 new Axis9Handler_SDK,同帧调用会 NRE
return _sdkAxisReadyAfterFrame < 0 || Time.frameCount > _sdkAxisReadyAfterFrame;
}
#endregion
void Update()
{
if (!UsesNineAxisDevice())
{
_magCompleted = false;
return;
}
var sdk = GetSdkForCalibration();
if (sdk == null)
{
_magCompleted = false;
return;
}
if (sdk != _lastPollSdk)
MarkSdkAxisPending(sdk);
if (!CanPollSdkAxisCalibration(sdk))
return;
if (sdk.IsGyrCalibrating())
gyrCalibrateCompleteCount = (int)(sdk.GetGyrProgress() * gyrCalibrateTotalCount);
if (IsMagCompleted())
{
if (!_magCompleted)
{
StartCoroutine(SaveMag());
PopupMgr.ins?.ShowTipTop(TextAutoLanguage2.GetTextByKey("tip_mag-calibrate_success"));
}
_magCompleted = true;
}
else
{
_magCompleted = false;
}
}
void HandleDeviceAndSystemInfo(ConnectPlatform connectPlatform, BluetoothDeviceType bleDeviceType)
{
var boxUserSettings = FindAnyObjectByType();
switch (bleDeviceType)
{
case BluetoothDeviceType.HOUYIPro:
UserSettings.ins.selectDevicesName = "HOUYI Pro";
UserSettings.ins.Save();
boxUserSettings?.OnUpdateClickInfoByName();
break;
case BluetoothDeviceType.ARTEMISPro:
UserSettings.ins.selectDevicesName = "ARTEMIS Pro";
UserSettings.ins.Save();
boxUserSettings?.OnUpdateClickInfoByName();
break;
case BluetoothDeviceType.PistolM9:
UserSettings.ins.selectDevicesName = "Pistol M9";
UserSettings.ins.Save();
boxUserSettings?.OnUpdateClickInfoByName();
break;
}
}
//弹夹状态
public BluetoothDeviceStatus bluetoothDeviceStatus = BluetoothDeviceStatus.MagazineLoading;
///
/// 0x00 - 弹夹分离 0x01 - 弹夹上膛
///
///
void HandleUpdateTheMagazine(BluetoothDeviceType bleDeviceType, BluetoothDeviceStatus gunStatusEnum)
{
bluetoothDeviceStatus = gunStatusEnum;
if (gunStatusEnum == BluetoothDeviceStatus.MagazineLoading)
{
//播放上弹夹时候的声音
AudioMgr.ins.PlayBeLoaded();
Debug.Log("弹夹上膛");
if (Billboard.ins)
{
Billboard.ins.bulletManager.ResetBullets(); //game
}
}
Ble.InvokeOnBleDevice(bleDeviceType, gunStatusEnum);
}
void HandleFunctionKeyShortPress()
{
if (bInitOne)
{
bInitOne = false;
if (IsLegacyArcheryType(GetActivePlayerDeviceType()))
AutoResetView.DoIdentity();
else
CrossBtnEvent();
return;
}
if (SB_EventSystem.ins && SB_EventSystem.ins.simulateMouseIsAwaked)
{
if (FindAnyObjectByType() == null)
DoIdentity();
if (FindAnyObjectByType() != null)
InvokeOnCrossBtnEvent();
}
else
{
if (IsLegacyArcheryType(GetActivePlayerDeviceType()))
AutoResetView.DoIdentity();
else
CrossBtnEvent();
}
}
void HandleFunctionKeyLongPress()
{
if (GlobalData.MyDeviceMode == DeviceMode.Gun || (Ble != null && Ble.isMainConnectToInfraredDevice()))
{
InfraredGuider infraredGuider = FindAnyObjectByType();
if (infraredGuider == null)
AutoResetView.DoIdentity();
else
infraredGuider.onLongClickDevice();
}
else if (SB_EventSystem.ins)
{
SB_EventSystem.ins.AwakenSimulateMouse();
}
}
static bool IsLegacyArcheryType(int type)
{
return type == (int)AimDeviceType.NONE
|| type == (int)AimDeviceType.HOUYI
|| type == (int)AimDeviceType.HOUYI2
|| type == (int)AimDeviceType.ARTEMIS;
}
void CrossBtnEvent()
{
Debug.Log("CrossBtnEvent");
InvokeOnCrossBtnEvent();
if (CommonConfig.StandaloneModeOrPlatformB)
{
FindAnyObjectByType()?.onClickDevice();
return;
}
if (GameAssistUI.ins)
{
InfraredGuider infraredGuider = FindAnyObjectByType();
if (infraredGuider == null)
{
Transform button5 = GameAssistUI.ins.transform.Find("Button5");
if (button5 != null && button5.gameObject.activeSelf)
button5.GetComponent