2026-06-08 21:23:40 +08:00
|
|
|
|
/*
|
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() 同步锁定
|
2026-06-13 01:25:41 +08:00
|
|
|
|
├── SyncTurretState() 同步激光状态
|
2026-05-04 17:21:52 +08:00
|
|
|
|
└── 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-05-28 18:16:45 +08:00
|
|
|
|
using System.Linq;
|
2026-05-27 19:47:06 +08:00
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
|
|
using System.Text;
|
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;
|
2026-06-21 00:07:26 +08:00
|
|
|
|
using UnityEngine.Playables;
|
2026-03-18 17:26:14 +08:00
|
|
|
|
using UnityEngine.SceneManagement;
|
|
|
|
|
|
using UnityEngine.UI;
|
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-06-08 21:23:40 +08:00
|
|
|
|
public Button btn_TuJian;
|
|
|
|
|
|
public Button btn_ChangeTurret;
|
|
|
|
|
|
|
2026-05-01 06:26:50 +08:00
|
|
|
|
public Button btn_TuiChu;
|
2026-06-21 00:07:26 +08:00
|
|
|
|
public Button btn_Setting;
|
2026-05-01 06:26:50 +08:00
|
|
|
|
|
2026-06-25 01:48:07 +08:00
|
|
|
|
//[Header("我在这")]
|
2026-03-27 15:18:35 +08:00
|
|
|
|
public GameObject mySitAnim;
|
2026-03-18 17:26:14 +08:00
|
|
|
|
public Dictionary<string, Toggle> dic_Skill_Toggle = new Dictionary<string, Toggle>();//开关型技能
|
2026-06-25 01:48:07 +08:00
|
|
|
|
[Header("Skillbottom")]
|
2026-03-18 17:26:14 +08:00
|
|
|
|
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-06-14 20:26:21 +08:00
|
|
|
|
public Image img_BaoZou;
|
2026-05-27 19:47:06 +08:00
|
|
|
|
public float frenzyDuration = 5f; // 疯狂状态持续时间
|
|
|
|
|
|
public float currFrenzyTime = 0f;
|
|
|
|
|
|
public Toggle toggle_BingFeng;
|
|
|
|
|
|
public Image img_BingFeng;
|
2026-06-16 02:28:58 +08:00
|
|
|
|
public GameObject bingFengEffect; // 冰封特效预制体(首次Instantiate,之后激活/隐藏)
|
|
|
|
|
|
private GameObject _bingFengInstance; // 克隆出来的实例
|
2026-05-27 19:47:06 +08:00
|
|
|
|
public float bingFengDuration = 5f; // 冰封状态持续时间
|
|
|
|
|
|
public float currBingFengTime = 0f;
|
2026-06-21 00:07:26 +08:00
|
|
|
|
private Dictionary<Component, float> _frozenAnimSpeeds = new Dictionary<Component, float>();
|
2026-06-25 01:48:07 +08:00
|
|
|
|
|
|
|
|
|
|
[Header("Skillleft")]
|
|
|
|
|
|
public Button btn_GuaGuaKa;
|
|
|
|
|
|
public TextMeshProUGUI tmp_GGKCount;
|
|
|
|
|
|
|
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;
|
2026-06-16 19:12:37 +08:00
|
|
|
|
private Dictionary<int, float> botFireTimer = new Dictionary<int, float>(); // 人机开火计时
|
2026-03-18 17:26:14 +08:00
|
|
|
|
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-06-14 20:26:21 +08:00
|
|
|
|
public List<int> paoBeiList = new List<int>();
|
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;
|
|
|
|
|
|
protected override void OnShow(object args = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
base.OnShow(args);
|
2026-05-01 06:26:50 +08:00
|
|
|
|
LoadResources();
|
2026-06-28 16:48:18 +08:00
|
|
|
|
RefreshGGKCount();
|
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)
|
|
|
|
|
|
{
|
2026-05-27 19:47:06 +08:00
|
|
|
|
int t = GameManager.Instance.gameData.currRoomData.gameType;
|
|
|
|
|
|
int l = GameManager.Instance.gameData.currRoomData.roomLevel;
|
|
|
|
|
|
foreach (var item in GameManager.Instance.gameConfig.roomConfigs)
|
|
|
|
|
|
{
|
2026-06-14 20:26:21 +08:00
|
|
|
|
if (item.gameType == t && l == GameManager.Instance.gameData.currRoomData.roomLevel)
|
2026-05-27 19:47:06 +08:00
|
|
|
|
{
|
2026-06-14 20:26:21 +08:00
|
|
|
|
paoBeiList = item.cannonMultipleList;
|
2026-05-27 19:47:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
string roomId = GameManager.Instance.gameData.currRoomData.roomId;
|
|
|
|
|
|
//string roomId = GameManager.Instance.gameData.currRoomData;
|
|
|
|
|
|
|
2026-03-18 17:26:14 +08:00
|
|
|
|
|
|
|
|
|
|
foreach (var item in GameManager.Instance.gameData.currRoomData.players)
|
|
|
|
|
|
{
|
2026-06-21 00:07:26 +08:00
|
|
|
|
JoinGame(item.position, item.gold, item.diamond, item.cannonMultiple, 0, item.userId, item.cannonId);
|
2026-03-18 17:26:14 +08:00
|
|
|
|
}
|
2026-06-16 19:12:37 +08:00
|
|
|
|
// 生成人机:倒着填空余座位
|
|
|
|
|
|
int botCount = GameManager.Instance.gameData.currRoomData.robotCount;
|
2026-06-16 23:45:31 +08:00
|
|
|
|
if (botCount > 0 && GameManager.Instance.enableBot)
|
2026-06-16 19:12:37 +08:00
|
|
|
|
{
|
|
|
|
|
|
var usedPositions = new HashSet<int>();
|
|
|
|
|
|
foreach (var p in GameManager.Instance.gameData.currRoomData.players)
|
|
|
|
|
|
usedPositions.Add(p.position);
|
|
|
|
|
|
// 第一个普通炮台的编号作为默认
|
|
|
|
|
|
int defaultTurret = 1;
|
|
|
|
|
|
var turretList = TurretResourcesData.GetTurretDataByType(1);
|
|
|
|
|
|
if (turretList != null && turretList.Count > 0)
|
|
|
|
|
|
defaultTurret = turretList[0].turretNumber;
|
|
|
|
|
|
int botGold = 100000;
|
2026-06-19 18:18:04 +08:00
|
|
|
|
int botDiamond = 500;
|
2026-06-16 19:12:37 +08:00
|
|
|
|
int botMultiple = paoBeiList != null && paoBeiList.Count > 0 ? paoBeiList[0] : 100;
|
|
|
|
|
|
for (int pos = list_Sits.Count - 1; pos >= 0 && botCount > 0; pos--)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (usedPositions.Contains(pos)) continue;
|
2026-06-19 18:18:04 +08:00
|
|
|
|
JoinGame(pos, botGold, botDiamond, botMultiple, 0, "bot_" + pos, defaultTurret.ToString());
|
2026-06-16 19:12:37 +08:00
|
|
|
|
var botSit = list_Sits[pos];
|
|
|
|
|
|
if (botSit != null) botSit.isBot = true;
|
|
|
|
|
|
botCount--;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-18 17:26:14 +08:00
|
|
|
|
GameManager.Instance.gameData.roomJoinNotice = v.arguments[0];
|
|
|
|
|
|
isfirstShow = false;
|
2026-04-29 01:18:39 +08:00
|
|
|
|
|
2026-06-13 01:25:41 +08:00
|
|
|
|
foreach (var item in list_Sits)
|
|
|
|
|
|
{
|
2026-06-14 20:26:21 +08:00
|
|
|
|
if (item.posID == roomJoinNotice.position)
|
2026-06-13 01:25:41 +08:00
|
|
|
|
{
|
|
|
|
|
|
currSitData = item;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
item.btn_TurretAdd.gameObject.SetActive(false);
|
|
|
|
|
|
item.btn_TurretMinus.gameObject.SetActive(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2026-06-08 21:23:40 +08:00
|
|
|
|
//// currSitData.ChangeTurret(1); — 已由 SitPosData.OnEnable 自动加载
|
2026-06-13 22:34:43 +08:00
|
|
|
|
if (currSitData.currentTurret != null)
|
|
|
|
|
|
SyncChangeTurret(currSitData.currentTurret.turretNumber);
|
2026-03-18 17:26:14 +08:00
|
|
|
|
currSitData.lockFX.transform.GetChild(0).gameObject.SetActive(false);
|
|
|
|
|
|
currSitData.lockFX.transform.GetChild(1).gameObject.SetActive(true);
|
2026-06-15 00:03:33 +08:00
|
|
|
|
if (mySitAnim != null && currSitData.turretParent != null)
|
|
|
|
|
|
mySitAnim.transform.position = currSitData.turretParent.position;
|
2026-03-18 17:26:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-06-16 19:12:37 +08:00
|
|
|
|
// 玩家加入 → 如果座位被bot占用则替换
|
|
|
|
|
|
var targetSit = list_Sits.Find(s => s.posID == roomJoinNotice.position);
|
|
|
|
|
|
if (targetSit != null && targetSit.isBot)
|
|
|
|
|
|
{
|
|
|
|
|
|
targetSit.isBot = false;
|
|
|
|
|
|
targetSit.gameObject.SetActive(false);
|
|
|
|
|
|
}
|
2026-06-21 00:07:26 +08:00
|
|
|
|
JoinGame(roomJoinNotice.position, roomJoinNotice.gold, roomJoinNotice.diamond, roomJoinNotice.cannonMultiple, 0, roomJoinNotice.userId, roomJoinNotice.cannonId);
|
2026-03-27 15:18:35 +08:00
|
|
|
|
|
2026-03-18 17:26:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
UIManager.Instance.ShowBoardError("获取信息失败");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2026-06-28 16:48:18 +08:00
|
|
|
|
private void RefreshGGKCount()
|
|
|
|
|
|
{
|
|
|
|
|
|
var props = GameManager.Instance.user?.props;
|
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
if (props != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var p in props)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (p.number == "40005" && p.count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
count = p.count;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (tmp_GGKCount != null)
|
|
|
|
|
|
tmp_GGKCount.text = count.ToString();
|
|
|
|
|
|
}
|
2026-03-18 17:26:14 +08:00
|
|
|
|
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-06-28 16:48:18 +08:00
|
|
|
|
GameManager.Instance.OnUserDataChanged += RefreshGGKCount;
|
2026-05-18 18:39:11 +08:00
|
|
|
|
baseAttackTime = attackTime;
|
2026-06-08 21:23:40 +08:00
|
|
|
|
btn_ChangeTurret.onClick.AddListener(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var panel = subUIManager.Get<SubUIBase>(UIPanel.TurretPanel);
|
|
|
|
|
|
if (panel != null && panel.gameObject.activeSelf)
|
|
|
|
|
|
subUIManager.Hide(UIPanel.TurretPanel);
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
subUIManager.ShowAsync(UIPanel.TurretPanel, (ItemPanelSelect.Turrect, true));
|
|
|
|
|
|
StartCoroutine(CloseSettingList());
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-06-21 00:07:26 +08:00
|
|
|
|
btn_Setting.onClick.AddListener(() => {subUIManager.ShowAsync(UIPanel.SettingPanel); });
|
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);
|
2026-05-27 19:47:06 +08:00
|
|
|
|
EventCenter.Instance.AddEventListener<FireCannonSendData>(ConstEventString.SyncGameData_FireBullet, SyncGameData_FireBullit);
|
2026-05-04 17:21:52 +08:00
|
|
|
|
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);
|
2026-06-19 18:18:04 +08:00
|
|
|
|
EventCenter.Instance.AddEventListener<SyncGameData>(ConstEventString.SyncGameData_ChangeCannonMultiple, SyncGameData_ChangeCannonMultiple);
|
2026-05-28 18:16:45 +08:00
|
|
|
|
EventCenter.Instance.AddEventListener<SyncGameData>(ConstEventString.SyncGameData_FreezeFish, SyncGameData_FreezeFish);
|
2026-06-08 21:23:40 +08:00
|
|
|
|
btn_TuJian.onClick.AddListener(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
subUIManager.ShowAsync(UIPanel.GameTuJian);
|
|
|
|
|
|
StartCoroutine(CloseSettingList());
|
|
|
|
|
|
});
|
2026-06-25 01:48:07 +08:00
|
|
|
|
btn_GuaGuaKa.onClick.AddListener(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
subUIManager.ShowAsync(UIPanel.GuaGuaKa);
|
|
|
|
|
|
});
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
public virtual void OnDestroy()
|
|
|
|
|
|
{
|
|
|
|
|
|
EventCenter.Instance.RemoveEventListener<SyncGameData>(ConstEventString.SyncGameData_Rotate, SyncGameData_Rotate);
|
2026-05-27 19:47:06 +08:00
|
|
|
|
EventCenter.Instance.RemoveEventListener<FireCannonSendData>(ConstEventString.SyncGameData_FireBullet, SyncGameData_FireBullit);
|
2026-05-04 17:21:52 +08:00
|
|
|
|
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);
|
2026-06-19 18:18:04 +08:00
|
|
|
|
EventCenter.Instance.RemoveEventListener<SyncGameData>(ConstEventString.SyncGameData_ChangeCannonMultiple, SyncGameData_ChangeCannonMultiple);
|
2026-05-28 18:16:45 +08:00
|
|
|
|
EventCenter.Instance.RemoveEventListener<SyncGameData>(ConstEventString.SyncGameData_FreezeFish, SyncGameData_FreezeFish);
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
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)
|
2026-06-13 18:23:25 +08:00
|
|
|
|
{
|
2026-05-18 18:39:11 +08:00
|
|
|
|
currSitData.ClearLock();
|
2026-06-13 18:23:25 +08:00
|
|
|
|
SyncTurretState(TurretState.Idle);
|
|
|
|
|
|
}
|
2026-05-18 18:39:11 +08:00
|
|
|
|
|
|
|
|
|
|
// 同步给服务器:取消锁定
|
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);
|
2026-05-28 18:16:45 +08:00
|
|
|
|
if (on) currFrenzyTime = frenzyDuration;
|
2026-05-18 18:39:11 +08:00
|
|
|
|
});
|
|
|
|
|
|
toggle_SkillAuto.onValueChanged.AddListener(on =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!on)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 自动开火关闭 → 立即切回 Idle
|
|
|
|
|
|
currSitData.ChangeState(TurretState.Idle);
|
2026-06-13 01:25:41 +08:00
|
|
|
|
SyncTurretState(TurretState.Idle);
|
2026-05-18 18:39:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-05-28 18:16:45 +08:00
|
|
|
|
toggle_BingFeng.onValueChanged.AddListener(on => {
|
|
|
|
|
|
if (on)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 开始冰冻
|
|
|
|
|
|
currBingFengTime = bingFengDuration;
|
2026-06-16 02:28:58 +08:00
|
|
|
|
if (bingFengEffect != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_bingFengInstance == null)
|
|
|
|
|
|
_bingFengInstance = Instantiate(bingFengEffect);
|
|
|
|
|
|
_bingFengInstance.SetActive(true);
|
|
|
|
|
|
}
|
2026-05-28 18:16:45 +08:00
|
|
|
|
CreateFishManager.Instance.FreezeAllFish();
|
2026-06-21 00:07:26 +08:00
|
|
|
|
PauseAllFishAnimations();
|
2026-05-28 18:16:45 +08:00
|
|
|
|
SyncFreeze(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 手动关闭 → 解冻
|
2026-06-16 02:28:58 +08:00
|
|
|
|
if (_bingFengInstance != null)
|
|
|
|
|
|
_bingFengInstance.SetActive(false);
|
2026-05-28 18:16:45 +08:00
|
|
|
|
if (CreateFishManager.Instance.isFrozen)
|
|
|
|
|
|
{
|
|
|
|
|
|
CreateFishManager.Instance.UnfreezeAllFish();
|
2026-06-21 00:07:26 +08:00
|
|
|
|
ResumeAllFishAnimations();
|
2026-05-28 18:16:45 +08:00
|
|
|
|
SyncFreeze(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-27 19:47:06 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
|
{
|
2026-05-27 19:47:06 +08:00
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
2026-05-27 19:47:06 +08:00
|
|
|
|
int ina = 0;
|
2026-05-29 17:03:08 +08:00
|
|
|
|
protected virtual void Update()
|
2026-05-04 17:21:52 +08:00
|
|
|
|
{
|
2026-05-27 19:47:06 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
HandleAutoRotate();
|
2026-06-14 20:26:21 +08:00
|
|
|
|
HandleInput();
|
2026-05-18 18:39:11 +08:00
|
|
|
|
CheckLockTargetValid();//锁定目标失效自动清除
|
|
|
|
|
|
|
2026-06-14 20:26:21 +08:00
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
HandleAutoFire(); // 自动开火真正发射
|
2026-05-27 19:47:06 +08:00
|
|
|
|
HandleLock();
|
2026-05-04 17:21:52 +08:00
|
|
|
|
UpdateLockFX();
|
2026-06-21 00:28:59 +08:00
|
|
|
|
RefreshTripleLockIcons();
|
2026-05-27 19:47:06 +08:00
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
HandleAutoLock();
|
|
|
|
|
|
LoopLaser();
|
2026-05-18 18:39:11 +08:00
|
|
|
|
SettingClose();
|
2026-05-28 18:16:45 +08:00
|
|
|
|
DaoJiShi();
|
2026-06-16 19:12:37 +08:00
|
|
|
|
HandleBotAI();
|
2026-06-17 21:26:06 +08:00
|
|
|
|
WebSocketSendList.FlushSyncQueues(Time.deltaTime);
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
|
|
|
|
|
}
|
2026-05-18 18:39:11 +08:00
|
|
|
|
float sittingTime = 10;
|
2026-05-27 19:47:06 +08:00
|
|
|
|
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());
|
2026-05-27 19:47:06 +08:00
|
|
|
|
if (isOpenSetting)
|
2026-05-18 18:39:11 +08:00
|
|
|
|
{
|
|
|
|
|
|
currSittingTime -= Time.deltaTime;
|
2026-05-27 19:47:06 +08:00
|
|
|
|
if (currSittingTime <= 0)
|
2026-05-18 18:39:11 +08:00
|
|
|
|
{
|
|
|
|
|
|
StartCoroutine(CloseSettingList());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
currSittingTime = 10;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#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;
|
2026-06-14 02:18:37 +08:00
|
|
|
|
if (input.wasPressedThisFrame)
|
|
|
|
|
|
{
|
|
|
|
|
|
// ⭐ UI 点击过滤
|
|
|
|
|
|
if (IsPointerOverUI(input.position))
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-05-18 18:39:11 +08:00
|
|
|
|
|
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)
|
2026-06-13 01:25:41 +08:00
|
|
|
|
{
|
2026-05-04 17:21:52 +08:00
|
|
|
|
currSitData.ChangeState(TurretState.Attack);
|
2026-06-13 01:25:41 +08:00
|
|
|
|
SyncTurretState(TurretState.Attack);
|
|
|
|
|
|
}
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 长按
|
2026-05-18 18:39:11 +08:00
|
|
|
|
if (input.isPressed)
|
2026-05-04 17:21:52 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (isChangingTurret)
|
|
|
|
|
|
{
|
|
|
|
|
|
HandleChangeTurretHold();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 00:34:19 +08:00
|
|
|
|
// 锁定模式:不手动旋转/开火,交给 HandleAutoRotate + HandleAutoFire
|
|
|
|
|
|
if (toggle_Lock.isOn) return;
|
|
|
|
|
|
|
2026-06-15 21:51:42 +08:00
|
|
|
|
// UI 上不旋转不发射
|
|
|
|
|
|
if (IsPointerOverUI(input.position)) return;
|
2026-06-15 00:34:19 +08:00
|
|
|
|
|
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-06-13 01:25:41 +08:00
|
|
|
|
SyncTurretState(TurretState.Idle);
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
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)
|
|
|
|
|
|
{
|
2026-06-08 21:23:40 +08:00
|
|
|
|
// 长按换炮台:已改为按钮打开 TurretPanel
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
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()
|
|
|
|
|
|
{
|
2026-06-08 21:23:40 +08:00
|
|
|
|
// currSitData.changeTurretList.SetActive(false); — 已废弃
|
2026-05-04 17:21:52 +08:00
|
|
|
|
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
|
|
|
|
|
2026-05-19 17:59:23 +08:00
|
|
|
|
//private void HandleRotate(Vector2 pos)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// if (toggle_Lock.isOn && currSitData.monCotl_Lock != null)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// RotateToLockTarget(currSitData);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// else
|
|
|
|
|
|
// {
|
|
|
|
|
|
// RotateToTouch(currSitData, pos);
|
|
|
|
|
|
// }
|
|
|
|
|
|
//}
|
2026-05-18 18:39:11 +08:00
|
|
|
|
private void HandleRotate(Vector2 pos)
|
2026-05-04 17:21:52 +08:00
|
|
|
|
{
|
2026-05-19 17:59:23 +08:00
|
|
|
|
// ⭐ 只有真正锁定目标时才自动旋转
|
|
|
|
|
|
if (toggle_Lock.isOn && currSitData.isLock && currSitData.monCotl_Lock != null)
|
2026-05-04 17:21:52 +08:00
|
|
|
|
{
|
|
|
|
|
|
RotateToLockTarget(currSitData);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-05-19 17:59:23 +08:00
|
|
|
|
// 锁定开关开着但没有锁定目标 → 允许手动旋转
|
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;
|
|
|
|
|
|
|
2026-06-13 01:25:41 +08:00
|
|
|
|
// 上方座位(2,3)炮台预制体默认朝上,加180°让它朝下打鱼
|
|
|
|
|
|
if (sit.IsFlipped) angle = (angle + 180f) % 360f;
|
|
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
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;
|
2026-06-20 15:00:15 +08:00
|
|
|
|
|
|
|
|
|
|
// ⭐ 鱼太近时 dir 接近零向量,Atan2 会不稳定导致原地转圈 → 跳过
|
|
|
|
|
|
const float minDistance = 5f;
|
|
|
|
|
|
if (dir.sqrMagnitude < minDistance * minDistance) return;
|
|
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg - 90f;
|
|
|
|
|
|
|
2026-06-13 01:25:41 +08:00
|
|
|
|
if (sit.IsFlipped) angle = (angle + 180f) % 360f;
|
|
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
sit.SetRotation(angle);
|
2026-06-20 15:00:15 +08:00
|
|
|
|
SyncRotate(angle);
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
2026-05-18 18:39:11 +08:00
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
private void HandleAutoRotate()
|
|
|
|
|
|
{
|
2026-05-19 17:59:23 +08:00
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
|
|
|
|
|
// 锁定模式优先
|
|
|
|
|
|
if (toggle_Lock.isOn && currSitData.isLock)
|
|
|
|
|
|
{
|
|
|
|
|
|
RotateToLockTarget(currSitData);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-05-19 17:59:23 +08:00
|
|
|
|
// 自动开火关闭 → 不自动旋转
|
|
|
|
|
|
if (!toggle_SkillAuto.isOn) return;
|
2026-05-04 17:21:52 +08:00
|
|
|
|
// 自动扫射(左右摆动)
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-13 01:25:41 +08:00
|
|
|
|
// 上方座位加180°,炮台朝下打鱼
|
|
|
|
|
|
float angle = currSitData.IsFlipped ? (autoRotateAngle + 180f) % 360f : autoRotateAngle;
|
|
|
|
|
|
currSitData.SetRotation(angle);
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
|
|
2026-05-27 19:47:06 +08:00
|
|
|
|
#region Skill系统(SkillModule)
|
|
|
|
|
|
public void DaoJiShi()
|
|
|
|
|
|
{
|
2026-05-29 17:03:08 +08:00
|
|
|
|
// 暴走倒计时
|
|
|
|
|
|
if (toggle_BaoZou != null && toggle_BaoZou.isOn && frenzyDuration > 0)
|
2026-05-27 19:47:06 +08:00
|
|
|
|
{
|
2026-05-29 17:03:08 +08:00
|
|
|
|
if (img_BaoZou != null)
|
|
|
|
|
|
img_BaoZou.fillAmount = Mathf.Clamp01(currFrenzyTime / frenzyDuration);
|
|
|
|
|
|
|
2026-05-27 19:47:06 +08:00
|
|
|
|
currFrenzyTime -= Time.deltaTime;
|
2026-05-29 17:03:08 +08:00
|
|
|
|
|
2026-05-27 19:47:06 +08:00
|
|
|
|
if (currFrenzyTime <= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
toggle_BaoZou.isOn = false;
|
2026-05-29 17:03:08 +08:00
|
|
|
|
currFrenzyTime = 0f;
|
|
|
|
|
|
if (img_BaoZou != null) img_BaoZou.fillAmount = 1f;
|
2026-05-27 19:47:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-29 17:03:08 +08:00
|
|
|
|
|
|
|
|
|
|
// 冰封倒计时
|
|
|
|
|
|
if (toggle_BingFeng != null && toggle_BingFeng.isOn && bingFengDuration > 0)
|
2026-05-27 19:47:06 +08:00
|
|
|
|
{
|
2026-05-29 17:03:08 +08:00
|
|
|
|
if (img_BingFeng != null)
|
|
|
|
|
|
img_BingFeng.fillAmount = Mathf.Clamp01(currBingFengTime / bingFengDuration);
|
2026-05-27 19:47:06 +08:00
|
|
|
|
|
|
|
|
|
|
currBingFengTime -= Time.deltaTime;
|
|
|
|
|
|
|
|
|
|
|
|
if (currBingFengTime <= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
toggle_BingFeng.isOn = false;
|
2026-05-29 17:03:08 +08:00
|
|
|
|
currBingFengTime = 0f;
|
|
|
|
|
|
if (img_BingFeng != null) img_BingFeng.fillAmount = 1f;
|
2026-05-28 18:16:45 +08:00
|
|
|
|
// 解冻由 toggle.onValueChanged(false) 触发,不在此重复
|
2026-05-27 19:47:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
#region ③ 攻击系统(FireModule)
|
|
|
|
|
|
private void TryFire()
|
|
|
|
|
|
{
|
|
|
|
|
|
clickTime += Time.deltaTime;
|
|
|
|
|
|
if (clickTime < attackTime) return;
|
|
|
|
|
|
|
2026-06-20 15:00:15 +08:00
|
|
|
|
// ⭐ 子弹上限校验(先于扣费和状态切换)
|
|
|
|
|
|
if (IsBulletLimitReached(currSitData.user))
|
|
|
|
|
|
{
|
|
|
|
|
|
clickTime = 0; // 重置计时,下次再试
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
clickTime = 0;
|
|
|
|
|
|
|
|
|
|
|
|
float angle = currSitData.currentAngle;
|
|
|
|
|
|
bool isSanShe = toggle_Three.isOn;
|
|
|
|
|
|
bool isDouble = toggle_Shuang.isOn;
|
|
|
|
|
|
bool isLock = toggle_Lock.isOn;
|
|
|
|
|
|
|
2026-05-27 19:47:06 +08:00
|
|
|
|
//FireBullet(currSitData, angle, isSanShe, isDouble, isLock);
|
2026-05-04 17:21:52 +08:00
|
|
|
|
SyncFire(angle, isSanShe, isDouble, isLock);
|
|
|
|
|
|
}
|
2026-05-28 18:16:45 +08:00
|
|
|
|
private void FireBullet(SitPosData sit, float angle, bool isSanShe, bool isDouble, bool isLock, List<string> bullitIDs,
|
|
|
|
|
|
List<string> fireTargetMonsterIDs = null)
|
2026-05-04 17:21:52 +08:00
|
|
|
|
{
|
|
|
|
|
|
Vector2 startPos = sit.GetBulletStartPos();
|
2026-05-28 18:16:45 +08:00
|
|
|
|
|
|
|
|
|
|
// ⭐ 三发 + 锁定 + 有多目标:每发子弹瞄准不同的目标
|
|
|
|
|
|
if (isSanShe && isLock && fireTargetMonsterIDs != null && fireTargetMonsterIDs.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < 3 && i < fireTargetMonsterIDs.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 按 GUID 查找目标
|
|
|
|
|
|
MonsterContral targetMonster = null;
|
|
|
|
|
|
Transform targetHitPos = null;
|
|
|
|
|
|
if (CreateFishManager.Instance.fishDatas_hashCodekey.TryGetValue(fireTargetMonsterIDs[i], out var m)
|
|
|
|
|
|
&& m != null && m.gameObject.activeInHierarchy)
|
|
|
|
|
|
{
|
|
|
|
|
|
targetMonster = m;
|
|
|
|
|
|
targetHitPos = m.transform;
|
2026-06-14 20:26:21 +08:00
|
|
|
|
// 优先用活跃 collider
|
2026-06-14 19:07:11 +08:00
|
|
|
|
if (m.colliderList != null && m.colliderList.Count > 0)
|
2026-05-28 18:16:45 +08:00
|
|
|
|
{
|
|
|
|
|
|
foreach (var col in m.colliderList)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (col.gameObject.activeInHierarchy) { targetHitPos = col; break; }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (targetMonster != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 计算朝目标的角度
|
|
|
|
|
|
Vector3 targetWorldPos = targetHitPos.position;
|
|
|
|
|
|
Vector2 dir = ((Vector2)targetWorldPos - startPos).normalized;
|
|
|
|
|
|
float targetAngle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg - 90f;
|
|
|
|
|
|
|
|
|
|
|
|
string bulletId = i < bullitIDs.Count ? bullitIDs[i] : bullitIDs[0];
|
|
|
|
|
|
CreateBullet(sit, startPos, targetAngle, isDouble, true, bulletId, targetMonster, targetHitPos);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (isSanShe)
|
2026-05-04 17:21:52 +08:00
|
|
|
|
{
|
2026-05-27 19:47:06 +08:00
|
|
|
|
CreateBullet(sit, startPos, angle - 20, isDouble, isLock, bullitIDs[0]);
|
|
|
|
|
|
CreateBullet(sit, startPos, angle, isDouble, isLock, bullitIDs[1]);
|
|
|
|
|
|
CreateBullet(sit, startPos, angle + 20, isDouble, isLock, bullitIDs[2]);
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-05-27 19:47:06 +08:00
|
|
|
|
CreateBullet(sit, startPos, angle, isDouble, isLock, bullitIDs[0]);
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
AudioManager.Instance.PlayFX(bulletClip);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private bool isLaser = false;
|
|
|
|
|
|
|
|
|
|
|
|
private void StartLaser()
|
|
|
|
|
|
{
|
|
|
|
|
|
isLaser = true;
|
|
|
|
|
|
currSitData.ChangeState(TurretState.LaserStart);
|
|
|
|
|
|
|
2026-06-13 01:25:41 +08:00
|
|
|
|
SyncTurretState(TurretState.LaserStart);
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
private void LoopLaser()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!isLaser) return;
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: 激光持续伤害 / 特效
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void StopLaser()
|
|
|
|
|
|
{
|
|
|
|
|
|
isLaser = false;
|
|
|
|
|
|
currSitData.ChangeState(TurretState.LaserEnd);
|
|
|
|
|
|
|
2026-06-13 01:25:41 +08:00
|
|
|
|
SyncTurretState(TurretState.LaserEnd);
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
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-27 19:47:06 +08:00
|
|
|
|
bool needAutoFire = toggle_SkillAuto.isOn || (toggle_Lock.isOn && currSitData.isLock && currSitData.lockFX.gameObject.activeSelf);
|
2026-05-18 18:39:11 +08:00
|
|
|
|
if (!needAutoFire)
|
|
|
|
|
|
{
|
|
|
|
|
|
autoFireTimer = 0f;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
autoFireTimer += Time.deltaTime;
|
|
|
|
|
|
if (autoFireTimer < attackTime) return;
|
|
|
|
|
|
|
2026-06-20 15:00:15 +08:00
|
|
|
|
// ⭐ 子弹上限校验(先于状态切换和扣费)
|
|
|
|
|
|
if (IsBulletLimitReached(currSitData.user))
|
|
|
|
|
|
{
|
|
|
|
|
|
autoFireTimer = 0f; // 重置计时,下次再试
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
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-06-13 01:25:41 +08:00
|
|
|
|
SyncTurretState(TurretState.Attack);
|
2026-05-18 18:39:11 +08:00
|
|
|
|
|
2026-05-27 19:47:06 +08:00
|
|
|
|
//FireBullet(currSitData, angle, isSanShe, isDouble, isLock);
|
2026-05-04 17:21:52 +08:00
|
|
|
|
SyncFire(angle, isSanShe, isDouble, isLock);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-05-18 18:39:11 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-05-28 18:16:45 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region 多目标查找(TripleLock)
|
|
|
|
|
|
/// <summary>鱼锁定优先级:特殊Boss(5) > Boss鱼(4) > 特殊鱼(3) > 黄金鱼(2) > 普通鱼(1)</summary>
|
|
|
|
|
|
private int GetFishLockPriority(MonsterContral monster)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (monster.fishenum)
|
|
|
|
|
|
{
|
|
|
|
|
|
case FishTypeEnum.特殊Boss: return 5;
|
2026-06-14 20:26:21 +08:00
|
|
|
|
case FishTypeEnum.Boss鱼: return 4;
|
|
|
|
|
|
case FishTypeEnum.特殊鱼: return 3;
|
|
|
|
|
|
case FishTypeEnum.黄金鱼: return 2;
|
|
|
|
|
|
case FishTypeEnum.普通鱼: return 1;
|
|
|
|
|
|
default: return 0;
|
2026-05-28 18:16:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 查找 fire 时的多目标(三发锁定用)。
|
|
|
|
|
|
/// 第一个目标 = 主锁 monCotl_Lock(打哪锁哪),剩余从 Boss/特殊Boss/特殊鱼/黄金鱼 按优先级补满
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private List<(MonsterContral monster, Transform hitPos)> FindFireTargets(int maxCount)
|
|
|
|
|
|
{
|
|
|
|
|
|
var result = new List<(MonsterContral monster, Transform hitPos)>();
|
|
|
|
|
|
var selected = new HashSet<string>();
|
|
|
|
|
|
|
|
|
|
|
|
if (CreateFishManager.Instance == null) return result;
|
|
|
|
|
|
|
|
|
|
|
|
// ① 主锁目标排第一(打哪锁哪)
|
|
|
|
|
|
if (currSitData.isLock && currSitData.monCotl_Lock != null &&
|
|
|
|
|
|
currSitData.monCotl_Lock.gameObject.activeInHierarchy)
|
|
|
|
|
|
{
|
|
|
|
|
|
Transform pos = currSitData.monCotl_Lock_Position != null && currSitData.monCotl_Lock_Position.gameObject.activeInHierarchy
|
|
|
|
|
|
? currSitData.monCotl_Lock_Position
|
|
|
|
|
|
: currSitData.monCotl_Lock.transform;
|
|
|
|
|
|
result.Add((currSitData.monCotl_Lock, pos));
|
|
|
|
|
|
selected.Add(currSitData.monCotl_Lock.guid);
|
|
|
|
|
|
if (result.Count >= maxCount) return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ② 按优先级从分类字典补满剩余位置
|
|
|
|
|
|
var dicts = new Dictionary<string, MonsterContral>[]
|
|
|
|
|
|
{
|
|
|
|
|
|
CreateFishManager.Instance.fishDatas_hashCodekey_TS_Boss,
|
|
|
|
|
|
CreateFishManager.Instance.fishDatas_hashCodekey_Boss,
|
|
|
|
|
|
CreateFishManager.Instance.fishDatas_hashCodekey_TS,
|
|
|
|
|
|
CreateFishManager.Instance.fishDatas_hashCodekey_HJ,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var dict in dicts)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (dict == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var kv in dict)
|
|
|
|
|
|
{
|
|
|
|
|
|
MonsterContral m = kv.Value;
|
|
|
|
|
|
if (m == null || !m.gameObject.activeInHierarchy) continue;
|
|
|
|
|
|
if (selected.Contains(m.guid)) continue; // 跳过已选中的
|
|
|
|
|
|
|
|
|
|
|
|
Transform pos = m.transform;
|
2026-06-14 19:07:11 +08:00
|
|
|
|
if (m.colliderList != null && m.colliderList.Count > 0)
|
2026-05-28 18:16:45 +08:00
|
|
|
|
{
|
|
|
|
|
|
foreach (var col in m.colliderList)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (col.gameObject.activeInHierarchy) { pos = col; break; }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result.Add((m, pos));
|
|
|
|
|
|
selected.Add(m.guid);
|
|
|
|
|
|
|
|
|
|
|
|
if (result.Count >= maxCount) return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
2026-05-04 17:21:52 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region 锁定系统(LockModule)
|
2026-06-21 00:28:59 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 刷新三发锁定图标:三发+锁定开启时显示3个锁定圈
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void RefreshTripleLockIcons()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (currSitData == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
bool isTripleLock = toggle_Three.isOn && toggle_Lock.isOn && currSitData.isLock;
|
|
|
|
|
|
if (!isTripleLock)
|
|
|
|
|
|
{
|
|
|
|
|
|
currSitData.HideTripleLockIcons();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 已经有三发目标 → 跳过
|
|
|
|
|
|
if (currSitData.tripleLockTargets.Count > 0) return;
|
|
|
|
|
|
|
|
|
|
|
|
var targets = FindFireTargets(3);
|
|
|
|
|
|
if (targets.Count > 0)
|
|
|
|
|
|
currSitData.SetTripleLockTargets(targets);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-14 19:07:11 +08:00
|
|
|
|
//private void HandleLock()
|
|
|
|
|
|
//{
|
|
|
|
|
|
// CrossPlatformInput input = GetInput();
|
|
|
|
|
|
|
|
|
|
|
|
// if (!toggle_Lock.isOn) return;
|
|
|
|
|
|
|
|
|
|
|
|
// // 只有按下或抬起的那一帧触发锁定(保留你原来的逻辑)
|
|
|
|
|
|
// if (!input.wasReleasedThisFrame && !input.wasPressedThisFrame) return;
|
|
|
|
|
|
|
|
|
|
|
|
// // UI 点击过滤
|
|
|
|
|
|
// if (IsPointerOverUI(input.position))
|
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
|
|
|
|
// // 🔧 修正:屏幕坐标转世界坐标需要指定 Z 轴(2D 正交相机专用)
|
|
|
|
|
|
// Vector3 mousePos = input.position;
|
|
|
|
|
|
// mousePos.z = -Camera.main.transform.position.z; // 关键修正
|
|
|
|
|
|
// Vector2 worldPos = Camera.main.ScreenToWorldPoint(mousePos);
|
|
|
|
|
|
|
|
|
|
|
|
// // 🔧 优化:将 LayerMask 缓存为成员变量,避免每次调用 GetMask(非必要,但更好)
|
|
|
|
|
|
// // 你可以在 Start 中初始化:fishLayerMask = LayerMask.GetMask("Fish");
|
|
|
|
|
|
// // 这里直接写原方式也可以,但建议如下:
|
|
|
|
|
|
// int fishLayer = LayerMask.GetMask("Fish");
|
|
|
|
|
|
// RaycastHit2D hit = Physics2D.Raycast(worldPos, Vector2.zero, 1, fishLayer);
|
|
|
|
|
|
|
|
|
|
|
|
// if (hit.collider != null)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// MonsterContral monster = hit.collider.GetComponentInParent<MonsterContral>();
|
|
|
|
|
|
// currSitData.SetLockTarget(monster, hit.collider.transform);
|
|
|
|
|
|
|
|
|
|
|
|
// // 保留你原来的调用方式:new List<string> { monster?.guid }
|
|
|
|
|
|
// SyncLock(new List<string> { monster?.guid }, monster.colliderList.IndexOf(hit.collider.transform));
|
|
|
|
|
|
// }
|
|
|
|
|
|
// else
|
|
|
|
|
|
// {
|
|
|
|
|
|
// currSitData.ClearLock();
|
|
|
|
|
|
// SyncTurretState(TurretState.Idle);
|
|
|
|
|
|
// SyncLock(null); // 保留你原来的重载(可能是 SyncLock(object) 或 SyncLock(string))
|
|
|
|
|
|
// }
|
|
|
|
|
|
//}
|
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-06-14 19:07:11 +08:00
|
|
|
|
if (!input.wasPressedThisFrame && !input.wasReleasedThisFrame) return;
|
|
|
|
|
|
if (IsPointerOverUI(input.position)) return;
|
2026-05-18 18:39:11 +08:00
|
|
|
|
|
2026-06-14 19:07:11 +08:00
|
|
|
|
// 射线从相机出发,指向鼠标点击点
|
|
|
|
|
|
Vector3 mousePos = input.position;
|
|
|
|
|
|
mousePos.z = -Camera.main.transform.position.z;
|
|
|
|
|
|
Vector2 mouseWorld = Camera.main.ScreenToWorldPoint(mousePos);
|
|
|
|
|
|
//Vector2 origin = Camera.main.transform.position;
|
|
|
|
|
|
//Vector2 dir = (mouseWorld - origin).normalized;
|
|
|
|
|
|
Vector2 dir = Vector2.zero;
|
|
|
|
|
|
float distance = 10000f;
|
|
|
|
|
|
int fishLayer = LayerMask.GetMask("Fish");
|
2026-05-18 18:39:11 +08:00
|
|
|
|
|
2026-06-14 19:07:11 +08:00
|
|
|
|
// 获取所有命中(可能有多个)
|
|
|
|
|
|
RaycastHit2D[] hits = Physics2D.RaycastAll(mouseWorld, dir, distance, fishLayer);
|
|
|
|
|
|
if (hits.Length == 0)
|
2026-05-18 18:39:11 +08:00
|
|
|
|
{
|
2026-06-14 19:07:11 +08:00
|
|
|
|
// 未命中:清除锁定
|
2026-05-18 18:39:11 +08:00
|
|
|
|
currSitData.ClearLock();
|
2026-06-13 18:23:25 +08:00
|
|
|
|
SyncTurretState(TurretState.Idle);
|
2026-05-18 18:39:11 +08:00
|
|
|
|
SyncLock(null);
|
2026-06-14 19:07:11 +08:00
|
|
|
|
return;
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
2026-06-14 19:07:11 +08:00
|
|
|
|
|
|
|
|
|
|
// 按 Z 轴深度选择最前面的(Z 值最大的,因为相机在负 Z,更大 Z 更近)
|
|
|
|
|
|
// 如果您的相机在 Z = -10,物体 Z 范围 0 ~ 10,则 Z 越大越靠近相机
|
|
|
|
|
|
RaycastHit2D bestHit = hits[0];
|
|
|
|
|
|
float bestDepth = bestHit.collider.transform.position.z;
|
|
|
|
|
|
foreach (var hit in hits)
|
|
|
|
|
|
{
|
|
|
|
|
|
float depth = hit.collider.transform.position.z;
|
|
|
|
|
|
if (depth > bestDepth) // 更大 Z → 更近
|
|
|
|
|
|
{
|
|
|
|
|
|
bestDepth = depth;
|
|
|
|
|
|
bestHit = hit;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理选中的最佳命中
|
|
|
|
|
|
MonsterContral monster = bestHit.collider.GetComponentInParent<MonsterContral>();
|
2026-06-14 23:54:59 +08:00
|
|
|
|
Transform lockTr = (monster.colliderList == null || monster.colliderList.Count == 0)
|
|
|
|
|
|
? (monster.lockPoint==null ? bestHit.collider.transform : monster.lockPoint)
|
|
|
|
|
|
: bestHit.collider.transform;
|
|
|
|
|
|
currSitData.SetLockTarget(monster, lockTr);
|
2026-06-14 19:07:11 +08:00
|
|
|
|
SyncLock(new List<string> { monster?.guid }, monster.colliderList.IndexOf(bestHit.collider.transform));
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
2026-05-27 19:47:06 +08:00
|
|
|
|
private void UpdateLockFX()
|
|
|
|
|
|
{
|
2026-05-18 18:39:11 +08:00
|
|
|
|
|
2026-05-27 19:47:06 +08:00
|
|
|
|
foreach (var sit in list_Sits)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!sit.isLock || sit.monCotl_Lock == null) continue;
|
|
|
|
|
|
|
2026-06-21 00:28:59 +08:00
|
|
|
|
// 更新主锁定图标
|
|
|
|
|
|
UpdateSingleLockFX(sit, sit.monCotl_Lock, sit.monCotl_Lock_Position, sit.lockFX);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新三发锁定额外图标
|
|
|
|
|
|
for (int i = 0; i < sit.lockFX_Triple.Count && i < sit.tripleLockTargets.Count; i++)
|
2026-05-27 19:47:06 +08:00
|
|
|
|
{
|
2026-06-21 00:28:59 +08:00
|
|
|
|
var icon = sit.lockFX_Triple[i];
|
|
|
|
|
|
var monster = sit.tripleLockTargets[i];
|
|
|
|
|
|
var hitPos = sit.tripleLockPositions[i];
|
|
|
|
|
|
if (icon == null || monster == null || hitPos == null) continue;
|
|
|
|
|
|
UpdateSingleLockFX(sit, monster, hitPos, icon);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void UpdateSingleLockFX(SitPosData sit, MonsterContral monster, Transform hitPos, GameObject icon)
|
|
|
|
|
|
{
|
|
|
|
|
|
Vector3 pos = Vector3.zero;
|
|
|
|
|
|
|
|
|
|
|
|
if (!hitPos.gameObject.activeInHierarchy)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (monster.colliderList != null && monster.colliderList.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool found = false;
|
|
|
|
|
|
foreach (Transform t in monster.colliderList)
|
2026-05-27 19:47:06 +08:00
|
|
|
|
{
|
2026-06-21 00:28:59 +08:00
|
|
|
|
if (t.gameObject.activeInHierarchy)
|
2026-05-27 19:47:06 +08:00
|
|
|
|
{
|
2026-06-21 00:28:59 +08:00
|
|
|
|
pos = t.position;
|
|
|
|
|
|
found = true;
|
|
|
|
|
|
break;
|
2026-05-27 19:47:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-21 00:28:59 +08:00
|
|
|
|
if (!found)
|
2026-06-14 23:54:59 +08:00
|
|
|
|
{
|
2026-06-21 00:28:59 +08:00
|
|
|
|
if (icon.activeSelf) icon.SetActive(false);
|
|
|
|
|
|
return;
|
2026-05-27 19:47:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-06-21 00:28:59 +08:00
|
|
|
|
var lp = monster.lockPoint ?? monster.transform;
|
|
|
|
|
|
pos = lp.position;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!hitPos.gameObject.activeInHierarchy)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (icon.activeSelf) icon.SetActive(false);
|
|
|
|
|
|
return;
|
2026-05-27 19:47:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-21 00:28:59 +08:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
pos = hitPos.position;
|
|
|
|
|
|
if (!icon.activeSelf) icon.SetActive(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pos.z = Camera.main.transform.position.z + 1;
|
|
|
|
|
|
icon.transform.position = pos;
|
2026-05-27 19:47:06 +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(); // 清除锁定
|
2026-06-13 18:23:25 +08:00
|
|
|
|
SyncTurretState(TurretState.Idle);
|
2026-05-18 18:39:11 +08:00
|
|
|
|
SyncLock(null); // 同步服务器
|
|
|
|
|
|
|
|
|
|
|
|
// ⭐⭐⭐ 关键:怪死了 → 动画回到 Idle ⭐⭐⭐
|
|
|
|
|
|
currSitData.ChangeState(TurretState.Idle);
|
2026-06-13 01:25:41 +08:00
|
|
|
|
SyncTurretState(TurretState.Idle);
|
2026-05-18 18:39:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
private void HandleAutoLock()
|
|
|
|
|
|
{
|
2026-05-19 17:59:23 +08:00
|
|
|
|
// 自动锁定必须满足:自动开火 + 锁定开关开启
|
2026-05-04 17:21:52 +08:00
|
|
|
|
if (!toggle_SkillAuto.isOn) return;
|
2026-05-19 17:59:23 +08:00
|
|
|
|
if (!toggle_Lock.isOn) return; // ✔ 自动锁定必须在锁定开关开启时才工作
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
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)
|
|
|
|
|
|
{
|
2026-06-14 20:26:21 +08:00
|
|
|
|
currSitData.SetLockTarget(target, target.transform);
|
|
|
|
|
|
if (target.colliderList.Count > 0)
|
2026-05-27 19:47:06 +08:00
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < target.colliderList.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (target.colliderList[i].gameObject.activeInHierarchy)
|
|
|
|
|
|
{
|
|
|
|
|
|
SyncLock(new List<string> { target?.guid }, i);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
SyncLock(new List<string> { target?.guid }, -1);
|
|
|
|
|
|
}
|
2026-05-18 18:39:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
currSitData.ClearLock();
|
2026-06-13 18:23:25 +08:00
|
|
|
|
SyncTurretState(TurretState.Idle);
|
2026-05-18 18:39:11 +08:00
|
|
|
|
SyncLock(null);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-19 17:59:23 +08:00
|
|
|
|
|
2026-05-18 18:39:11 +08:00
|
|
|
|
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(子弹系统)
|
2026-06-21 00:07:26 +08:00
|
|
|
|
public int sitBulletMaxCount = 100;
|
2026-06-20 15:00:15 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 检查指定用户是否已达50发子弹上限
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private bool IsBulletLimitReached(string userId)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (bulletParent == null) return false;
|
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
foreach (Transform child in bulletParent.transform)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (child.TryGetComponent<BullitMove>(out var bm) && bm.userid == userId)
|
|
|
|
|
|
count++;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (count >= sitBulletMaxCount)
|
|
|
|
|
|
{
|
|
|
|
|
|
UIManager.Instance.ShowBoardError("子弹已达上限");
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-28 18:16:45 +08:00
|
|
|
|
private BullitMove CreateBullet(SitPosData sit, Vector2 pos, float angle, bool isDouble, bool isLock, string id,
|
|
|
|
|
|
MonsterContral lockTarget = null, Transform lockHitPos = null)
|
2026-05-04 17:21:52 +08:00
|
|
|
|
{
|
2026-06-13 01:25:41 +08:00
|
|
|
|
// ⭐ 远程座位炮台未加载时补加载
|
|
|
|
|
|
if (sit.currentTurret == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
sit.LoadTurret(sit.currentTurretNumber);
|
|
|
|
|
|
if (sit.currentTurret == null) return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
GameObject prefab = isDouble ? sit.currentTurret.bulletPrefab_Two : sit.currentTurret.bulletPrefab;
|
|
|
|
|
|
|
2026-06-21 00:07:26 +08:00
|
|
|
|
// ⭐ 每座位最多100发子弹(先校验,不扣钱)
|
2026-06-20 15:00:15 +08:00
|
|
|
|
int activeBullets = 0;
|
|
|
|
|
|
foreach (Transform child in bulletParent.transform)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (child.TryGetComponent<BullitMove>(out var bm) && bm.userid == sit.user)
|
|
|
|
|
|
activeBullets++;
|
|
|
|
|
|
}
|
2026-06-21 00:07:26 +08:00
|
|
|
|
if (activeBullets >= sitBulletMaxCount)
|
2026-06-20 15:00:15 +08:00
|
|
|
|
{
|
2026-06-21 00:07:26 +08:00
|
|
|
|
UIManager.Instance.ShowBoardError($"[子弹上限] 座位{sit.posID}已达{sitBulletMaxCount}发上限");
|
2026-06-20 15:00:15 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
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;
|
2026-05-27 19:47:06 +08:00
|
|
|
|
bullet.bulletID = id;
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
|
|
|
|
|
if (isLock)
|
2026-05-28 18:16:45 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (lockTarget != null)
|
|
|
|
|
|
bullet.Lock(lockTarget, lockHitPos);
|
|
|
|
|
|
else
|
|
|
|
|
|
bullet.Lock(sit.monCotl_Lock, currSitData.monCotl_Lock_Position);
|
|
|
|
|
|
}
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
{
|
2026-06-16 19:12:37 +08:00
|
|
|
|
var fireData = new FrameData_FireBullet
|
2026-05-28 18:16:45 +08:00
|
|
|
|
{
|
|
|
|
|
|
angle = angle,
|
|
|
|
|
|
gold = currSitData.goldNum,
|
|
|
|
|
|
isSanShe = isSanShe,
|
|
|
|
|
|
twoRTruuect = isDouble,
|
|
|
|
|
|
isSuoDing = isLock,
|
|
|
|
|
|
auto = toggle_SkillAuto.isOn
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// ⭐ 三发 + 锁定:查找 3 个优先级目标,写入 monsterIDs
|
|
|
|
|
|
if (isSanShe && isLock)
|
|
|
|
|
|
{
|
|
|
|
|
|
var targets = FindFireTargets(3);
|
|
|
|
|
|
if (targets.Count > 0)
|
|
|
|
|
|
fireData.monsterIDs = targets.Select(t => t.monster.guid).ToList();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
SyncGameData sgd = new SyncGameData
|
|
|
|
|
|
{
|
|
|
|
|
|
sitPos = currSitData.posID,
|
|
|
|
|
|
frameType = FrameDataType.fireBullet,
|
|
|
|
|
|
time = BeijingTimeManager.Instance.CurrentTimestampMs,
|
2026-05-28 18:16:45 +08:00
|
|
|
|
frameData = fireData
|
2026-05-04 17:21:52 +08:00
|
|
|
|
};
|
2026-06-17 21:26:06 +08:00
|
|
|
|
//EnqueueSync(sgd);
|
2026-05-27 19:47:06 +08:00
|
|
|
|
var roomdata = GameManager.Instance.gameData.currRoomData;
|
|
|
|
|
|
var user = GameManager.Instance.user;
|
|
|
|
|
|
|
|
|
|
|
|
// 三射 = 3 发,否则 = 1 发
|
|
|
|
|
|
int count = isSanShe ? 3 : 1;
|
|
|
|
|
|
|
|
|
|
|
|
// 新版 FireCannon 不需要 timestamp 和 sign
|
|
|
|
|
|
WebSocketSendList.FireCannon(
|
|
|
|
|
|
roomdata.roomId,
|
|
|
|
|
|
user.id,
|
|
|
|
|
|
currSitData.consumeGoldNum,
|
|
|
|
|
|
count,
|
|
|
|
|
|
sgd,
|
|
|
|
|
|
"63215f0afa3f5b25"
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// WebSocketSendList.FireCannon(roomdata.roomId, user.id,currSitData.consumeGoldNum,1,BeijingTimeManager.Instance.CurrentTimestamp, "63215f0afa3f5b25", sgd);
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-27 19:47:06 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-06-14 20:26:21 +08:00
|
|
|
|
private void SyncLock(List<string> monsterguid, int index = -1)
|
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
|
|
|
|
|
|
{
|
2026-05-27 19:47:06 +08:00
|
|
|
|
targetGuid = monsterguid,
|
2026-06-14 20:26:21 +08:00
|
|
|
|
colliderIndex = index,
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
WebSocketSendList.SyncGameData(sgd);
|
|
|
|
|
|
}
|
2026-06-13 01:25:41 +08:00
|
|
|
|
private void SyncTurretState(TurretState state)
|
2026-05-04 17:21:52 +08:00
|
|
|
|
{
|
|
|
|
|
|
SyncGameData sgd = new SyncGameData
|
|
|
|
|
|
{
|
|
|
|
|
|
sitPos = currSitData.posID,
|
|
|
|
|
|
frameType = FrameDataType.changeTurretState,
|
|
|
|
|
|
time = BeijingTimeManager.Instance.CurrentTimestampMs,
|
|
|
|
|
|
frameData = new FrameData_TurretState { state = state }
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
WebSocketSendList.SyncGameData(sgd);
|
|
|
|
|
|
}
|
2026-06-13 22:34:43 +08:00
|
|
|
|
public void SyncChangeTurret(int turretNumber)
|
2026-05-04 17:21:52 +08:00
|
|
|
|
{
|
|
|
|
|
|
SyncGameData sgd = new SyncGameData
|
|
|
|
|
|
{
|
|
|
|
|
|
sitPos = currSitData.posID,
|
|
|
|
|
|
frameType = FrameDataType.changeTurret,
|
|
|
|
|
|
time = BeijingTimeManager.Instance.CurrentTimestampMs,
|
2026-06-08 21:23:40 +08:00
|
|
|
|
frameData = new FrameData_ChangeTurret { turretIndex = turretNumber }
|
2026-05-04 17:21:52 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
WebSocketSendList.SyncGameData(sgd);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-28 18:16:45 +08:00
|
|
|
|
private void SyncFreeze(bool isFreeze)
|
|
|
|
|
|
{
|
|
|
|
|
|
SyncGameData sgd = new SyncGameData
|
|
|
|
|
|
{
|
|
|
|
|
|
sitPos = currSitData.posID,
|
|
|
|
|
|
frameType = FrameDataType.freezeFish,
|
|
|
|
|
|
time = BeijingTimeManager.Instance.CurrentTimestampMs,
|
|
|
|
|
|
frameData = new FrameData_FreezeFish { isFreeze = isFreeze }
|
|
|
|
|
|
};
|
|
|
|
|
|
WebSocketSendList.SyncGameData(sgd);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
|
|
|
|
|
#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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-27 19:47:06 +08:00
|
|
|
|
public void SyncGameData_FireBullit(FireCannonSendData sync)
|
2026-05-04 17:21:52 +08:00
|
|
|
|
{
|
2026-05-27 19:47:06 +08:00
|
|
|
|
//if (sync.sitPos == currSitData.posID) return;
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
2026-06-16 19:12:37 +08:00
|
|
|
|
var data = FrameDataParser.Parse<FrameData_FireBullet>(sync.extra.frameData);
|
2026-05-28 18:16:45 +08:00
|
|
|
|
FireBullet(list_Sits[sync.extra.sitPos], data.angle, data.isSanShe, data.twoRTruuect, data.isSuoDing, sync.bulletIds, data.monsterIDs);
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-27 19:47:06 +08:00
|
|
|
|
MonsterContral monster = CreateFishManager.Instance.fishDatas_hashCodekey[data.targetGuid[0]];
|
2026-06-14 19:07:11 +08:00
|
|
|
|
Transform hitPos;
|
2026-06-14 23:54:59 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-06-14 19:07:11 +08:00
|
|
|
|
if (monster.colliderList != null && monster.colliderList.Count > 0 && data.colliderIndex != -1)
|
|
|
|
|
|
hitPos = monster.colliderList[data.colliderIndex];
|
|
|
|
|
|
else
|
2026-06-14 23:54:59 +08:00
|
|
|
|
hitPos = monster.lockPoint ?? monster.transform;
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
|
|
|
|
|
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);
|
2026-06-08 21:23:40 +08:00
|
|
|
|
list_Sits[sync.sitPos].LoadTurret(data.turretIndex);
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-19 18:18:04 +08:00
|
|
|
|
public void SyncGameData_ChangeCannonMultiple(SyncGameData sync)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (sync.sitPos == currSitData.posID) return;
|
|
|
|
|
|
|
|
|
|
|
|
var data = FrameDataParser.Parse<FrameData_ChangeCannonMultiple>(sync.frameData);
|
|
|
|
|
|
var sit = list_Sits[sync.sitPos];
|
|
|
|
|
|
sit.consumeGoldNum = data.cannonMultiple;
|
|
|
|
|
|
sit.tMP_TurretConsumeNum.text = data.cannonMultiple.ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void SyncCannonMultiple(int consumeGold)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (currSitData == null) return;
|
|
|
|
|
|
SyncGameData sgd = new SyncGameData
|
|
|
|
|
|
{
|
|
|
|
|
|
sitPos = currSitData.posID,
|
|
|
|
|
|
frameType = FrameDataType.changeCannonMultiple,
|
|
|
|
|
|
time = BeijingTimeManager.Instance.CurrentTimestampMs,
|
|
|
|
|
|
frameData = new FrameData_ChangeCannonMultiple { cannonMultiple = consumeGold }
|
|
|
|
|
|
};
|
|
|
|
|
|
WebSocketSendList.SyncGameData(sgd);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-28 18:16:45 +08:00
|
|
|
|
public void SyncGameData_FreezeFish(SyncGameData sync)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (sync.sitPos == currSitData.posID) return; // 自己发的不管
|
|
|
|
|
|
|
|
|
|
|
|
var data = FrameDataParser.Parse<FrameData_FreezeFish>(sync.frameData);
|
|
|
|
|
|
if (data.isFreeze)
|
2026-06-21 00:07:26 +08:00
|
|
|
|
{
|
2026-05-28 18:16:45 +08:00
|
|
|
|
CreateFishManager.Instance.FreezeAllFish();
|
2026-06-21 00:07:26 +08:00
|
|
|
|
PauseAllFishAnimations();
|
|
|
|
|
|
}
|
2026-05-28 18:16:45 +08:00
|
|
|
|
else
|
2026-06-21 00:07:26 +08:00
|
|
|
|
{
|
2026-05-28 18:16:45 +08:00
|
|
|
|
CreateFishManager.Instance.UnfreezeAllFish();
|
2026-06-21 00:07:26 +08:00
|
|
|
|
ResumeAllFishAnimations();
|
|
|
|
|
|
}
|
2026-05-28 18:16:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
2026-06-21 00:07:26 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
#region 冰冻动画暂停/恢复
|
|
|
|
|
|
|
|
|
|
|
|
private void PauseAllFishAnimations()
|
|
|
|
|
|
{
|
|
|
|
|
|
var root = CreateFishManager.Instance.fishRoot;
|
|
|
|
|
|
if (root == null) return;
|
|
|
|
|
|
_frozenAnimSpeeds.Clear();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var s in root.GetComponentsInChildren<Spine.Unity.SkeletonAnimation>(true))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!s.gameObject.activeInHierarchy) continue;
|
|
|
|
|
|
_frozenAnimSpeeds[s] = s.timeScale;
|
|
|
|
|
|
s.timeScale = 0f;
|
|
|
|
|
|
}
|
|
|
|
|
|
foreach (var a in root.GetComponentsInChildren<Animator>(true))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!a.gameObject.activeInHierarchy) continue;
|
|
|
|
|
|
_frozenAnimSpeeds[a] = a.speed;
|
|
|
|
|
|
a.speed = 0f;
|
|
|
|
|
|
}
|
|
|
|
|
|
foreach (var an in root.GetComponentsInChildren<Animation>(true))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!an.gameObject.activeInHierarchy || !an.isPlaying) continue;
|
|
|
|
|
|
foreach (AnimationState state in an) state.speed = 0f;
|
|
|
|
|
|
_frozenAnimSpeeds[an] = 1f;
|
|
|
|
|
|
}
|
|
|
|
|
|
foreach (var pd in root.GetComponentsInChildren<UnityEngine.Playables.PlayableDirector>(true))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!pd.gameObject.activeInHierarchy || pd.state != PlayState.Playing) continue;
|
|
|
|
|
|
_frozenAnimSpeeds[pd] = 1f;
|
|
|
|
|
|
pd.Pause();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void ResumeAllFishAnimations()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_frozenAnimSpeeds.Count == 0) return;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var kv in _frozenAnimSpeeds)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (kv.Key == null) continue;
|
|
|
|
|
|
switch (kv.Key)
|
|
|
|
|
|
{
|
|
|
|
|
|
case Spine.Unity.SkeletonAnimation s: s.timeScale = kv.Value; break;
|
|
|
|
|
|
case Animator a: a.speed = kv.Value; break;
|
|
|
|
|
|
case Animation an:
|
|
|
|
|
|
foreach (AnimationState state in an) state.speed = 1f;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case UnityEngine.Playables.PlayableDirector pd: pd.Resume(); break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
_frozenAnimSpeeds.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-04 17:21:52 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region 玩家加入/退出(PlayerJoinModule)
|
|
|
|
|
|
|
2026-06-21 00:07:26 +08:00
|
|
|
|
public void JoinGame(int sitNum, long goldNum, long diamondNum, long consumeGold, int currentTurretIndex, string user = null, string cannonId = null)
|
2026-05-04 17:21:52 +08:00
|
|
|
|
{
|
2026-05-27 19:47:06 +08:00
|
|
|
|
foreach (var sit in list_Sits)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (sit.posID != sitNum) continue;
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
2026-05-27 19:47:06 +08:00
|
|
|
|
sit.user = user ?? GameManager.Instance.user.id;
|
|
|
|
|
|
sit.goldNum = goldNum;
|
|
|
|
|
|
sit.consumeGoldNum = consumeGold;
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
2026-05-27 19:47:06 +08:00
|
|
|
|
sit.tMP_GoldNum.text = goldNum.ToString();
|
2026-06-21 00:07:26 +08:00
|
|
|
|
sit.tMP_DiamondNum.text = diamondNum.ToString();
|
2026-05-27 19:47:06 +08:00
|
|
|
|
sit.tMP_TurretConsumeNum.text = consumeGold.ToString();
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
2026-06-08 21:23:40 +08:00
|
|
|
|
if (sit.user == GameManager.Instance?.user?.id)
|
|
|
|
|
|
SitPosData.localPlayerSit = sit;
|
|
|
|
|
|
|
|
|
|
|
|
sit.gameObject.SetActive(true);
|
|
|
|
|
|
|
2026-06-13 22:34:43 +08:00
|
|
|
|
if (!string.IsNullOrEmpty(cannonId) && int.TryParse(cannonId, out int turretNum))
|
|
|
|
|
|
sit.LoadTurret(turretNum);
|
2026-05-27 19:47:06 +08:00
|
|
|
|
}
|
2026-05-04 17:21:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-27 19:47:06 +08:00
|
|
|
|
public void TuiChuHHWK()
|
|
|
|
|
|
{
|
|
|
|
|
|
WebSocketSendList.LeaveRoom(GameManager.Instance.gameData.currRoomData.roomId, currSitData.user);
|
|
|
|
|
|
CreateFishManager.Instance.fangzhu = false;
|
|
|
|
|
|
}
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
2026-05-27 19:47:06 +08:00
|
|
|
|
#endregion
|
2026-05-04 17:21:52 +08:00
|
|
|
|
|
2026-06-16 19:12:37 +08:00
|
|
|
|
#region 人机AI
|
|
|
|
|
|
private void HandleBotAI()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 只有房主控制人机
|
|
|
|
|
|
if (SitPosData.roomOwnerSit == null || currSitData != SitPosData.roomOwnerSit) return;
|
2026-06-16 23:45:31 +08:00
|
|
|
|
if (!GameManager.Instance.enableBot) return;
|
2026-06-16 19:12:37 +08:00
|
|
|
|
|
|
|
|
|
|
var fishList = CreateFishManager.Instance;
|
|
|
|
|
|
if (fishList == null || fishList.fishDatas_hashCodekey == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var sit in list_Sits)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!sit.isBot || sit.gameObject.activeSelf == false) continue;
|
|
|
|
|
|
if (sit.tra_CurrTurret == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
// 找最近鱼
|
|
|
|
|
|
MonsterContral target = null;
|
|
|
|
|
|
float bestDist = float.MaxValue;
|
|
|
|
|
|
Vector3 turretPos = sit.tra_CurrTurret.position;
|
|
|
|
|
|
foreach (var kv in fishList.fishDatas_hashCodekey)
|
|
|
|
|
|
{
|
|
|
|
|
|
var m = kv.Value;
|
|
|
|
|
|
if (m == null || !m.gameObject.activeInHierarchy) continue;
|
|
|
|
|
|
float d = (m.transform.position - turretPos).sqrMagnitude;
|
|
|
|
|
|
if (d < bestDist) { bestDist = d; target = m; }
|
|
|
|
|
|
}
|
|
|
|
|
|
if (target == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
// 旋转朝向鱼
|
|
|
|
|
|
Vector3 fishWorldPos = target.transform.position;
|
|
|
|
|
|
Vector3 screenPos = Camera.main.WorldToScreenPoint(fishWorldPos);
|
|
|
|
|
|
RotateToTouch(sit, screenPos);
|
|
|
|
|
|
|
|
|
|
|
|
// 定时开火
|
|
|
|
|
|
if (!botFireTimer.ContainsKey(sit.posID))
|
|
|
|
|
|
botFireTimer[sit.posID] = 0f;
|
|
|
|
|
|
botFireTimer[sit.posID] += Time.deltaTime;
|
|
|
|
|
|
if (botFireTimer[sit.posID] >= attackTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
botFireTimer[sit.posID] = 0f;
|
|
|
|
|
|
float angle = sit.currentAngle;
|
|
|
|
|
|
SyncFire_ForBot(sit, angle);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>人机开火同步(房主代发)</summary>
|
|
|
|
|
|
private void SyncFire_ForBot(SitPosData botSit, float angle)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool isSanShe = toggle_Three.isOn;
|
|
|
|
|
|
bool isDouble = toggle_Shuang.isOn;
|
|
|
|
|
|
SyncGameData sgd = new SyncGameData
|
|
|
|
|
|
{
|
|
|
|
|
|
sitPos = botSit.posID,
|
|
|
|
|
|
frameType = FrameDataType.fireBullet,
|
|
|
|
|
|
time = BeijingTimeManager.Instance.CurrentTimestampMs,
|
|
|
|
|
|
frameData = new FrameData_FireBullet
|
|
|
|
|
|
{
|
|
|
|
|
|
angle = angle,
|
|
|
|
|
|
gold = botSit.consumeGoldNum,
|
|
|
|
|
|
isSanShe = isSanShe,
|
|
|
|
|
|
twoRTruuect = isDouble,
|
|
|
|
|
|
isSuoDing = false,
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
WebSocketSendList.SyncGameData(sgd);
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2026-02-13 19:23:01 +08:00
|
|
|
|
}
|