Files
5.24BuYu/Assets/MyGame/aa/UI/Game/XJMH/GameMainPanel_XJMH.cs
T
2026-07-28 18:29:49 +08:00

225 lines
8.0 KiB
C#

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<int> _fanTurretNumbers;
private int _fanSelectedIndex = -1;
public override void Awake()
{
base.Awake();
}
public override void Start()
{
base.Start();
}
// ==================== 扇形切换:显示/更新/隐藏 ====================
/// <summary>长按炮台达到阈值后弹出扇形切换UI</summary>
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<RectTransform>();
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<RectTransform>();
rt.anchoredPosition = localPos;
rt.sizeDelta = fanItemSize;
var img = item.AddComponent<Image>();
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<RectTransform>();
txtRt.anchorMin = txtRt.anchorMax = new Vector2(0.5f, 0.5f);
txtRt.sizeDelta = fanItemSize;
var tmp = txtGo.AddComponent<TextMeshProUGUI>();
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;
}
/// <summary>手指滑动时更新扇形高亮</summary>
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<Image>();
if (img == null) continue;
img.color = (i == _fanSelectedIndex)
? new Color(1f, 0.85f, 0.3f, 0.95f)
: new Color(1f, 1f, 1f, 0.55f);
}
}
}
/// <summary>手指松开,切换炮台并关闭扇形UI</summary>
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;
}
/// <summary>面板隐藏时清理扇形UI对象</summary>
protected override void CleanupTurretSwitcher()
{
base.CleanupTurretSwitcher();
if (_fanRoot != null)
{
Destroy(_fanRoot);
_fanRoot = null;
}
_fanTurretNumbers = null;
_fanSelectedIndex = -1;
}
/// <summary>
/// 根据屏幕坐标计算扇形索引。
/// 把屏幕坐标转换到扇形根节点的本地坐标,再用角度匹配扇区。
/// </summary>
private int CalcFanIndexByScreenPos(Vector2 screenPos)
{
if (_fanRoot == null || _fanTurretNumbers == null || _fanTurretNumbers.Count == 0)
return -1;
RectTransform fanRect = _fanRoot.GetComponent<RectTransform>();
Camera cam = null;
Canvas canvas = UIManager.Instance != null && UIManager.Instance.uiRoot != null
? UIManager.Instance.uiRoot.GetComponent<Canvas>()
: 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;
}
}