using Assets.MyGame.Scripts.Net; using Assets.MyGame.Scripts.Net.API; using Assets.MyGame.Scripts.Net.WebResponses; using DG.Tweening; using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.InputSystem; using UnityEngine.SceneManagement; using UnityEngine.UI; public class GameMainPanel_XJMH : GameMainPanel_FishBase { // ==================== 炮台扇形切换 UI ==================== [Header("扇形切换")] [Tooltip("扇形半径(像素)")] public float fanRadius = 160f; [Tooltip("扇形展开总角度")] public float fanArcSpan = 150f; [Tooltip("扇形单项尺寸")] public Vector2 fanItemSize = new Vector2(90, 90); [Tooltip("滑动手势死区半径(像素),小于此距离不高亮")] public float fanDeadZone = 40f; private GameObject _fanRoot; private List _fanTurretNumbers; private int _fanSelectedIndex = -1; public override void Awake() { base.Awake(); } public override void Start() { base.Start(); } protected override void OnShow(object args = null) { base.OnShow(args); AudioManager.Instance.ChangeBG(61109); } // ==================== 扇形切换:显示/更新/隐藏 ==================== /// 长按炮台达到阈值后弹出扇形切换UI protected override void OnTurretSwitcherShow(Vector2 pressPos) { _fanTurretNumbers = currSitData.GetAvailableTurretNumbers(); if (_fanTurretNumbers == null || _fanTurretNumbers.Count == 0) { _fanTurretNumbers = null; return; } // 扇形根节点挂在炮台父节点下,和炮台共享坐标系 Transform anchor = currSitData.turretParent != null ? currSitData.turretParent : currSitData.currentTurret.transform.parent; _fanRoot = new GameObject("TurretFanSwitcher"); _fanRoot.transform.SetParent(anchor, false); var fanRect = _fanRoot.AddComponent(); fanRect.anchoredPosition = currSitData.turretAnchoredPos; fanRect.localScale = Vector3.zero; // 上方座位(2,3)扇形朝下展开,下方座位(0,1)朝上展开 bool flip = currSitData.IsFlipped; float startAngle = flip ? fanArcSpan / 2f : -fanArcSpan / 2f; int count = _fanTurretNumbers.Count; float step = count > 1 ? fanArcSpan / (count - 1) : 0; for (int i = 0; i < count; i++) { float angle = startAngle + step * i; float rad = angle * Mathf.Deg2Rad; // 0°=正上方,扇形沿 Y 轴展开 Vector2 localPos = new Vector2(Mathf.Sin(rad), Mathf.Cos(rad)) * fanRadius; var item = new GameObject($"FanItem_{_fanTurretNumbers[i]}"); item.transform.SetParent(_fanRoot.transform, false); var rt = item.AddComponent(); rt.anchoredPosition = localPos; rt.sizeDelta = fanItemSize; var img = item.AddComponent(); img.color = new Color(1f, 1f, 1f, 0.55f); img.raycastTarget = false; // 炮台编号文字 var txtGo = new GameObject("Num"); txtGo.transform.SetParent(item.transform, false); var txtRt = txtGo.AddComponent(); txtRt.anchorMin = txtRt.anchorMax = new Vector2(0.5f, 0.5f); txtRt.sizeDelta = fanItemSize; var tmp = txtGo.AddComponent(); tmp.text = _fanTurretNumbers[i].ToString(); tmp.alignment = TextAlignmentOptions.Center; tmp.fontSize = 36; tmp.color = Color.white; tmp.raycastTarget = false; // 弹出动画(错峰) item.transform.localScale = Vector3.zero; item.transform.DOScale(Vector3.one, 0.18f) .SetDelay(i * 0.02f) .SetEase(Ease.OutBack); } // 整体缩放进入 _fanRoot.transform.DOScale(Vector3.one, 0.2f).SetEase(Ease.OutBack); _fanSelectedIndex = -1; } /// 手指滑动时更新扇形高亮 protected override void OnTurretSwitcherUpdate(Vector2 currentPos) { if (_fanRoot == null || _fanTurretNumbers == null || _fanTurretNumbers.Count == 0) return; int newIndex = CalcFanIndexByScreenPos(currentPos); if (newIndex != _fanSelectedIndex) { _fanSelectedIndex = newIndex; for (int i = 0; i < _fanRoot.transform.childCount; i++) { var img = _fanRoot.transform.GetChild(i).GetComponent(); if (img == null) continue; img.color = (i == _fanSelectedIndex) ? new Color(1f, 0.85f, 0.3f, 0.95f) : new Color(1f, 1f, 1f, 0.55f); } } } /// 手指松开,切换炮台并关闭扇形UI protected override void OnTurretSwitcherHide(Vector2 releasePos) { int selectedIndex = CalcFanIndexByScreenPos(releasePos); if (_fanRoot != null) { _fanRoot.transform.DOScale(Vector3.zero, 0.15f).SetEase(Ease.InBack) .OnComplete(() => { if (_fanRoot != null) Destroy(_fanRoot); }); _fanRoot = null; } if (selectedIndex >= 0 && selectedIndex < _fanTurretNumbers.Count) { int selectedTurret = _fanTurretNumbers[selectedIndex]; if (selectedTurret != currSitData.currentTurretNumber) { currSitData.LoadTurret(selectedTurret); SyncChangeTurret(selectedTurret); } } _fanTurretNumbers = null; _fanSelectedIndex = -1; } /// 面板隐藏时清理扇形UI对象 protected override void CleanupTurretSwitcher() { base.CleanupTurretSwitcher(); if (_fanRoot != null) { Destroy(_fanRoot); _fanRoot = null; } _fanTurretNumbers = null; _fanSelectedIndex = -1; } /// /// 根据屏幕坐标计算扇形索引。 /// 把屏幕坐标转换到扇形根节点的本地坐标,再用角度匹配扇区。 /// private int CalcFanIndexByScreenPos(Vector2 screenPos) { if (_fanRoot == null || _fanTurretNumbers == null || _fanTurretNumbers.Count == 0) return -1; RectTransform fanRect = _fanRoot.GetComponent(); Camera cam = null; Canvas canvas = UIManager.Instance != null && UIManager.Instance.uiRoot != null ? UIManager.Instance.uiRoot.GetComponent() : null; if (canvas != null && canvas.renderMode != RenderMode.ScreenSpaceOverlay) cam = canvas.worldCamera; // 屏幕坐标 → 扇形根节点本地坐标 RectTransformUtility.ScreenPointToLocalPointInRectangle( fanRect, screenPos, cam, out Vector2 localPos); // 死区 if (localPos.magnitude < fanDeadZone) return -1; // 计算角度:0°=正上方(扇形中心方向),顺时针为正 float angle = Mathf.Atan2(localPos.x, localPos.y) * Mathf.Rad2Deg; bool flip = currSitData.IsFlipped; float startAngle = flip ? fanArcSpan / 2f : -fanArcSpan / 2f; int count = _fanTurretNumbers.Count; float step = count > 1 ? fanArcSpan / (count - 1) : 0; // 找到角度差最小的扇区 int bestIdx = -1; float bestDelta = float.MaxValue; for (int i = 0; i < count; i++) { float centerAngle = startAngle + step * i; float delta = Mathf.Abs(Mathf.DeltaAngle(angle, centerAngle)); if (delta < bestDelta) { bestDelta = delta; bestIdx = i; } } // 允许的误差范围(半个扇区宽度 + 余量) float halfSector = step > 0 ? step * 0.6f + 15f : 90f; return bestDelta <= halfSector ? bestIdx : -1; } }