| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255 |
- using AppUI.Bluetooth;
- using AppUI.Localization;
- using AppUI.Manager.View;
- using AppUI.View.Home.Main;
- using AppUIDeviceView = AppUI.View.Home.DeviceView.DeviceView;
- using DG.Tweening;
- using InfraredManager;
- using SmartBowSDK;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using TMPro;
- using UnityEngine;
- using UnityEngine.Events;
- using UnityEngine.UI;
- using BLEAimhandler = AppUI.Bluetooth.AimHandler;
- namespace AppUI.View.Component
- {
- /// <summary>
- /// 侧滑面板:<c>BG</c> 遮罩(可选点击关闭),<c>Right</c> 自右向左滑入;
- /// 左上关闭、底部切换 Step,<c>Content</c> 下步骤内容留给业务扩展。
- /// </summary>
- [DisallowMultipleComponent]
- [ExecuteAlways]
- public class ModuleSideSlip : MonoBehaviour
- {
- const string PathMaskBg = "BG";
- const string PathRight = "Right";
- const string PathBtnClose = "Right/BtnClose";
- const string PathContent = "Right/Content";
- const string PathBottomBar = "Right/BottomBar";
- const string PathBtnStep = "Right/BottomBar/BtnStep";
- const float Step3BatteryRefreshInterval = 1f;
- const float DefaultSlideDuration = 0.32f;
- const string KeyBtnNext = "appui-common-next";
- const string KeyBtnStartConnect = "appui-sideslip-btn-start-connect";
- const string KeyBtnConnecting = "appui-sideslip-btn-connecting";
- const string KeyBtnDone = "appui-sideslip-btn-done";
- const string KeyBtnStep = "appui-sideslip-btn-step";
- const string KeyBtnStepProgress = "appui-sideslip-btn-step-progress";
- const string KeyBtnScreenPositioning = "appui-sideslip-btn-screen-positioning";
- const string KeyScreenPositioningDone = "appui-sideslip-screen-positioning-done";
- const string KeyScreenPositioningIncomplete = "appui-sideslip-screen-positioning-incomplete";
- [SerializeField]
- bool maskCloseOnClick = true;
- [Header("动画")]
- [SerializeField]
- float slideDuration = DefaultSlideDuration;
- [SerializeField]
- Ease slideInEase = Ease.OutCubic;
- [SerializeField]
- Ease slideOutEase = Ease.InCubic;
- [Header("侧滑坐标(Right 需右锚点 + 右 Pivot,贴边 X 一般为 0)")]
- [Tooltip("面板完全展开时 Right 的 AnchoredPosition.x,勿用编辑器里拖偏后的值")]
- [SerializeField]
- float shownAnchorPosX;
- [Tooltip("打开前将 Right.PosX 归位到贴边值,避免预制体残留错误坐标")]
- [SerializeField]
- bool normalizeRightPosBeforeSlide = true;
- [Header("步骤(可留空,自动收集 Content 下 Step_* 子物体)")]
- [SerializeField]
- List<GameObject> stepPanels = new List<GameObject>();
- [Header("引用(可留空,自动绑定)")]
- [SerializeField]
- RectTransform maskBg;
- [SerializeField]
- Button maskButton;
- [SerializeField]
- RectTransform rightPanel;
- [SerializeField]
- Button btnClose;
- [SerializeField]
- Button btnStep;
- [SerializeField]
- TMP_Text btnStepLabel;
- [Header("Step_0~2 按设备类型配置(可留空,留空则不改对应 Step 内容)")]
- [SerializeField]
- SideSlipDeviceGuideEntry[] guideEntries;
- [Header("Step_0 引导页")]
- [SerializeField] Image step0Image;
- [SerializeField] TMP_Text step0Description;
- [Header("Step_1 引导页")]
- [SerializeField] Image step1Image;
- [SerializeField] TMP_Text step1Description;
- [Header("Step_2 连接中页")]
- [SerializeField] Image step2Image;
- [SerializeField] TMP_Text step2Description;
- [Header("Step_3 连接成功页")]
- [SerializeField] Image step3DeviceImage;
- [SerializeField] TMP_Text step3Title;
- [SerializeField] Image step3BatteryBar;
- [SerializeField] Text step3BatteryValue;
- [SerializeField] TMP_Text step3ConnectStatus;
- [SerializeField] TMP_Text step3ScreenStatus;
- AimDeviceType? _guideDeviceTypeOverride;
- bool _closing;
- int _stepIndex;
- float _shownAnchorX;
- float _hiddenAnchorX;
- Tween _slideTween;
- Coroutine _slideInRoutine;
- float _step3BatteryTimer;
- bool _hubStep3RefreshBound;
- static readonly Color32 StatusCompleteColor = new Color32(0, 0, 0, 255);
- static readonly Color32 StatusIncompleteColor = new Color32(145, 144, 144, 255);
- public bool MaskCloseOnClick
- {
- get => maskCloseOnClick;
- set
- {
- maskCloseOnClick = value;
- ApplyMaskCloseInteractable();
- }
- }
- public int StepCount => stepPanels != null ? stepPanels.Count : 0;
- public int CurrentStepIndex => _stepIndex;
- public RectTransform ContentRoot => transform.Find(PathContent) as RectTransform;
- public UnityEvent onClosed = new UnityEvent();
- public event Action<int> OnStepChanged;
- /// <summary>补齐 BG / Right / BtnClose / Content / BottomBar 标准层级。</summary>
- public static void EnsurePrefabHierarchy(Transform root) => SideSlipUIBuilder.EnsureHierarchy(root);
- public static ModuleSideSlip Show(bool dontDestroyOnLoad = false, AimDeviceType? guideDeviceType = null)
- {
- ViewConfig viewConfig = ViewManager.GetConfig(UIPrefabType.ModuleSideSlip);
- GameObject go = Instantiate(Resources.Load<GameObject>(viewConfig.path));
- var slip = go.GetComponent<ModuleSideSlip>();
- if (slip == null)
- slip = go.AddComponent<ModuleSideSlip>();
- if (guideDeviceType.HasValue)
- slip.SetGuideDeviceType(guideDeviceType.Value);
- if (dontDestroyOnLoad)
- DontDestroyOnLoad(go);
- return slip;
- }
- /// <summary>打开侧滑前指定当前引导所针对的设备类型(如从设备列表点击传入)。</summary>
- public void SetGuideDeviceType(AimDeviceType deviceType)
- {
- _guideDeviceTypeOverride = deviceType;
- if (Application.isPlaying && isActiveAndEnabled)
- RefreshGuideSteps();
- }
- void OnDestroy()
- {
- UnbindHubStep3Refresh();
- //退出面板时候,取消正在连接的连接
- SmartBowDeviceHub.ins?.CancelConnecting();
- if (_slideInRoutine != null)
- StopCoroutine(_slideInRoutine);
- _slideTween?.Kill();
- if (rightPanel != null)
- rightPanel.DOKill();
- }
- #if UNITY_EDITOR
- void OnValidate()
- {
- if (Application.isPlaying)
- return;
- SideSlipUIBuilder.EnsureHierarchy(transform);
- ResolveReferences();
- ResolveGuideStepReferences();
- ApplyMaskCloseInteractable();
- AppUILocalization.Init();
- RefreshGuideSteps();
- }
- #endif
- void Awake()
- {
- AppUILocalization.Init();
- transform.localScale = Vector3.one;
- SideSlipUIBuilder.EnsureHierarchy(transform);
- ResolveReferences();
- ApplyMaskCloseInteractable();
- }
- void OnEnable()
- {
- AppUILocalization.OnLanguageChanged += HandleLanguageChanged;
- }
- void OnDisable()
- {
- AppUILocalization.OnLanguageChanged -= HandleLanguageChanged;
- }
- void HandleLanguageChanged(LanguageEnum _)
- {
- if (!Application.isPlaying)
- return;
- RefreshStepButtonForIndex(_stepIndex);
- if (_stepIndex is >= 0 and <= 2)
- RefreshGuideSteps();
- if (_stepIndex == 3)
- RefreshStep3ConnectedInfo();
- }
- void Start()
- {
- resetAimType();
- BindButtons();
- CollectStepPanelsIfEmpty();
- ResolveGuideStepReferences();
- if (Application.isPlaying)
- EnsureGuideDescriptionLocalization();
- RefreshGuideSteps();
- ShowStep(0, notify: false);
- _slideInRoutine = StartCoroutine(PlaySlideInAfterLayout());
- }
- void Update()
- {
- if (!Application.isPlaying)
- return;
- UpdateBtnForConnect();
- if (_stepIndex == 3)
- {
- _step3BatteryTimer += Time.deltaTime;
- if (_step3BatteryTimer >= Step3BatteryRefreshInterval)
- {
- _step3BatteryTimer = 0f;
- RefreshStep3Battery();
- }
- }
- }
- void ResolveReferences()
- {
- if (maskBg == null)
- {
- var bgTr = transform.Find(PathMaskBg) as RectTransform;
- if (bgTr != null)
- maskBg = bgTr;
- }
- if (maskBg != null && maskButton == null)
- maskButton = maskBg.GetComponent<Button>();
- if (rightPanel == null)
- rightPanel = transform.Find(PathRight) as RectTransform;
- if (btnClose == null)
- {
- var tr = transform.Find(PathBtnClose);
- if (tr != null)
- btnClose = tr.GetComponent<Button>();
- }
- if (btnStep == null)
- {
- var tr = transform.Find(PathBtnStep);
- if (tr != null)
- btnStep = tr.GetComponent<Button>();
- }
- if (btnStepLabel == null && btnStep != null)
- btnStepLabel = btnStep.GetComponentInChildren<TMP_Text>(true);
- }
- void BindButtons()
- {
- if (maskButton != null)
- {
- maskButton.onClick.RemoveListener(OnMaskClicked);
- maskButton.onClick.AddListener(OnMaskClicked);
- }
- if (btnClose != null)
- {
- btnClose.onClick.RemoveListener(Close);
- btnClose.onClick.AddListener(Close);
- }
- if (btnStep != null)
- {
- btnStep.onClick.RemoveListener(OnStepButtonClicked);
- btnStep.onClick.AddListener(OnStepButtonClicked);
- }
- }
- void ApplyMaskCloseInteractable()
- {
- if (maskButton == null && maskBg != null)
- maskButton = maskBg.GetComponent<Button>();
- if (maskButton != null)
- maskButton.interactable = maskCloseOnClick;
- }
- IEnumerator PlaySlideInAfterLayout()
- {
- // 等 Canvas / Layout 算出正确宽度,避免 Start 当帧 rect.width 为 0
- yield return null;
- Canvas.ForceUpdateCanvases();
- ForceRebuildLayout();
- if (normalizeRightPosBeforeSlide && rightPanel != null)
- {
- var pos = rightPanel.anchoredPosition;
- rightPanel.anchoredPosition = new Vector2(shownAnchorPosX, pos.y);
- }
- CacheSlideAnchors();
- PlaySlideIn();
- _slideInRoutine = null;
- }
- void ForceRebuildLayout()
- {
- var canvas = GetComponentInParent<Canvas>();
- if (canvas != null)
- {
- var canvasRt = canvas.transform as RectTransform;
- if (canvasRt != null)
- LayoutRebuilder.ForceRebuildLayoutImmediate(canvasRt);
- }
- if (rightPanel != null)
- LayoutRebuilder.ForceRebuildLayoutImmediate(rightPanel);
- }
- void CacheSlideAnchors()
- {
- if (rightPanel == null)
- return;
- ForceRebuildLayout();
- _shownAnchorX = shownAnchorPosX;
- var width = GetPanelSlideWidth();
- // 右锚 + 右 Pivot:X 增大表示向右(屏外),滑入终点为 shown,起点为 shown + 宽度
- _hiddenAnchorX = _shownAnchorX + width;
- }
- float GetPanelSlideWidth()
- {
- var w = Mathf.Abs(rightPanel.rect.width);
- if (w > 1f)
- return w;
- w = Mathf.Abs(rightPanel.sizeDelta.x);
- if (w > 1f)
- return w;
- var parent = rightPanel.parent as RectTransform;
- if (parent != null)
- {
- w = Mathf.Abs(parent.rect.width);
- if (w > 1f)
- return w;
- }
- return 900f;
- }
- void CollectStepPanelsIfEmpty()
- {
- if (stepPanels != null && stepPanels.Count > 0)
- return;
- stepPanels = new List<GameObject>();
- var content = transform.Find(PathContent);
- if (content == null)
- return;
- for (var i = 0; i < content.childCount; i++)
- {
- var child = content.GetChild(i);
- if (child.name.StartsWith("Step_", StringComparison.Ordinal))
- stepPanels.Add(child.gameObject);
- }
- stepPanels.Sort((a, b) => string.CompareOrdinal(a.name, b.name));
- }
- void PlaySlideIn()
- {
- if (rightPanel == null)
- return;
- _slideTween?.Kill();
- rightPanel.DOKill();
- var pos = rightPanel.anchoredPosition;
- rightPanel.anchoredPosition = new Vector2(_hiddenAnchorX, pos.y);
- _slideTween = rightPanel
- .DOAnchorPosX(_shownAnchorX, slideDuration)
- .SetEase(slideInEase)
- .SetUpdate(true);
- }
- void PlaySlideOut(Action onComplete)
- {
- if (rightPanel == null)
- {
- onComplete?.Invoke();
- return;
- }
- CacheSlideAnchors();
- _slideTween?.Kill();
- rightPanel.DOKill();
- var pos = rightPanel.anchoredPosition;
- _slideTween = rightPanel
- .DOAnchorPosX(_hiddenAnchorX, slideDuration)
- .SetEase(slideOutEase)
- .SetUpdate(true)
- .OnComplete(() => onComplete?.Invoke());
- }
- void OnMaskClicked()
- {
- if (!maskCloseOnClick)
- return;
- Close();
- }
- void OnStepButtonClicked()
- {
- if (_stepIndex == 3)
- {
- NavigateAfterConnect();
- return;
- }
- if (StepCount <= 1)
- return;
- int next = _stepIndex + 1;
- if (next >= StepCount)
- next = 0;
- ShowStep(next);
- }
- /// <summary>切换并显示指定步骤(0 起)。</summary>
- public void ShowStep(int index, bool notify = true)
- {
- CollectStepPanelsIfEmpty();
- if (StepCount == 0)
- return;
- _stepIndex = Mathf.Clamp(index, 0, StepCount - 1);
- for (var i = 0; i < stepPanels.Count; i++)
- {
- if (stepPanels[i] != null)
- stepPanels[i].SetActive(i == _stepIndex);
- }
- RefreshStepButtonForIndex(_stepIndex);
- if (_stepIndex is >= 0 and <= 2)
- RefreshGuideSteps();
- if (_stepIndex == 3)
- {
- ResolveStep3References();
- _step3BatteryTimer = 0f;
- BindHubStep3Refresh();
- RefreshStep3ConnectedInfo();
- RefreshDeviceViewConnectContent();
- }
- else
- {
- UnbindHubStep3Refresh();
- }
- if (notify)
- OnStepChanged?.Invoke(_stepIndex);
- }
- void ResolveGuideStepReferences()
- {
- ResolveGuideStepReference(ref step0Image, ref step0Description, 0);
- ResolveGuideStepReference(ref step1Image, ref step1Description, 1);
- ResolveGuideStepReference(ref step2Image, ref step2Description, 2);
- ResolveStep3References();
- }
- void ResolveStep3References()
- {
- if (StepCount <= 3 || stepPanels[3] == null)
- return;
- Transform stepRoot = stepPanels[3].transform;
- if (step3DeviceImage == null)
- {
- Transform imageTr = stepRoot.Find("Image");
- if (imageTr != null)
- step3DeviceImage = imageTr.GetComponent<Image>();
- step3DeviceImage.SetNativeSize();
- }
- if (step3Title == null)
- {
- Transform titleTr = stepRoot.Find("title");
- if (titleTr == null)
- titleTr = stepRoot.Find("Title");
- if (titleTr != null)
- step3Title = titleTr.GetComponent<TMP_Text>();
- }
- if (step3BatteryBar == null)
- {
- Transform batteryTr = stepRoot.Find("battery");
- if (batteryTr != null)
- step3BatteryBar = batteryTr.GetComponent<Image>();
- }
- if (step3BatteryValue == null && step3BatteryBar != null)
- {
- Transform valueTr = step3BatteryBar.transform.Find("Text");
- if (valueTr != null)
- step3BatteryValue = valueTr.GetComponent<Text>();
- }
- if (step3ConnectStatus == null)
- {
- Transform statusTr = stepRoot.Find("connectStatus");
- if (statusTr == null)
- statusTr = stepRoot.Find("ConnectStatus");
- if (statusTr != null)
- step3ConnectStatus = statusTr.GetComponent<TMP_Text>();
- }
- if (step3ScreenStatus == null)
- {
- Transform screenTr = stepRoot.Find("screenStatus");
- if (screenTr == null)
- screenTr = stepRoot.Find("ScreenStatus");
- if (screenTr != null)
- step3ScreenStatus = screenTr.GetComponent<TMP_Text>();
- }
- }
- void ResolveGuideStepReference(ref Image image, ref TMP_Text description, int stepIndex)
- {
- if (stepIndex >= StepCount || stepPanels[stepIndex] == null)
- return;
- Transform stepRoot = stepPanels[stepIndex].transform;
- if (image == null)
- {
- Transform imageTr = stepRoot.Find("Image");
- if (imageTr == null)
- imageTr = stepRoot.Find("Content");
- if (imageTr != null)
- image = imageTr.GetComponent<Image>();
- }
- if (description == null)
- {
- Transform textTr = stepRoot.Find("Text (TMP)");
- if (textTr != null)
- description = textTr.GetComponent<TMP_Text>();
- }
- }
- static void EnsureGuideDescriptionLocalization(TMP_Text step0, TMP_Text step1, TMP_Text step2)
- {
- EnsureLocalizedComponent(step0);
- EnsureLocalizedComponent(step1);
- EnsureLocalizedComponent(step2);
- }
- void EnsureGuideDescriptionLocalization()
- {
- EnsureGuideDescriptionLocalization(step0Description, step1Description, step2Description);
- }
- static void EnsureLocalizedComponent(TMP_Text text)
- {
- if (text == null || text.GetComponent<AppUITextAutoLanguage>() != null)
- return;
- text.gameObject.AddComponent<AppUITextAutoLanguage>();
- }
- AimDeviceType ResolveGuideDeviceType()
- {
- if (_guideDeviceTypeOverride.HasValue && _guideDeviceTypeOverride.Value != AimDeviceType.NONE)
- return _guideDeviceTypeOverride.Value;
- SmartBowDeviceHub hub = SmartBowDeviceHub.ins;
- BLEAimhandler aim = hub?.Aim;
- if (aim == null)
- return AimDeviceType.NONE;
- BluetoothPlayer player = hub.GetActivePlayer();
- int deviceType = aim.GetDeviceType(player);
- if (deviceType == (int)AimDeviceType.NONE)
- {
- int tempType = aim.GetTempDeviceType();
- if (tempType != -1)
- deviceType = tempType;
- }
- return (AimDeviceType)deviceType;
- }
- void RefreshGuideSteps()
- {
- AimDeviceType deviceType = ResolveGuideDeviceType();
- if (deviceType == AimDeviceType.NONE)
- return;
- TryGetGuideEntry(deviceType, out SideSlipDeviceGuideEntry entry);
- ApplyStepDeviceVisualRoots(1, GetActiveDeviceVisualRoot(entry?.step1));
- ApplyStepDeviceVisualRoots(2, GetActiveDeviceVisualRoot(entry?.step2));
- if (entry == null)
- return;
- ApplyGuideStepContent(step0Image, step0Description, entry.step0);
- ApplyGuideStepContent(step1Image, step1Description, entry.step1);
- ApplyGuideStepContent(step2Image, step2Description, entry.step2);
- }
- void ApplyStepDeviceVisualRoots(int stepIndex, GameObject activeRoot)
- {
- if (guideEntries == null)
- return;
- for (int i = 0; i < guideEntries.Length; i++)
- {
- SideSlipDeviceGuideEntry guideEntry = guideEntries[i];
- if (guideEntry == null)
- continue;
- SideSlipGuideStepContent stepContent = stepIndex == 1 ? guideEntry.step1 : guideEntry.step2;
- if (!IsDeviceVisualRootEnabled(stepContent))
- continue;
- GameObject root = stepContent.deviceVisualRoot;
- if (root == null)
- continue;
- SetActiveSafe(root, root == activeRoot);
- }
- }
- static GameObject GetActiveDeviceVisualRoot(SideSlipGuideStepContent content)
- {
- if (!IsDeviceVisualRootEnabled(content))
- return null;
- return content.deviceVisualRoot;
- }
- static bool IsDeviceVisualRootEnabled(SideSlipGuideStepContent content) =>
- content != null && content.useDeviceVisualRoot;
- static void SetActiveSafe(GameObject go, bool active)
- {
- if (go == null || go.activeSelf == active)
- return;
- go.SetActive(active);
- }
- bool TryGetGuideEntry(AimDeviceType deviceType, out SideSlipDeviceGuideEntry entry)
- {
- entry = null;
- if (guideEntries == null)
- return false;
- for (int i = 0; i < guideEntries.Length; i++)
- {
- SideSlipDeviceGuideEntry candidate = guideEntries[i];
- if (candidate == null || candidate.deviceType != deviceType)
- continue;
- entry = candidate;
- return true;
- }
- return false;
- }
- static void ApplyGuideStepContent(Image image, TMP_Text description, SideSlipGuideStepContent content)
- {
- if (content == null)
- return;
- if (content.useImage && image != null && content.image != null)
- {
- image.sprite = content.image;
- image.SetNativeSize();
- image.enabled = true;
- }
- if (description == null)
- return;
- if (content.useDescriptionKey && !string.IsNullOrEmpty(content.descriptionKey))
- {
- AppUITextAutoLanguage localized = description.GetComponent<AppUITextAutoLanguage>();
- if (localized != null)
- {
- localized.SetTextKey(content.descriptionKey);
- return;
- }
- description.text = AppUILocalization.GetTextByKey(content.descriptionKey);
- return;
- }
- if (content.useDescription && !string.IsNullOrEmpty(content.description))
- description.text = content.description;
- }
- void RefreshStep3ConnectedInfo()
- {
- SmartBowDeviceHub hub = SmartBowDeviceHub.ins;
- BLEAimhandler aim = hub?.Aim;
- if (hub == null || aim == null)
- return;
- BluetoothPlayer player = hub.GetActivePlayer();
- int deviceType = aim.GetDeviceType(player);
- if (deviceType != (int)AimDeviceType.NONE)
- {
- var deviceTypeEnum = (AimDeviceType)deviceType;
- if (step3Title != null)
- step3Title.text = hub.GetDeviceDisplayName(deviceTypeEnum);
- Sprite sprite = hub.GetDeviceDisplaySprite(deviceTypeEnum);
- if (step3DeviceImage != null && sprite != null)
- {
- step3DeviceImage.sprite = sprite;
- step3DeviceImage.SetNativeSize();
- }
- }
- RefreshStep3Battery();
- RefreshStep3StatusTexts(hub, aim, player);
- }
- void BindHubStep3Refresh()
- {
- SmartBowDeviceHub hub = SmartBowDeviceHub.ins;
- if (hub == null || _hubStep3RefreshBound)
- return;
- hub.OnStateChanged += OnHubStateChangedForStep3;
- _hubStep3RefreshBound = true;
- }
- void UnbindHubStep3Refresh()
- {
- if (!_hubStep3RefreshBound)
- return;
- SmartBowDeviceHub hub = SmartBowDeviceHub.ins;
- if (hub != null)
- hub.OnStateChanged -= OnHubStateChangedForStep3;
- _hubStep3RefreshBound = false;
- }
- void OnHubStateChangedForStep3(SmartBowDeviceState state)
- {
- if (_stepIndex != 3)
- return;
- RefreshStep3Battery();
- SmartBowDeviceHub hub = SmartBowDeviceHub.ins;
- BLEAimhandler aim = hub?.Aim;
- if (hub == null || aim == null)
- return;
- RefreshStep3StatusTexts(hub, aim, hub.GetActivePlayer());
- }
- void RefreshStep3Battery()
- {
- SmartBowDeviceHub hub = SmartBowDeviceHub.ins;
- if (hub == null)
- return;
- BluetoothPlayer player = hub.GetActivePlayer();
- SmartBowHelper helper = hub.GetHelper(player);
- int battery = helper != null ? helper.GetBattery() : hub.GetBattery(player);
- battery = Mathf.Clamp(battery, 0, 100);
- if (step3BatteryBar != null)
- step3BatteryBar.fillAmount = battery / 100f;
- if (step3BatteryValue != null)
- step3BatteryValue.text = battery + "%";
- }
- void RefreshStep3StatusTexts(SmartBowDeviceHub hub, BLEAimhandler aim, BluetoothPlayer player)
- {
- bool connected = hub.IsConnected(player);
- if (step3ConnectStatus != null)
- {
- string statusKey = hub.GetStatusTextKey(player);
- step3ConnectStatus.text = AppUILocalization.GetTextByKey(statusKey, step3ConnectStatus);
- step3ConnectStatus.color = connected ? StatusCompleteColor : StatusIncompleteColor;
- }
- if (step3ScreenStatus == null)
- return;
- if (!BLEAimhandler.IsInfraredDeviceType(aim.GetDeviceType(player)))
- {
- step3ScreenStatus.gameObject.SetActive(false);
- return;
- }
- step3ScreenStatus.gameObject.SetActive(true);
- bool positioned = IsScreenPositioned();
- step3ScreenStatus.text = AppUILocalization.GetTextByKey(
- positioned ? KeyScreenPositioningDone : KeyScreenPositioningIncomplete, step3ScreenStatus);
- step3ScreenStatus.color = positioned ? StatusCompleteColor : StatusIncompleteColor;
- }
- static bool IsScreenPositioned()
- {
- InfraredCameraHelper helper = InfraredCameraHelper.GetInstance();
- return helper != null && helper.IsScreenLoateOK();
- }
- void RefreshStepButtonForIndex(int index)
- {
- if (btnStepLabel == null)
- return;
- switch (index)
- {
- case 0:
- btnStepLabel.text = AppUILocalization.GetTextByKey(KeyBtnNext);
- if (btnStep != null)
- btnStep.interactable = true;
- break;
- case 1:
- btnStepLabel.text = AppUILocalization.GetTextByKey(KeyBtnStartConnect);
- if (btnStep != null)
- btnStep.interactable = true;
- break;
- case 2:
- btnStepLabel.text = AppUILocalization.GetTextByKey(KeyBtnConnecting);
- OnClick_ConnectBLE();
- if (btnStep != null)
- btnStep.interactable = false;
- break;
- case 3:
- ApplyPostConnectStepLabel();
- if (btnStep != null)
- btnStep.interactable = true;
- break;
- }
- }
- void ApplyPostConnectStepLabel()
- {
- BLEAimhandler aim = SmartBowDeviceHub.ins?.Aim;
- if (aim != null && aim.IsActivePlayerInfraredDevice())
- {
- btnStepLabel.text = AppUILocalization.GetTextByKey(KeyBtnScreenPositioning);
- return;
- }
- if (aim != null && aim.IsActivePlayerNineAxisDevice())
- {
- btnStepLabel.text = AppUILocalization.GetTextByKey(KeyBtnNext);
- return;
- }
- btnStepLabel.text = AppUILocalization.GetTextByKey(KeyBtnDone);
- }
- void NavigateAfterConnect()
- {
- AudioMgr.ins.PlayBtn();
- SmartBowDeviceHub hub = SmartBowDeviceHub.ins;
- BLEAimhandler aim = hub?.Aim;
- Close();
- if (aim == null)
- return;
- if (aim.IsActivePlayerInfraredDevice())
- {
- ViewManager.ShowView(UIViewType.InfraredView);
- }
- else if (aim.IsActivePlayerNineAxisDevice())
- {
- hub?.EnsureNineAxisSensorsStarted();
- ViewManager.ShowView(UIViewType.GyrGuidanceView);
- }
- }
- /// <summary>九轴设备需等 moduleInited 后再进入步骤 3,并预开传感(对齐 DemoStarter)。</summary>
- static bool TryAdvanceToPostConnectStep(SmartBowDeviceHub hub)
- {
- BLEAimhandler aim = hub?.Aim;
- if (aim == null)
- return true;
- if (!aim.IsActivePlayerNineAxisDevice())
- return true;
- BluetoothPlayer player = hub.GetActivePlayer();
- if (!hub.IsModuleInited(player))
- return false;
- hub.EnsureNineAxisSensorsStarted(player);
- return true;
- }
- void RefreshStepButtonLabel()
- {
- if (btnStepLabel == null)
- return;
- btnStepLabel.text = StepCount <= 1
- ? AppUILocalization.GetTextByKey(KeyBtnStep)
- : string.Format(
- AppUILocalization.GetTextByKey(KeyBtnStepProgress),
- _stepIndex + 1,
- StepCount);
- }
- /// <summary>关闭:右侧面板滑出后销毁。</summary>
- public void Close()
- {
- if (_closing)
- return;
- _closing = true;
- if (maskButton != null)
- maskButton.interactable = false;
- if (btnClose != null)
- btnClose.interactable = false;
- if (btnStep != null)
- btnStep.interactable = false;
- PlaySlideOut(() =>
- {
- UnbindHubStep3Refresh();
- SmartBowDeviceHub hub = SmartBowDeviceHub.ins;
- if (hub != null && hub.IsConnected(hub.GetActivePlayer()))
- {
- // 未打开时 HideView 仅清理缓存,不会抛错
- ViewManager.HideView(UIViewType.DeviceArcheryView);
- ViewManager.HideView(UIViewType.DeviceGunView);
- }
- RefreshDeviceViewConnectContent();
- onClosed?.Invoke();
- Destroy(gameObject);
- });
- }
- /// <summary>侧滑关闭或连接成功时,关闭设备选型子页并刷新 DeviceView。</summary>
- static void RefreshDeviceViewConnectContent()
- {
- AppUIDeviceView active = AppUIDeviceView.Active;
- if (active == null) return;
- active.EndDeviceSelectionFlow();
- active.RefreshConnectContent(force: true);
- }
- #region 操作蓝牙连接的按钮事件,放在这里是因为这个侧滑面板目前只用在连接蓝牙设备的引导上,如果后续有其他用途再考虑重构
- //[SerializeField] GameObject btnConnectBow;
- //[SerializeField] Text btnOmitText;
- private int dotCount = 0;
- private float interval = 0.5f; // 标点更新间隔
- private float timer = 0f;
- bool isNext = false;
- SmartBowSDK.BluetoothStatusEnum bowStatus1P;
- SmartBowSDK.BluetoothStatusEnum bowStatus2P;
- public void OnClick_ConnectBLE()
- {
-
- //此页面连接当作重新初始化
- resetAimDevice();
- SmartBowDeviceHub hub = SmartBowDeviceHub.ins;
- if (hub == null) return;
- Debug.Log("SmartBowDeviceHub ActivePlayer:" + hub.GetActivePlayer());
- hub.ConnectActivePlayer();
- }
- void UpdateBtnForConnect()
- {
- SmartBowDeviceHub hub = SmartBowDeviceHub.ins;
- if (hub == null) return;
- if (hub.GetActivePlayer() == BluetoothPlayer.FIRST_PLAYER)
- {
- SmartBowSDK.BluetoothStatusEnum status1P = hub.GetSdkStatus(BluetoothPlayer.FIRST_PLAYER);
- if (bowStatus1P != status1P)
- {
- bowStatus1P = status1P;
- }
- if (bowStatus1P != SmartBowSDK.BluetoothStatusEnum.Connected)
- {
- isNext = false;
- }
- else if (!isNext && bowStatus1P == SmartBowSDK.BluetoothStatusEnum.Connected)
- {
- if (TryAdvanceToPostConnectStep(hub))
- {
- isNext = true;
- ShowStep(3);
- }
- }
- }
- else if (hub.GetActivePlayer() == BluetoothPlayer.SECOND_PLAYER)
- {
- SmartBowSDK.BluetoothStatusEnum status2P = hub.GetSdkStatus(BluetoothPlayer.SECOND_PLAYER);
- if (bowStatus2P != status2P)
- {
- bowStatus2P = status2P;
- }
- if (bowStatus2P != SmartBowSDK.BluetoothStatusEnum.Connected)
- {
- isNext = false;
- }
- if (!isNext && bowStatus2P == SmartBowSDK.BluetoothStatusEnum.Connected)
- {
- if (TryAdvanceToPostConnectStep(hub))
- {
- isNext = true;
- ShowStep(3);
- }
- }
- }
- }
- public void resetAimDevice()
- {
- BLEAimhandler aim = SmartBowDeviceHub.ins?.Aim;
- if (aim == null) return;
- aim.onCreateAimDeviceInfoById();
- if (!aim.SideSlipOverwritesDeviceInfo)
- {
- Debug.Log("[SideSlip] 直接连接,沿用已记录 MAC bInitMac:" + aim.aimDeviceInfo.bInitMac);
- return;
- }
- // 切换设备页已写入 type 并清空 MAC,此处仅结束覆盖标记
- aim.ClearSideSlipOverwriteFlag();
- Debug.Log("[SideSlip] 切换选设备连接 bInitMac:" + aim.aimDeviceInfo.bInitMac);
- }
- public void resetAimType()
- {
- BLEAimhandler aim = SmartBowDeviceHub.ins?.Aim;
- if (aim == null) return;
- aim.onCreateAimDeviceInfoById();
- if (!aim.SideSlipOverwritesDeviceInfo)
- return;
- if (aim.GetTempDeviceType() != -1)
- aim.SetAimDeviceType(aim.GetTempDeviceType());
- }
- #endregion
- static class SideSlipUIBuilder
- {
- const string BuilderMaskBg = "BG";
- const string BuilderRight = "Right";
- const string BuilderBtnClose = "BtnClose";
- const string BuilderContent = "Content";
- const string BuilderBottomBar = "BottomBar";
- const string BuilderBtnStep = "BtnStep";
- static readonly Color MaskColor = new Color(0f, 0f, 0f, 0.55f);
- static readonly Color PlaceholderTextColor = new Color(0.35f, 0.35f, 0.35f, 1f);
- public static void EnsureHierarchy(Transform root)
- {
- if (root == null)
- return;
- var layer = root.gameObject.layer;
- EnsureMask(root, layer);
- var right = EnsureRight(root, layer);
- if (right == null)
- return;
- EnsureBtnClose(right, layer);
- EnsureContent(right, layer);
- EnsureBottomBar(right, layer);
- }
- static void EnsureMask(Transform root, int layer)
- {
- var bg = root.Find(BuilderMaskBg);
- if (bg == null)
- {
- var go = CreateRect(root, BuilderMaskBg, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f),
- Vector2.zero, Vector2.zero, layer);
- bg = go.transform;
- }
- var image = bg.GetComponent<Image>();
- if (image == null)
- image = bg.gameObject.AddComponent<Image>();
- image.color = MaskColor;
- image.raycastTarget = true;
- if (bg.GetComponent<Button>() == null)
- {
- var btn = bg.gameObject.AddComponent<Button>();
- btn.transition = Selectable.Transition.None;
- btn.targetGraphic = image;
- }
- }
- static RectTransform EnsureRight(Transform root, int layer)
- {
- var right = root.Find(BuilderRight) as RectTransform;
- if (right != null)
- return right;
- var go = CreateRect(root, BuilderRight, new Vector2(1f, 0f), new Vector2(1f, 1f),
- new Vector2(1f, 0.5f), Vector2.zero, new Vector2(900f, 0f), layer);
- right = go.GetComponent<RectTransform>();
- var image = go.AddComponent<Image>();
- image.color = Color.white;
- image.raycastTarget = true;
- return right;
- }
- static void EnsureBtnClose(RectTransform right, int layer)
- {
- if (right.Find(BuilderBtnClose) != null)
- return;
- var go = CreateRect(right, BuilderBtnClose, new Vector2(0f, 1f), new Vector2(0f, 1f),
- new Vector2(0f, 1f), new Vector2(48f, -48f), new Vector2(72f, 72f), layer);
- var image = go.AddComponent<Image>();
- image.color = new Color(1f, 1f, 1f, 0.9f);
- image.raycastTarget = true;
- var btn = go.AddComponent<Button>();
- btn.targetGraphic = image;
- var labelGo = CreateRect(go.transform, "Label", Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f),
- Vector2.zero, Vector2.zero, layer);
- var tmp = labelGo.AddComponent<TextMeshProUGUI>();
- tmp.text = "×";
- tmp.fontSize = 48;
- tmp.alignment = TextAlignmentOptions.Center;
- tmp.color = Color.black;
- tmp.raycastTarget = false;
- }
- static void EnsureContent(RectTransform right, int layer)
- {
- var content = right.Find(BuilderContent) as RectTransform;
- if (content == null)
- {
- var go = CreateRect(right, BuilderContent, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f),
- Vector2.zero, Vector2.zero, layer);
- content = go.GetComponent<RectTransform>();
- content.offsetMin = new Vector2(32f, 140f);
- content.offsetMax = new Vector2(-32f, -120f);
- }
- EnsureStepPlaceholder(content, "Step_0", "步骤 1 — 内容待开发", layer);
- EnsureStepPlaceholder(content, "Step_1", "步骤 2 — 内容待开发", layer);
- }
- static void EnsureStepPlaceholder(RectTransform content, string stepName, string placeholder, int layer)
- {
- if (content.Find(stepName) != null)
- return;
- var go = CreateRect(content, stepName, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f),
- Vector2.zero, Vector2.zero, layer);
- var tmp = go.AddComponent<TextMeshProUGUI>();
- tmp.text = placeholder;
- tmp.fontSize = 36;
- tmp.alignment = TextAlignmentOptions.TopLeft;
- tmp.color = PlaceholderTextColor;
- tmp.raycastTarget = false;
- go.SetActive(stepName == "Step_0");
- }
- static void EnsureBottomBar(RectTransform right, int layer)
- {
- if (right.Find(BuilderBottomBar) != null)
- return;
- var bar = CreateRect(right, BuilderBottomBar, new Vector2(0f, 0f), new Vector2(1f, 0f),
- new Vector2(0.5f, 0f), new Vector2(0f, 48f), new Vector2(0f, 120f), layer);
- var btnGo = CreateRect(bar.transform, BuilderBtnStep, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f),
- new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(420f, 88f), layer);
- var img = btnGo.AddComponent<Image>();
- img.color = new Color(0.2f, 0.55f, 1f, 1f);
- img.raycastTarget = true;
- var btn = btnGo.AddComponent<Button>();
- btn.targetGraphic = img;
- var labelGo = CreateRect(btnGo.transform, "Text (TMP)", Vector2.zero, Vector2.one,
- new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero, layer);
- var tmp = labelGo.AddComponent<TextMeshProUGUI>();
- tmp.text = "切换步骤";
- tmp.fontSize = 32;
- tmp.alignment = TextAlignmentOptions.Center;
- tmp.color = Color.white;
- tmp.raycastTarget = false;
- }
- static GameObject CreateRect(Transform parent, string name, Vector2 anchorMin, Vector2 anchorMax,
- Vector2 pivot, Vector2 anchoredPosition, Vector2 sizeDelta, int layer)
- {
- var go = new GameObject(name, typeof(RectTransform));
- go.layer = layer;
- var rt = go.GetComponent<RectTransform>();
- rt.SetParent(parent, false);
- rt.anchorMin = anchorMin;
- rt.anchorMax = anchorMax;
- rt.pivot = pivot;
- rt.anchoredPosition = anchoredPosition;
- rt.sizeDelta = sizeDelta;
- rt.localScale = Vector3.one;
- rt.localRotation = Quaternion.identity;
- return go;
- }
- }
- }
- }
|