Files
5.24BuYu/Assets/MyGame/aa/UI/Game/GameMainPanel_FishBase.cs
T

1158 lines
35 KiB
C#
Raw Normal View History

2026-05-04 17:21:52 +08:00
/*
======================== GameMainPanel_FishBase ========================
【主控制层职责】
负责:输入 → 旋转 → 攻击 → 子弹 → 锁定 → 网络同步 → 广播处理
不负责:动画(TurretData)、发射点(TurretData)、金币(SitPosData)
=======================================================================
① InputModule(输入系统)
├── HandleInput() 主入口
├── 按下(PressDown)
├── 长按(PressHold)
├── 松开(PressUp)
├── CheckTouchIsChangeTurret() 判断是否点到“换炮台按钮”
└── HandleChangeTurretHold() 长按换炮台按钮 → 打开换炮台面板
-----------------------------------------------------------------------
② RotateModule(旋转系统)
├── HandleRotate() 手动旋转入口
├── RotateToTouch() 朝触摸点旋转
├── RotateToLockTarget() 朝锁定目标旋转
├── HandleAutoRotate() 自动开火时的自动旋转
└── AutoSweepRotate() 自动扫射(左右摆动)
-----------------------------------------------------------------------
③ FireModule(攻击系统)
├── TryFire() 长按触发攻击(冷却计时)
├── FireBullet() 发射子弹(普通/三射/双倍/锁定)
├── StartLaser() 激光开始(LaserStart)
├── LoopLaser() 激光持续(LaserLoop)
└── StopLaser() 激光结束(LaserEnd)
-----------------------------------------------------------------------
④ BulletModule(子弹系统)
├── CreateBullet() 创建子弹实例
-----------------------------------------------------------------------
⑤ LockModule(锁定系统)
├── HandleLock() 点击锁定
├── 自动锁定(AutoLock) 可选扩展
├── CancelLock() 取消锁定
├── UpdateLockFX() 锁定特效跟随目标
└── SyncLock() 网络同步锁定状态
-----------------------------------------------------------------------
⑥ NetSyncModule(网络同步)
├── SyncRotate() 同步旋转
├── SyncFire() 同步发射
├── SyncLock() 同步锁定
├── SyncLaserState() 同步激光状态
└── SyncChangeTurret() 同步炮台切换(可选)
-----------------------------------------------------------------------
⑦ NetReceiveModule(服务器广播处理)
├── SyncGameData_Rotate() 广播旋转
├── SyncGameData_FireBullet() 广播发射
├── SyncGameData_ChangeTarget() 广播锁定
├── SyncGameData_ChangeTurret() 广播炮台切换(可选)
└── SyncGameData_LaserState() 广播激光状态(可选)
-----------------------------------------------------------------------
⑧ ResourceModule(资源加载)
├── LoadResources() 加载音频/鱼资源/路线
└── UnLoadResources() 卸载资源
-----------------------------------------------------------------------
⑨ PlayerJoinModule(玩家加入/退出)
├── JoinGame() 玩家加入房间
└── ExitGame() 玩家退出房间
=======================================================================
*/
using Assets.MyGame.Scripts.Net;
2026-03-18 17:26:14 +08:00
using Assets.MyGame.Scripts.Net.API;
using Assets.MyGame.Scripts.Net.APIResponses.Enum;
using Assets.MyGame.Scripts.Net.WebResponses;
2026-04-29 01:18:39 +08:00
using DG.Tweening;
2026-03-18 17:26:14 +08:00
using Newtonsoft.Json;
2026-03-27 15:18:35 +08:00
using Spine.Unity;
2026-03-18 17:26:14 +08:00
using System;
using System.Collections;
2026-02-13 19:23:01 +08:00
using System.Collections.Generic;
2026-03-27 15:18:35 +08:00
using System.Threading.Tasks;
using TMPro;
2026-02-13 19:23:01 +08:00
using UnityEngine;
2026-03-27 15:18:35 +08:00
using UnityEngine.AddressableAssets;
2026-03-18 17:26:14 +08:00
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
2026-03-27 15:18:35 +08:00
using static UnityEngine.AddressableAssets.Addressables;
2026-02-13 19:23:01 +08:00
public class GameMainPanel_FishBase : GameMainPanel_Base
{
2026-03-18 17:26:14 +08:00
[Header("游戏场景")]
public string sceneName;
public GameTypeEnum gameType;
[Header("设置")]
protected bool isOpenSetting;
public Button btn_SettingList;
2026-04-29 01:18:39 +08:00
public Transform SittingListParent;
2026-05-01 06:26:50 +08:00
public Button btn_TuiChu;
2026-03-18 17:26:14 +08:00
[Header("我在这")]
2026-03-27 15:18:35 +08:00
public GameObject mySitAnim;
2026-03-18 17:26:14 +08:00
[Header("Skill")]
public Dictionary<string, Toggle> dic_Skill_Toggle = new Dictionary<string, Toggle>();//开关型技能
public Toggle toggle_SkillAuto;
public Toggle toggle_Lock;
public Toggle toggle_Three;
public Toggle toggle_Shuang;
2026-05-18 18:39:11 +08:00
public Toggle toggle_BaoZou;
2026-03-18 17:26:14 +08:00
public List<SitPosData> list_Sits = new List<SitPosData>();
public SitPosData currSitData;
[Header("攻击参数")]
public float attackTime = 0.5f;
float clickTime = 0;
public GameObject bullet_Test;
public GameObject bulletParent;
float changeTurretTime = 0f;
bool isChangingTurret = false;
int bulletCount = 0;
public GameObject boardCast_Boss;
2026-03-27 15:18:35 +08:00
AudioClip bulletClip;
2026-04-24 22:51:34 +08:00
2026-05-01 06:26:50 +08:00
public ProgressBar JinDuTiao;
2026-05-04 17:21:52 +08:00
#region UI
2026-03-18 17:26:14 +08:00
bool isfirstShow = true;
2026-05-04 17:21:52 +08:00
2026-03-18 17:26:14 +08:00
protected override void OnShow(object args = null)
{
base.OnShow(args);
2026-05-01 06:26:50 +08:00
LoadResources();
2026-03-18 17:26:14 +08:00
//UIManager.Instance.ShowBoard("太好看了");
var v = JsonConvert.DeserializeObject<WebSocketResponseBase<RoomJoinNotice>>(args.ToString());
if (v.arguments.Count > 0)
{
RoomJoinNotice roomJoinNotice = v.arguments[0];
if (isfirstShow)
{
foreach (var item in GameManager.Instance.gameData.currRoomData.players)
{
JoinGame(item.position, item.gold, item.cannonMultiple, 0, item.userId);
}
GameManager.Instance.gameData.roomJoinNotice = v.arguments[0];
isfirstShow = false;
2026-04-29 01:18:39 +08:00
2026-03-18 17:26:14 +08:00
currSitData = list_Sits[roomJoinNotice.position];
//currSitData.ChangeTurret(1);
currSitData.lockFX.transform.GetChild(0).gameObject.SetActive(false);
currSitData.lockFX.transform.GetChild(1).gameObject.SetActive(true);
2026-03-31 17:00:05 +08:00
mySitAnim.GetComponent<RectTransform>().anchoredPosition = currSitData.currentTurret.GetComponent<RectTransform>().anchoredPosition;
2026-03-18 17:26:14 +08:00
}
else
{
JoinGame(roomJoinNotice.position, roomJoinNotice.gold, roomJoinNotice.cannonMultiple, 0, roomJoinNotice.userId);
2026-03-27 15:18:35 +08:00
2026-03-18 17:26:14 +08:00
}
}
else
{
UIManager.Instance.ShowBoardError("获取信息失败");
}
}
protected override void OnHide()
{
base.OnHide();
2026-05-04 17:21:52 +08:00
2026-05-01 06:26:50 +08:00
UnLoadResources();
2026-03-18 17:26:14 +08:00
SceneManager.UnloadSceneAsync(sceneName);
WebSocketManager.Instance.Close(SocketType.game);
2026-02-13 19:23:01 +08:00
}
2026-05-04 17:21:52 +08:00
#endregion
#region 生命周期
2026-05-18 18:39:11 +08:00
private float baseAttackTime;
2026-05-04 17:21:52 +08:00
public override void Awake()
{
base.Awake();
2026-05-18 18:39:11 +08:00
baseAttackTime = attackTime;
2026-05-04 17:21:52 +08:00
btn_TuiChu.onClick.AddListener(() =>
{
WebSocketSendList.LeaveRoom(GameManager.Instance.gameData.currRoomData.roomId, currSitData.user);
CreateFishManager.Instance.fangzhu = false;
});
// 服务器广播监听
EventCenter.Instance.AddEventListener<SyncGameData>(ConstEventString.SyncGameData_Rotate, SyncGameData_Rotate);
EventCenter.Instance.AddEventListener<SyncGameData>(ConstEventString.SyncGameData_FireBullet, SyncGameData_FireBullit);
EventCenter.Instance.AddEventListener<SyncGameData>(ConstEventString.SyncGameData_ChangeTargetMonster, SyncGameData_ChangeTargetMonster);
EventCenter.Instance.AddEventListener<SyncGameData>(ConstEventString.SyncGameData_RendromDieFish, SyncGameData_RendromDieFish);
EventCenter.Instance.AddEventListener<SyncGameData>(ConstEventString.SyncGameData_TurretState, SyncGameData_TurretState);
EventCenter.Instance.AddEventListener<SyncGameData>(ConstEventString.SyncGameData_ChangeTurret, SyncGameData_ChangeTurret);
}
public virtual void OnDestroy()
{
EventCenter.Instance.RemoveEventListener<SyncGameData>(ConstEventString.SyncGameData_Rotate, SyncGameData_Rotate);
EventCenter.Instance.RemoveEventListener<SyncGameData>(ConstEventString.SyncGameData_FireBullet, SyncGameData_FireBullit);
EventCenter.Instance.RemoveEventListener<SyncGameData>(ConstEventString.SyncGameData_ChangeTargetMonster, SyncGameData_ChangeTargetMonster);
EventCenter.Instance.RemoveEventListener<SyncGameData>(ConstEventString.SyncGameData_RendromDieFish, SyncGameData_RendromDieFish);
EventCenter.Instance.RemoveEventListener<SyncGameData>(ConstEventString.SyncGameData_TurretState, SyncGameData_TurretState);
EventCenter.Instance.RemoveEventListener<SyncGameData>(ConstEventString.SyncGameData_ChangeTurret, SyncGameData_ChangeTurret);
}
public virtual void Start()
{
btn_SettingList.onClick.AddListener(() =>
{
if (isOpenSetting) StartCoroutine(CloseSettingList());
else StartCoroutine(OpenSettingList());
});
if (toggle_Lock != null)
{
toggle_Lock.onValueChanged.AddListener(on =>
{
if (!on)
{
2026-05-18 18:39:11 +08:00
// 本地清理锁定
if (currSitData != null)
currSitData.ClearLock();
// 同步给服务器:取消锁定
2026-05-04 17:21:52 +08:00
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
frameType = FrameDataType.changeAutoTarget,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_ChangeAutoTarget { targetGuid = null }
};
WebSocketSendList.SyncGameData(sgd);
}
});
}
2026-05-18 18:39:11 +08:00
toggle_BaoZou.onValueChanged.AddListener(on =>
{
// UI 开关只是触发,但最终状态以 currGameType 为准
CreateFishManager.Instance.currGameType.isFrenzy = on;
SetFrenzy(on);
});
toggle_SkillAuto.onValueChanged.AddListener(on =>
{
if (!on)
{
// 自动开火关闭 → 立即切回 Idle
currSitData.ChangeState(TurretState.Idle);
}
});
2026-05-04 17:21:52 +08:00
}
2026-05-18 18:39:11 +08:00
public bool isFrenzy;
public void SetFrenzy(bool on)
{
bool frenzy = CreateFishManager.Instance.currGameType.isFrenzy;
// 攻击速度翻倍
attackTime = frenzy ? baseAttackTime * 0.5f : baseAttackTime;
// 动画倍速
if (currSitData != null && currSitData.currentTurret != null)
currSitData.currentTurret.SetFrenzy(frenzy);
}
2026-05-04 17:21:52 +08:00
public void OnEnable()
{
}
private void Update()
{
HandleInput();
HandleAutoRotate();
2026-05-18 18:39:11 +08:00
CheckLockTargetValid();//锁定目标失效自动清除
HandleLock();
2026-05-04 17:21:52 +08:00
HandleAutoFire(); // 自动开火真正发射
UpdateLockFX();
HandleAutoLock();
LoopLaser();
2026-05-18 18:39:11 +08:00
SettingClose();
2026-05-04 17:21:52 +08:00
}
2026-05-18 18:39:11 +08:00
float sittingTime = 10;
float currSittingTime ;
2026-05-04 17:21:52 +08:00
2026-05-18 18:39:11 +08:00
/// <summary>
/// 等待10秒未操作关闭设置面板
/// </summary>
public void SettingClose()
{
//if (isOpenSetting) StartCoroutine(CloseSettingList());
//else StartCoroutine(OpenSettingList());
if (isOpenSetting)
{
currSittingTime -= Time.deltaTime;
if (currSittingTime<=0)
{
StartCoroutine(CloseSettingList());
}
}
else
{
currSittingTime = 10;
}
}
2026-05-04 17:21:52 +08:00
private void UpdateLockFX()
{
foreach (var sit in list_Sits)
{
if (!sit.isLock || sit.monCotl_Lock == null) continue;
Vector3 pos = sit.monCotl_Lock_Position.position;
pos.z = Camera.main.transform.position.z + 1;
sit.lockFX.transform.position = pos;
if (!sit.lockFX.activeSelf)
sit.lockFX.SetActive(true);
}
}
#endregion
#region 资源加载模块
public async void LoadResources()
{
CreateFishManager.Instance.isResources = false;
await AudioResourcesData.LoadAudioGame();
JinDuTiao.SetProgress(0.2f);
await MonsterMoveLine.LoadBezier();
JinDuTiao.SetProgress(0.4f);
await FishResourcesData.LoadFishAssetsData();
JinDuTiao.SetProgress(0.6f);
await Addressables.LoadAssetsAsync<AudioClip>("Audio", clip =>
{
if (clip.name == "PW_Shoot")
bulletClip = clip;
}).Task;
JinDuTiao.SetProgress(0.8f);
2026-05-07 03:46:03 +08:00
await Task.Delay(100);
2026-05-04 17:21:52 +08:00
CreateFishManager.Instance.EnterRoom(gameType);
CreateFishManager.Instance.isResources = true;
2026-05-07 03:46:03 +08:00
JinDuTiao.SetProgress(0.9f);
await Task.Delay(2000);
2026-05-04 17:21:52 +08:00
JinDuTiao.SetProgress(1);
2026-05-07 03:46:03 +08:00
await Task.Delay(300);
2026-05-04 17:21:52 +08:00
JinDuTiao.Hide();
}
public async void UnLoadResources()
{
2026-05-08 20:44:54 +08:00
CreateFishManager.Instance.ExitRoom();
2026-05-04 17:21:52 +08:00
CreateFishManager.Instance.isResources = false;
await FishResourcesData.UnLoadFishAssetsData();
await AudioResourcesData.UnLoadAudioGame();
await MonsterMoveLine.UnLoadBezier();
}
#endregion
#region 输入系统(InputModule)
2026-05-18 18:39:11 +08:00
private bool IsPointerOverUI(Vector2 pos)
{
PointerEventData eventData = new PointerEventData(EventSystem.current);
eventData.position = pos;
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(eventData, results);
return results.Count > 0;
}
public struct CrossPlatformInput
{
public bool isPressed;
public bool wasPressedThisFrame;
public bool wasReleasedThisFrame;
public Vector2 position;
}
private CrossPlatformInput GetInput()
{
CrossPlatformInput input = new CrossPlatformInput();
// ============================
// PC / Editor → 鼠标输入
// ============================
#if UNITY_STANDALONE || UNITY_EDITOR
if (Mouse.current != null)
{
input.isPressed = Mouse.current.leftButton.isPressed;
input.wasPressedThisFrame = Mouse.current.leftButton.wasPressedThisFrame;
input.wasReleasedThisFrame = Mouse.current.leftButton.wasReleasedThisFrame;
input.position = Mouse.current.position.ReadValue();
}
#else
// ============================
// 移动平台 → 触摸输入
// ============================
var touch = Touchscreen.current?.primaryTouch;
if (touch != null)
{
input.isPressed = touch.press.isPressed;
input.wasPressedThisFrame = touch.press.wasPressedThisFrame;
input.wasReleasedThisFrame = touch.press.wasReleasedThisFrame;
input.position = touch.position.ReadValue();
}
#endif
return input;
}
2026-05-04 17:21:52 +08:00
private void HandleInput()
{
2026-05-18 18:39:11 +08:00
CrossPlatformInput input = GetInput();
if (!input.isPressed && !input.wasPressedThisFrame && !input.wasReleasedThisFrame)
return;
// ⭐ UI 点击过滤(保持你之前的逻辑)
if (IsPointerOverUI(input.position))
return;
2026-05-04 17:21:52 +08:00
// 按下
2026-05-18 18:39:11 +08:00
if (input.wasPressedThisFrame)
2026-05-04 17:21:52 +08:00
{
2026-05-18 18:39:11 +08:00
isChangingTurret = CheckTouchIsChangeTurret(input.position);
2026-05-04 17:21:52 +08:00
clickTime = attackTime;
if (mySitAnim != null && mySitAnim.gameObject.activeSelf)
mySitAnim.gameObject.SetActive(false);
if (!isChangingTurret)
currSitData.ChangeState(TurretState.Attack);
}
// 长按
2026-05-18 18:39:11 +08:00
if (input.isPressed)
2026-05-04 17:21:52 +08:00
{
if (isChangingTurret)
{
HandleChangeTurretHold();
return;
}
2026-05-18 18:39:11 +08:00
HandleRotate(input.position);
2026-05-04 17:21:52 +08:00
TryFire();
}
// 松开
2026-05-18 18:39:11 +08:00
if (input.wasReleasedThisFrame)
2026-05-04 17:21:52 +08:00
{
if (isChangingTurret)
{
isChangingTurret = false;
changeTurretTime = 0;
CloseChangeTurretUI();
return;
}
currSitData.ChangeState(TurretState.Idle);
}
}
2026-05-18 18:39:11 +08:00
2026-05-04 17:21:52 +08:00
private void HandleChangeTurretHold()
{
changeTurretTime += Time.deltaTime;
if (changeTurretTime > 1f)
{
if (!currSitData.changeTurretList.activeSelf)
currSitData.changeTurretList.SetActive(true);
}
}
2026-05-18 18:39:11 +08:00
private bool CheckTouchIsChangeTurret(Vector2 pos)
2026-05-04 17:21:52 +08:00
{
PointerEventData pointerData = new PointerEventData(EventSystem.current);
2026-05-18 18:39:11 +08:00
pointerData.position = pos;
2026-05-04 17:21:52 +08:00
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(pointerData, results);
foreach (var result in results)
{
if (result.gameObject.CompareTag("ChangeTurret"))
{
SitPosData sit = result.gameObject.GetComponentInParent<SitPosData>();
if (sit == currSitData)
return true;
}
}
return false;
}
2026-05-18 18:39:11 +08:00
2026-05-04 17:21:52 +08:00
private void CloseChangeTurretUI()
{
currSitData.changeTurretList.SetActive(false);
changeTurretTime = 0;
isChangingTurret = false;
}
#endregion
#region 打开关闭右上角设置选项
public virtual IEnumerator OpenSettingList()
{
RectTransform tran = SittingListParent.GetComponent<RectTransform>();
tran.gameObject.SetActive(true);
tran.DOSizeDelta(new Vector2(466, 143), 0.2f);
isOpenSetting = true;
Vector3 v3 = btn_SettingList.transform.localScale;
v3.x = Mathf.Abs(v3.x) * -1;
btn_SettingList.transform.localScale = v3;
yield break;
}
public virtual IEnumerator CloseSettingList()
{
RectTransform tran = SittingListParent.GetComponent<RectTransform>();
Vector3 v3 = btn_SettingList.transform.localScale;
v3.x = Mathf.Abs(v3.x);
btn_SettingList.transform.localScale = v3;
tran.DOSizeDelta(new Vector2(0, 143), 0.2f).OnComplete(() =>
{
tran.gameObject.SetActive(false);
});
isOpenSetting = false;
yield break;
}
#endregion
#region ② 炮台旋转系统(RotateModule)
2026-05-18 18:39:11 +08:00
private void HandleRotate(Vector2 pos)
2026-05-04 17:21:52 +08:00
{
if (toggle_Lock.isOn && currSitData.monCotl_Lock != null)
{
RotateToLockTarget(currSitData);
}
else
{
2026-05-18 18:39:11 +08:00
RotateToTouch(currSitData, pos);
2026-05-04 17:21:52 +08:00
}
}
2026-05-18 18:39:11 +08:00
private void RotateToTouch(SitPosData sit, Vector2 pos)
2026-05-04 17:21:52 +08:00
{
RectTransformUtility.ScreenPointToLocalPointInRectangle(
sit.tra_CurrTurret.transform.parent as RectTransform,
2026-05-18 18:39:11 +08:00
pos, null, out Vector2 localPos);
2026-05-04 17:21:52 +08:00
Vector2 dir = localPos - sit.tra_CurrTurret.GetComponent<RectTransform>().anchoredPosition;
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg - 90f;
sit.SetRotation(angle);
SyncRotate(angle);
}
2026-05-18 18:39:11 +08:00
2026-05-04 17:21:52 +08:00
private void RotateToLockTarget(SitPosData sit)
{
Vector2 screenPos = Camera.main.WorldToScreenPoint(sit.monCotl_Lock_Position.position);
2026-05-18 18:39:11 +08:00
2026-05-04 17:21:52 +08:00
RectTransformUtility.ScreenPointToLocalPointInRectangle(
sit.tra_CurrTurret.transform.parent as RectTransform,
screenPos, null, out Vector2 localPos);
Vector2 dir = localPos - sit.tra_CurrTurret.GetComponent<RectTransform>().anchoredPosition;
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg - 90f;
sit.SetRotation(angle);
}
2026-05-18 18:39:11 +08:00
2026-05-04 17:21:52 +08:00
private void HandleAutoRotate()
{
// 自动开火关闭 → 不自动旋转
if (!toggle_SkillAuto.isOn) return;
// 锁定模式优先
if (toggle_Lock.isOn && currSitData.isLock)
{
RotateToLockTarget(currSitData);
return;
}
// 自动扫射(左右摆动)
AutoSweepRotate();
}
private float autoRotateAngle = 0;
private float autoRotateSpeed = 40f;
private float autoRotateMin = -60f;
private float autoRotateMax = 60f;
private int autoRotateDir = 1;
private void AutoSweepRotate()
{
autoRotateAngle += autoRotateSpeed * autoRotateDir * Time.deltaTime;
if (autoRotateAngle > autoRotateMax)
{
autoRotateAngle = autoRotateMax;
autoRotateDir = -1;
}
else if (autoRotateAngle < autoRotateMin)
{
autoRotateAngle = autoRotateMin;
autoRotateDir = 1;
}
currSitData.SetRotation(autoRotateAngle);
}
private Coroutine rotateCoroutine;
private void SmoothRotate(SitPosData sit, float angle)
{
sit.SetRotation(angle); // 先立即更新角度(逻辑角度)
if (rotateCoroutine != null)
StopCoroutine(rotateCoroutine);
rotateCoroutine = StartCoroutine(RotateIE_Angle(sit, Quaternion.Euler(0, 0, angle)));
}
private IEnumerator RotateIE_Angle(SitPosData sit, Quaternion target)
{
float angleThreshold = 0.1f;
while (true)
{
float angleDiff = Quaternion.Angle(sit.tra_CurrTurret.rotation, target);
if (angleDiff <= angleThreshold)
{
sit.tra_CurrTurret.rotation = target;
yield break;
}
sit.tra_CurrTurret.rotation = Quaternion.Lerp(
sit.tra_CurrTurret.rotation,
target,
0.6f
);
yield return null;
}
}
#endregion
#region ③ 攻击系统(FireModule)
private void TryFire()
{
clickTime += Time.deltaTime;
if (clickTime < attackTime) return;
clickTime = 0;
float angle = currSitData.currentAngle;
bool isSanShe = toggle_Three.isOn;
bool isDouble = toggle_Shuang.isOn;
bool isLock = toggle_Lock.isOn;
FireBullet(currSitData, angle, isSanShe, isDouble, isLock);
SyncFire(angle, isSanShe, isDouble, isLock);
}
private void FireBullet(SitPosData sit, float angle, bool isSanShe, bool isDouble, bool isLock)
{
Vector2 startPos = sit.GetBulletStartPos();
if (isSanShe)
{
CreateBullet(sit, startPos, angle - 20, isDouble, isLock);
CreateBullet(sit, startPos, angle, isDouble, isLock);
CreateBullet(sit, startPos, angle + 20, isDouble, isLock);
}
else
{
CreateBullet(sit, startPos, angle, isDouble, isLock);
}
AudioManager.Instance.PlayFX(bulletClip);
}
private bool isLaser = false;
private void StartLaser()
{
isLaser = true;
currSitData.ChangeState(TurretState.LaserStart);
SyncLaserState(TurretState.LaserStart);
}
private void LoopLaser()
{
if (!isLaser) return;
// TODO: 激光持续伤害 / 特效
}
private void StopLaser()
{
isLaser = false;
currSitData.ChangeState(TurretState.LaserEnd);
SyncLaserState(TurretState.LaserEnd);
}
private float autoFireTimer = 0f;
2026-05-18 18:39:11 +08:00
//private void HandleAutoFire()
//{
// if (!toggle_SkillAuto.isOn) return;
// if (currSitData == null) return;
// autoFireTimer += Time.deltaTime;
// if (autoFireTimer < attackTime) return;
// autoFireTimer = 0f;
// float angle = currSitData.currentAngle;
// bool isSanShe = toggle_Three.isOn;
// bool isDouble = toggle_Shuang.isOn;
// bool isLock = toggle_Lock.isOn;
// FireBullet(currSitData, angle, isSanShe, isDouble, isLock);
// SyncFire(angle, isSanShe, isDouble, isLock);
//}
2026-05-04 17:21:52 +08:00
private void HandleAutoFire()
{
if (currSitData == null) return;
2026-05-18 18:39:11 +08:00
bool needAutoFire = toggle_SkillAuto.isOn || (toggle_Lock.isOn && currSitData.isLock);
if (!needAutoFire)
{
autoFireTimer = 0f;
return;
}
2026-05-04 17:21:52 +08:00
autoFireTimer += Time.deltaTime;
if (autoFireTimer < attackTime) return;
autoFireTimer = 0f;
float angle = currSitData.currentAngle;
bool isSanShe = toggle_Three.isOn;
bool isDouble = toggle_Shuang.isOn;
bool isLock = toggle_Lock.isOn;
2026-05-18 18:39:11 +08:00
// ⭐ 自动攻击也要播放攻击动画
currSitData.ChangeState(TurretState.Attack);
2026-05-04 17:21:52 +08:00
FireBullet(currSitData, angle, isSanShe, isDouble, isLock);
SyncFire(angle, isSanShe, isDouble, isLock);
}
2026-05-18 18:39:11 +08:00
2026-05-04 17:21:52 +08:00
#endregion
#region 锁定系统(LockModule)
2026-05-18 18:39:11 +08:00
//private void HandleLock()
//{
// if (!toggle_Lock.isOn) return;
// if (Touchscreen.current.primaryTouch.press.wasPressedThisFrame)
// {
// Vector2 worldPos = Camera.main.ScreenToWorldPoint(
// Touchscreen.current.primaryTouch.position.ReadValue());
// RaycastHit2D hit = Physics2D.Raycast(worldPos, Vector2.zero, 50, LayerMask.GetMask("Fish"));
// if (hit.collider != null)
// {
// MonsterContral monster = hit.collider.GetComponentInParent<MonsterContral>();
// currSitData.SetLockTarget(monster, hit.collider.transform);
// // 同步锁定
// SyncLock(monster);
// }
// else
// {
// // 点击空白:取消锁定 + 隐藏图标
// currSitData.ClearLock();
// SyncLock(null);
// }
// }
//}
2026-05-04 17:21:52 +08:00
private void HandleLock()
{
2026-05-18 18:39:11 +08:00
CrossPlatformInput input = GetInput();
2026-05-04 17:21:52 +08:00
if (!toggle_Lock.isOn) return;
2026-05-18 18:39:11 +08:00
// 只有按下那一帧才触发锁定
if (!input.wasPressedThisFrame) return;
Vector2 worldPos = Camera.main.ScreenToWorldPoint(input.position);
RaycastHit2D hit = Physics2D.Raycast(worldPos, Vector2.zero, 50, LayerMask.GetMask("Fish"));
if (hit.collider != null)
2026-05-04 17:21:52 +08:00
{
2026-05-18 18:39:11 +08:00
MonsterContral monster = hit.collider.GetComponentInParent<MonsterContral>();
currSitData.SetLockTarget(monster, hit.collider.transform);
2026-05-04 17:21:52 +08:00
2026-05-18 18:39:11 +08:00
SyncLock(monster);
}
else
{
currSitData.ClearLock();
SyncLock(null);
2026-05-04 17:21:52 +08:00
}
}
2026-05-18 18:39:11 +08:00
private void CheckLockTargetValid()
{
if (!currSitData.isLock) return;
var monster = currSitData.monCotl_Lock;
// 目标无效:被对象池回收 / 不激活 / 被销毁
if (monster == null || !monster.gameObject.activeInHierarchy)
{
currSitData.ClearLock(); // 清除锁定
SyncLock(null); // 同步服务器
// ⭐⭐⭐ 关键:怪死了 → 动画回到 Idle ⭐⭐⭐
currSitData.ChangeState(TurretState.Idle);
}
}
2026-05-04 17:21:52 +08:00
private void HandleAutoLock()
{
if (!toggle_SkillAuto.isOn) return;
if (toggle_Lock.isOn) return; // 手动锁定优先
// TODO: 自动锁定最近鱼 / 最大鱼
2026-05-18 18:39:11 +08:00
// 已经有锁定目标并且还活着,就不重复找
if (currSitData.isLock &&
currSitData.monCotl_Lock != null &&
currSitData.monCotl_Lock.gameObject.activeInHierarchy)
{
return;
}
MonsterContral target = FindAutoLockTarget();
if (target != null)
{
// 这里用怪物本体作为锁定点,你也可以换成某个挂点
currSitData.SetLockTarget(target, target.transform);
SyncLock(target);
}
else
{
// 没有可锁定目标时,清理锁定状态
currSitData.ClearLock();
SyncLock(null);
}
}
private MonsterContral FindAutoLockTarget()
{
if (CreateFishManager.Instance == null ||
CreateFishManager.Instance.fishDatas_hashCodekey == null ||
CreateFishManager.Instance.fishDatas_hashCodekey.Count == 0)
return null;
MonsterContral best = null;
float bestSqr = float.MaxValue;
Vector3 origin = currSitData.tra_CurrTurret.position;
foreach (var kv in CreateFishManager.Instance.fishDatas_hashCodekey)
{
MonsterContral m = kv.Value;
if (m == null || !m.gameObject.activeInHierarchy) continue;
float sqr = (m.transform.position - origin).sqrMagnitude;
if (sqr < bestSqr)
{
bestSqr = sqr;
best = m;
}
}
return best;
2026-05-04 17:21:52 +08:00
}
//private void HandleAutoLock()
//{
// if (!toggle_SkillAuto.isOn) return;
// if (toggle_Lock.isOn) return;
// if (currSitData.isLock) return;
// MonsterContral nearest = CreateFishManager.Instance;
// if (nearest != null)
// {
// currSitData.SetLockTarget(nearest, nearest.transform);
// SyncLock(nearest);
// }
//}
#endregion
#region BulletModule(子弹系统)
private BullitMove CreateBullet(SitPosData sit, Vector2 pos, float angle, bool isDouble, bool isLock)
{
GameObject prefab = isDouble ? sit.currentTurret.bulletPrefab_Two : sit.currentTurret.bulletPrefab;
if (!sit.ConsumeGold(isDouble ? 2 : 1))
return null;
var bullet = Instantiate(prefab, pos, Quaternion.Euler(0, 0, angle), bulletParent.transform)
.GetComponent<BullitMove>();
bullet.userid = sit.user;
bullet.playerID = sit.user;
bullet.bulletID = Guid.NewGuid().ToString();
if (isLock)
bullet.Lock(sit.monCotl_Lock);
return bullet;
}
#endregion
#region ⑥ 网络同步(NetSyncModule)
private void SyncRotate(float angle)
{
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
frameType = FrameDataType.rotateTurrect,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_Rotate { angle = angle }
};
WebSocketSendList.SyncGameData(sgd);
}
private void SyncFire(float angle, bool isSanShe, bool isDouble, bool isLock)
{
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
frameType = FrameDataType.fireBullet,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_FireBullit
{
angle = angle,
gold = currSitData.goldNum,
isSanShe = isSanShe,
twoRTruuect = isDouble,
isSuoDing = isLock,
auto = toggle_SkillAuto.isOn
}
};
WebSocketSendList.SyncGameData(sgd);
}
private void SyncLock(MonsterContral monster)
{
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
frameType = FrameDataType.changeAutoTarget,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_ChangeAutoTarget
{
targetGuid = monster?.guid,
targetPostName = monster == null ? null : monster.name
}
};
WebSocketSendList.SyncGameData(sgd);
}
private void SyncLaserState(TurretState state)
{
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
frameType = FrameDataType.changeTurretState,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_TurretState { state = state }
};
WebSocketSendList.SyncGameData(sgd);
}
private void SyncChangeTurret(int index)
{
SyncGameData sgd = new SyncGameData
{
sitPos = currSitData.posID,
frameType = FrameDataType.changeTurret,
time = BeijingTimeManager.Instance.CurrentTimestampMs,
frameData = new FrameData_ChangeTurret { turretIndex = index }
};
WebSocketSendList.SyncGameData(sgd);
}
#endregion
#region ⑦ 服务器广播处理(NetReceiveModule)
public void SyncGameData_Rotate(SyncGameData sync)
{
if (sync.sitPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_Rotate>(sync.frameData);
SmoothRotate(list_Sits[sync.sitPos], data.angle);
}
public void SyncGameData_FireBullit(SyncGameData sync)
{
if (sync.sitPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_FireBullit>(sync.frameData);
FireBullet(list_Sits[sync.sitPos], data.angle, data.isSanShe, data.twoRTruuect, data.isSuoDing);
}
public void SyncGameData_ChangeTargetMonster(SyncGameData sync)
{
if (sync.sitPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_ChangeAutoTarget>(sync.frameData);
if (data.targetGuid == null)
{
list_Sits[sync.sitPos].ClearLock();
return;
}
MonsterContral monster = CreateFishManager.Instance.fishDatas_hashCodekey[data.targetGuid];
Transform hitPos = monster.transform.Find(data.targetPostName);
list_Sits[sync.sitPos].SetLockTarget(monster, hitPos);
}
public void SyncGameData_RendromDieFish(SyncGameData sync)
{
var data = JsonConvert.DeserializeObject<FrameData_RandomDieFish>(sync.frameData.ToString());
CreateFishManager.Instance.Id_DieListFish_FX(
data.guids,
data.coinNum,
new Vector3(data.x, data.y, data.z),
data.count
);
}
public void SyncGameData_TurretState(SyncGameData sync)
{
if (sync.sitPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_TurretState>(sync.frameData);
var sit = list_Sits[sync.sitPos];
sit.ChangeState(data.state);
}
public void SyncGameData_ChangeTurret(SyncGameData sync)
{
if (sync.sitPos == currSitData.posID) return;
var data = FrameDataParser.Parse<FrameData_ChangeTurret>(sync.frameData);
list_Sits[sync.sitPos].ChangeTurret(data.turretIndex);
}
#endregion
#region 玩家加入/退出(PlayerJoinModule)
public void JoinGame(int sitNum, long goldNum, long consumeGold, int currentTurretIndex, string user = null)
{
foreach (var sit in list_Sits)
{
if (sit.posID != sitNum) continue;
sit.gameObject.SetActive(true);
sit.user = user ?? GameManager.Instance.user.id;
sit.goldNum = goldNum;
sit.consumeGoldNum = consumeGold;
sit.tMP_GoldNum.text = goldNum.ToString();
sit.tMP_TurretConsumeNum.text = consumeGold.ToString();
sit.ChangeTurret(currentTurretIndex);
}
}
public void TuiChuHHWK()
{
WebSocketSendList.LeaveRoom(GameManager.Instance.gameData.currRoomData.roomId, currSitData.user);
CreateFishManager.Instance.fangzhu = false;
}
#endregion
2026-02-13 19:23:01 +08:00
}