225 lines
8.0 KiB
C#
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;
|
|
}
|
|
}
|