Files
5.24BuYu/Assets/MyGame/Scripts/Turret/SitPosData.cs
T
2026-07-16 20:16:05 +08:00

487 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class SitPosData : MonoBehaviour
{
/// <summary>本地玩家的座位引用(由游戏面板初始化时设置)</summary>
public static SitPosData localPlayerSit;
/// <summary>当前房间房主座位(人机请求由房主发送,房主退出后自动继承)</summary>
public static SitPosData roomOwnerSit;
// -------------------------
// 玩家基础信息
// -------------------------
[Header("玩家信息")]
public string user;
[Tooltip("座位编号 0~3。0,1=下方 2,3=上方")]
public int posID;
[Tooltip("是否为人机")]
public bool isBot = false;
/// <summary>posID>=2 = 上方,需要翻转(0,1=下方;2,3=上方)</summary>
public bool IsFlipped => posID >= 2;
[Header("金币")]
public long goldNum;
public long consumeGoldNum;
public TextMeshProUGUI tMP_GoldNum;
public TextMeshProUGUI tMP_DiamondNum;
public TextMeshProUGUI tMP_TurretConsumeNum;
[Header("翅膀")]
[Tooltip("翅膀实例化的父节点")]
public Transform wingParent;
// -------------------------
// 炮台管理(动态克隆)
// -------------------------
[Header("炮台")]
[Tooltip("炮台实例化的父节点")]
public Transform turretParent;
[Tooltip("炮台锚点位置(相对 turretParent)")]
public Vector2 turretAnchoredPos = Vector2.zero;
[Tooltip("当前装备的炮台编号")]
public int currentTurretNumber;
/// <summary>当前运行中的炮台实例</summary>
public TurretData currentTurret;
// 状态机入口(GameMainPanel 调用)
public Action<TurretState> OnStateChange;
// -------------------------
// 炮台旋转
// -------------------------
[Header("炮台旋转")]
public Transform tra_CurrTurret;
public float currentAngle;
/// <summary>
/// 炮台在世界坐标(Z=0平面)的位置缓存。
/// 因为炮台是 UGUI 元素,tra_CurrTurret.position 是 Canvas 坐标,不能直接和鱼的世界坐标比较距离。
/// 这里用和子弹起点(GetBulletStartWorldPos)相同的转换方式,把 UI 坐标转成世界坐标。
/// 由 LoadTurret 加载完炮台后计算并缓存,bot AI/锁定检测距离时用此字段。
/// </summary>
[System.NonSerialized]
public Vector3 turretWorldPos;
// -------------------------
// 锁定系统
// -------------------------
[Header("锁定系统")]
public MonsterContral monCotl_Lock;
public Transform monCotl_Lock_Position;
public bool isLock = false;
public GameObject lockFX; // 主锁定图标(单发/双倍用)
public List<GameObject> lockFX_Triple = new List<GameObject>(); // 三发锁定额外2个图标
public List<MonsterContral> tripleLockTargets = new List<MonsterContral>();
public List<Transform> tripleLockPositions = new List<Transform>();
// -------------------------
// UI
// -------------------------
[Header("UI")]
public Button btn_TurretAdd;
public Button btn_TurretMinus;
private void Awake()
{
btn_TurretAdd.onClick.AddListener(() => ChangeTurretAddConsumeNum());
btn_TurretMinus.onClick.AddListener(() => ChangeTurretMinusConsumeNum());
}
private void OnEnable()
{
// 默认显示等待界面
ShowWaiting();
}
private void OnDisable()
{
if (localPlayerSit == this)
localPlayerSit = null;
// 房主退出 → 自动继承给第一个非人机玩家
if (roomOwnerSit == this)
{
roomOwnerSit = null;
TransferRoomOwner();
}
}
/// <summary>
/// 玩家加入 → 显示玩家 UI(child0),隐藏等待界面(child1)。
/// </summary>
public void ShowPlayer()
{
if (transform.childCount > 0)
transform.GetChild(0).gameObject.SetActive(true);
if (transform.childCount > 1)
transform.GetChild(1).gameObject.SetActive(false);
// 如果是本地玩家的座位,自动注册并加载装备
if (user == GameManager.Instance?.user?.id)
{
localPlayerSit = this;
var cannonUsing = GameManager.Instance.user.cannonUsing;
if (cannonUsing != null && int.TryParse(cannonUsing.number, out int num))
LoadTurret(num);
var wingUsing = GameManager.Instance.user.wingUsing;
if (wingUsing != null && int.TryParse(wingUsing.number, out int wingNum))
LoadWing(wingNum);
}
// 非人机玩家加入时,自动分配为首个房主
if (!isBot)
AssignRoomOwner(this);
}
/// <summary>
/// 玩家退出/等待 → 显示等待界面(child1),隐藏玩家 UI(child0)。
/// </summary>
public void ShowWaiting()
{
if (transform.childCount > 0)
transform.GetChild(0).gameObject.SetActive(false);
if (transform.childCount > 1)
transform.GetChild(1).gameObject.SetActive(true);
}
/// <summary>房主退出后,将房主身份转移给第一个活跃的非人机玩家</summary>
public static void TransferRoomOwner()
{
var list = FindObjectsOfType<SitPosData>();
foreach (var sit in list)
{
if (!sit.isBot && sit.gameObject.activeInHierarchy && sit != roomOwnerSit)
{
roomOwnerSit = sit;
// 同步出鱼权限:继承者是本地玩家时接管出鱼
if (CreateFishManager.Instance != null)
{
CreateFishManager.Instance.fangzhu =
sit.user == GameManager.Instance?.user?.id;
}
break;
}
}
}
/// <summary>分配房主(首个非人机玩家)</summary>
public static void AssignRoomOwner(SitPosData sit)
{
if (roomOwnerSit == null && !sit.isBot)
{
roomOwnerSit = sit;
// 同步出鱼权限:本地玩家作为首个非人机玩家时接管出鱼
if (CreateFishManager.Instance != null)
{
CreateFishManager.Instance.fangzhu =
sit.user == GameManager.Instance?.user?.id;
}
}
}
// ==================== 炮台加载(动态克隆) ====================
/// <summary>
/// 根据炮台编号,从 Addressables 加载预制体并克隆到 turretParent。
/// 位置/旋转/缩放保持预制体原始值,不动。
/// </summary>
public void LoadTurret(int turretNumber)
{
// ⭐ 换炮台时只清活跃子弹(清屏),保留池缓存(同 prefab 能复用)
// 用 ClearActive 而非 ClearAll:ClearAll 会清池导致永远不复用
BulletPool.Instance.ClearActive();
if (currentTurret != null)
{
Destroy(currentTurret.gameObject);
currentTurret = null;
}
currentTurretNumber = turretNumber;
// 从 TurretResourcesData 拿预制体
GameObject prefab = TurretResourcesData.GetTurretPrefab(turretNumber);
if (prefab == null)
{
Debug.LogWarning($"[SitPosData] 炮台预制体未加载: #{turretNumber}");
return;
}
// 克隆到父节点
GameObject instance = Instantiate(prefab, turretParent, false);
instance.SetActive(true);
// 固定位置归零,由外部父节点定位
var rt = instance.GetComponent<RectTransform>();
if (rt != null)
rt.anchoredPosition = turretAnchoredPos;
currentTurret = instance.GetComponent<TurretData>();
if (currentTurret == null)
{
Debug.LogError($"[SitPosData] 预制体上未找到 TurretData 组件: #{turretNumber}");
Destroy(instance);
return;
}
currentTurret.turretNumber = turretNumber;
// 缓存旋转节点
//tra_CurrTurret = currentTurret.transform.GetChild(0);
tra_CurrTurret = currentTurret.transform.parent;
// 绑定状态机入口
OnStateChange = currentTurret.ChangeState;
// ⭐ 缓存炮台在世界坐标(Z=0平面)的位置,bot AI/锁定检测距离时用
// 和子弹起点(GetBulletStartWorldPos)用相同的 UI→世界 转换方式
UpdateTurretWorldPos();
Debug.Log($"[SitPosData] 炮台已加载: #{turretNumber}");
}
/// <summary>
/// 把炮台 UI 坐标转换成世界坐标(Z=0平面)并缓存到 turretWorldPos。
/// 转换方式和 TurretData.GetBulletStartWorldPos 一致。
/// </summary>
public void UpdateTurretWorldPos()
{
if (tra_CurrTurret == null)
{
turretWorldPos = Vector3.zero;
return;
}
RectTransform rect = tra_CurrTurret.GetComponent<RectTransform>();
if (rect != null && UIManager.Instance != null && UIManager.Instance.uiRoot != null)
{
Canvas canvas = UIManager.Instance.uiRoot.GetComponent<Canvas>();
Camera cam = Camera.main;
Vector3 screenPos = RectTransformUtility.WorldToScreenPoint(
canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera,
rect.position
);
float distance = Mathf.Abs(cam.transform.position.z - 0f);
Vector3 worldPos = cam.ScreenToWorldPoint(new Vector3(screenPos.x, screenPos.y, distance));
worldPos.z = 0f;
turretWorldPos = worldPos;
}
else
{
// 非 UI(3D 物体)直接用 position
turretWorldPos = tra_CurrTurret.position;
}
}
/// <summary>根据炮台编号字符串加载。</summary>
public void LoadTurret(string turretNumberStr)
{
if (int.TryParse(turretNumberStr, out int num))
LoadTurret(num);
}
// ==================== 翅膀加载(动态克隆) ====================
/// <summary>
/// 根据炮台编号,从 Addressables 加载预制体并克隆到 turretParent。
/// 位置/旋转/缩放保持预制体原始值,不动。
/// </summary>
public void LoadWing(int turretNumber)
{
// 先清理已有翅膀(保留预设的 Camera,它是用来渲染翅膀到 RawImage 的)
for (int i = wingParent.childCount - 1; i >= 0; i--)
{
var child = wingParent.GetChild(i);
if (child.GetComponent<Camera>() != null)
continue; // 保留 Camera
Destroy(child.gameObject);
}
// 从 TurretResourcesData 拿预制体
GameObject prefab = TurretResourcesData.GetTurretPrefab(turretNumber);
if (prefab == null)
{
Debug.LogWarning($"[SitPosData] 翅膀预制体未加载: #{turretNumber}");
return;
}
// 克隆到 wingParent,保持预制体原始位置/旋转/缩放
GameObject instance = Instantiate(prefab, wingParent, false);
instance.SetActive(true);
Debug.Log($"[SitPosData] 翅膀已加载: #{turretNumber}");
}
/// <summary>根据炮台编号字符串加载。</summary>
public void LoadWing(string turretNumberStr)
{
if (int.TryParse(turretNumberStr, out int num))
LoadWing(num);
}
// -------------------------
// 切换状态(GameMainPanel 调用)
// -------------------------
public void ChangeState(TurretState state)
{
OnStateChange?.Invoke(state);
}
// -------------------------
// 炮台旋转(GameMainPanel 调用)
// -------------------------
public void SetRotation(float angle)
{
currentAngle = angle;
if (tra_CurrTurret != null)
tra_CurrTurret.rotation = Quaternion.Euler(0, 0, angle);
}
// -------------------------
// 获取发射点(给 GameMainPanel)
// -------------------------
public Vector2 GetBulletStartPos()
{
return currentTurret != null
? currentTurret.GetBulletStartWorldPos()
: Vector2.zero;
}
// ==================== 金币消耗 ====================
public bool ConsumeGold(int mul = 1)
{
long temp = goldNum - consumeGoldNum * mul;
if (temp < 0) return false;
goldNum = temp;
tMP_GoldNum.text = goldNum.ToString();
return true;
}
int indexConsume = 0;
public void ChangeTurretAddConsumeNum()
{
if (CreateFishManager.Instance.currGameType.paoBeiList.Count - 1 > indexConsume)
indexConsume++;
else
indexConsume = 0;
AudioManager.Instance.PlayerFX(62011);
consumeGoldNum = CreateFishManager.Instance.currGameType.paoBeiList[indexConsume];
tMP_TurretConsumeNum.text = consumeGoldNum.ToString();
CreateFishManager.Instance.currGameType?.SyncCannonMultiple((int)consumeGoldNum);
}
public void ChangeTurretMinusConsumeNum()
{
if (indexConsume > 0)
indexConsume--;
else
indexConsume = CreateFishManager.Instance.currGameType.paoBeiList.Count - 1;
AudioManager.Instance.PlayerFX(62010);
consumeGoldNum = CreateFishManager.Instance.currGameType.paoBeiList[indexConsume];
tMP_TurretConsumeNum.text = consumeGoldNum.ToString();
CreateFishManager.Instance.currGameType?.SyncCannonMultiple((int)consumeGoldNum);
}
// ==================== 锁定系统 ====================
public void SetLockTarget(MonsterContral monster, Transform hitPos)
{
monCotl_Lock = monster;
monCotl_Lock_Position = hitPos;
isLock = monster != null;
lockFX.SetActive(isLock);
// 非三发锁定 → 隐藏额外图标
if (!isLock) HideTripleLockIcons();
}
/// <summary>
/// 设置三发锁定目标(3个目标分别追踪)
/// </summary>
public void SetTripleLockTargets(List<(MonsterContral monster, Transform hitPos)> targets)
{
if (targets == null || targets.Count == 0)
{
HideTripleLockIcons();
return;
}
// 确保 lockFX_Triple 有2个克隆的图标(+主图标=3个)
EnsureTripleLockIcons();
tripleLockTargets.Clear();
tripleLockPositions.Clear();
// 第一个目标用主 lockFX
var primary = targets[0];
monCotl_Lock = primary.monster;
monCotl_Lock_Position = primary.hitPos;
isLock = true;
lockFX.SetActive(true);
tripleLockTargets.Add(primary.monster);
tripleLockPositions.Add(primary.hitPos);
// 第二个和第三个目标用 lockFX_Triple
for (int i = 1; i < targets.Count && i < 3; i++)
{
int idx = i - 1;
if (idx < lockFX_Triple.Count && lockFX_Triple[idx] != null)
{
lockFX_Triple[idx].SetActive(true);
tripleLockTargets.Add(targets[i].monster);
tripleLockPositions.Add(targets[i].hitPos);
}
}
// 隐藏多余的
for (int i = targets.Count - 1; i < lockFX_Triple.Count; i++)
{
if (lockFX_Triple[i] != null)
lockFX_Triple[i].SetActive(false);
}
}
private void EnsureTripleLockIcons()
{
while (lockFX_Triple.Count < 2 && lockFX != null)
{
var clone = GameObject.Instantiate(lockFX, lockFX.transform.parent);
clone.name = lockFX.name + "_Triple" + (lockFX_Triple.Count + 1);
clone.transform.localScale = lockFX.transform.localScale * 0.5f;
lockFX_Triple.Add(clone);
}
}
public void HideTripleLockIcons()
{
foreach (var icon in lockFX_Triple)
{
if (icon != null)
icon.SetActive(false);
}
tripleLockTargets.Clear();
tripleLockPositions.Clear();
}
public void ClearLock()
{
monCotl_Lock = null;
monCotl_Lock_Position = null;
isLock = false;
lockFX.SetActive(false);
HideTripleLockIcons();
ChangeState(TurretState.Idle);
}
}