1
This commit is contained in:
@@ -118,6 +118,18 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
/// <summary>是否正在退出倒计时(期间屏蔽帧同步和爆奖特效)</summary>
|
||||
private bool _isLeavingCountdown = false;
|
||||
private Coroutine _leaveCountdownCoroutine;
|
||||
|
||||
[Header("无操作自动退出")]
|
||||
[Tooltip("无操作总时长(秒),走完自动退出回大厅(<=0 关闭该功能)")]
|
||||
public float idleExitTotalSeconds = 70f;
|
||||
[Tooltip("最后多少秒才显示倒计时面板(autoExitGamePanel)并逐秒倒数,之前全程无提示")]
|
||||
public float idleExitPanelShowSeconds = 10f;
|
||||
/// <summary>无操作计时是否生效(资源加载完成后才开始计时,OnHide/离房时失效)</summary>
|
||||
private bool _idleExitArmed = false;
|
||||
/// <summary>最近一次玩家操作的时间(Time.realtimeSinceStartup,含切后台时间)</summary>
|
||||
private float _lastOperationTime = 0f;
|
||||
/// <summary>警告文本当前显示的剩余秒数(-1=未显示;避免每帧刷新 TMP 文本)</summary>
|
||||
private int _idleWarnShownSeconds = -1;
|
||||
[Header("游戏场景")]
|
||||
public string sceneName;
|
||||
public GameTypeEnum gameType;
|
||||
@@ -181,6 +193,8 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
public Toggle toggle_Lock;
|
||||
public Toggle toggle_Three;
|
||||
public Toggle toggle_Shuang;
|
||||
|
||||
|
||||
// ⭐ 双倍开火:toggle_Shuang 开启后计时 shuangDuration 秒自动关闭(DaoJiShi 驱动),
|
||||
// child0 的 Image(Filled 360°) 做环形倒计时显示(GetShuangFillImage 懒获取)
|
||||
float shuangDuration = 15f;
|
||||
@@ -189,14 +203,15 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
public Toggle toggle_BaoZou;
|
||||
public TextMeshProUGUI tmp_BaoZouCount;
|
||||
public Image img_BaoZou;
|
||||
float frenzyDuration = 5f; // 疯狂状态持续时间
|
||||
[Header("狂暴/冰封持续时间(秒,可在面板手动设置)")]
|
||||
public float frenzyDuration = 15f; // 疯狂状态持续时间
|
||||
public float currFrenzyTime = 0f;
|
||||
public Toggle toggle_BingFeng;
|
||||
public TextMeshProUGUI tmp_BingFengCount;
|
||||
public Image img_BingFeng;
|
||||
public GameObject bingFengEffect; // 冰封特效预制体(首次Instantiate,之后激活/隐藏)
|
||||
private GameObject _bingFengInstance; // 克隆出来的实例
|
||||
float bingFengDuration = 15f; // 冰封状态持续时间(服务端 FishFrozen 延长 15 秒,客户端写死 15s 倒计时)
|
||||
public float bingFengDuration = 15f; // 冰封状态持续时间(服务端 FishFrozen 延长 15 秒,可在面板调整)
|
||||
public float currBingFengTime = 0f;
|
||||
private Dictionary<Component, object> _frozenAnimSpeeds = new Dictionary<Component, object>();
|
||||
|
||||
@@ -243,7 +258,10 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
public GameObject bulletParent;
|
||||
float changeTurretTime = 0f;
|
||||
bool isChangingTurret = false;
|
||||
|
||||
[Header("超时自动退出倒计时")]
|
||||
public GameObject autoExitGamePanel; //倒计时面板
|
||||
public TextMeshProUGUI tmp_ExitDaoJiShi; //里面显示的是这个 n秒后自动关闭
|
||||
public Button btn_CloseAutoExitGamePanel; //退出游戏按钮
|
||||
// XJMH 长按炮台扇形切换
|
||||
[Header("XJMH 炮台长按切换")]
|
||||
[Tooltip("长按炮台触发扇形切换的阈值(秒)")]
|
||||
@@ -279,6 +297,9 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
protected override void OnShow(object args = null)
|
||||
{
|
||||
base.OnShow(args);
|
||||
// ⭐ 无操作自动退出:加载期间不计时,LoadResources 完成后重新开始计;确保倒计时面板初始隐藏
|
||||
_idleExitArmed = false;
|
||||
HideIdleExitWarning();
|
||||
LoadResources();
|
||||
RefreshGGKCount();
|
||||
//UIManager.Instance.ShowBoard("太好看了");
|
||||
@@ -352,7 +373,11 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
currSitData.lockFX.transform.GetChild(0).gameObject.SetActive(false);
|
||||
currSitData.lockFX.transform.GetChild(1).gameObject.SetActive(true);
|
||||
if (mySitAnim != null && currSitData.turretParent != null)
|
||||
{
|
||||
mySitAnim.transform.position = currSitData.turretParent.position;
|
||||
// ⭐ 进入游戏圆圈:6 秒后自动消失(点击屏幕立即消失的逻辑在 HandleInput)
|
||||
StartCoroutine(HideMySitAnimAfter(6f));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -478,6 +503,10 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
UnhookOpenPTLongPress();
|
||||
UnhookAutoAttackOff();
|
||||
|
||||
// ⭐ 无操作自动退出:面板隐藏即停止计时
|
||||
_idleExitArmed = false;
|
||||
HideIdleExitWarning();
|
||||
|
||||
UnLoadResources();
|
||||
// 场景可能未加载或已卸载,做保护避免重复卸载报错
|
||||
var scene = SceneManager.GetSceneByName(sceneName);
|
||||
@@ -604,6 +633,10 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
if (btn_ChangeRoom != null)
|
||||
btn_ChangeRoom.onClick.AddListener(StartChangeRoom);
|
||||
|
||||
// ⭐ 无操作自动退出:关闭倒计时面板并重新计时(未拖引用时为null,做保护)
|
||||
if (btn_CloseAutoExitGamePanel != null)
|
||||
btn_CloseAutoExitGamePanel.onClick.AddListener(OnCloseAutoExitPanel);
|
||||
|
||||
// ⭐ 左侧道具按钮:开启/关闭 obj_PropPanel(一级列表由 PropConfigData 配置构建,
|
||||
// 点击一级道具在 tmp_PropTwoPanel 展开二级子道具)
|
||||
if (btn_Prop != null)
|
||||
@@ -726,6 +759,124 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
if (btn_QueRen != null) btn_QueRen.interactable = true;
|
||||
}
|
||||
|
||||
#region 无操作自动退出(60秒静默 → 最后10秒倒计时面板 → 70秒自动回大厅)
|
||||
|
||||
/// <summary>
|
||||
/// ⭐ 每帧记录玩家操作(触摸/鼠标/键盘,含 UI 点击),超时自动走离房流程回大厅。
|
||||
/// 机器人/其他玩家的同步数据不算操作;退出确认面板开着时超时同样生效。
|
||||
/// 只在资源加载完成后计时(加载慢不会被踢);离房流程进行中不重复触发。
|
||||
/// 流程:前 idleExitTotalSeconds - idleExitPanelShowSeconds(默认60秒)全程无提示
|
||||
/// → 最后 idleExitPanelShowSeconds(默认10秒)才弹出倒计时面板(autoExitGamePanel)
|
||||
/// 逐秒倒数 → 走满 idleExitTotalSeconds(默认70秒)自动退出回大厅;
|
||||
/// 期间任意操作或点 btn_CloseAutoExitGamePanel → 关闭面板并重新计时。
|
||||
/// </summary>
|
||||
private void UpdateIdleAutoExit()
|
||||
{
|
||||
if (idleExitTotalSeconds <= 0f || idleExitPanelShowSeconds <= 0f)
|
||||
{
|
||||
HideIdleExitWarning();
|
||||
return;
|
||||
}
|
||||
|
||||
// 任何触摸/鼠标/键盘输入(含点在 UI 上)都算一次操作
|
||||
if (IsAnyOperationThisFrame())
|
||||
{
|
||||
_lastOperationTime = Time.realtimeSinceStartup;
|
||||
// 有操作 → 立即隐藏倒计时面板
|
||||
HideIdleExitWarning();
|
||||
}
|
||||
|
||||
// 计时未生效(加载中/已隐藏)或已在离房流程中 → 跳过
|
||||
if (!_idleExitArmed || _leaveRoomResponseTcs != null) return;
|
||||
|
||||
float idleElapsed = Time.realtimeSinceStartup - _lastOperationTime;
|
||||
// 面板弹出时刻:总时长 - 面板显示时长(70 - 10 = 60 秒)
|
||||
float panelShowAt = idleExitTotalSeconds - idleExitPanelShowSeconds;
|
||||
|
||||
if (idleElapsed >= idleExitTotalSeconds)
|
||||
{
|
||||
HideIdleExitWarning();
|
||||
Debug.Log($"[IdleExit] 连续 {idleExitTotalSeconds} 秒无操作,自动退出游戏回大厅");
|
||||
// 退出确认面板还开着 → 先关掉,再走离房流程
|
||||
if (_isLeavingCountdown) CancelLeaveCountdown();
|
||||
RequestLeaveRoom();
|
||||
return;
|
||||
}
|
||||
|
||||
// 只有最后 idleExitPanelShowSeconds 秒才显示面板;之前保持隐藏
|
||||
if (idleElapsed >= panelShowAt)
|
||||
ShowIdleExitWarning(Mathf.CeilToInt(idleExitTotalSeconds - idleElapsed));
|
||||
else
|
||||
HideIdleExitWarning();
|
||||
}
|
||||
|
||||
/// <summary>显示/刷新无操作退出倒计时面板(仅在剩余秒数变化时刷新文本,避免每帧 GC)</summary>
|
||||
private void ShowIdleExitWarning(int remainingSeconds)
|
||||
{
|
||||
// 弹出倒计时面板(未拖面板引用时退化为只显示文本)
|
||||
if (autoExitGamePanel != null)
|
||||
{
|
||||
if (!autoExitGamePanel.activeSelf)
|
||||
autoExitGamePanel.SetActive(true);
|
||||
}
|
||||
else if (tmp_ExitDaoJiShi != null && !tmp_ExitDaoJiShi.gameObject.activeSelf)
|
||||
{
|
||||
tmp_ExitDaoJiShi.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
if (tmp_ExitDaoJiShi != null && _idleWarnShownSeconds != remainingSeconds)
|
||||
{
|
||||
_idleWarnShownSeconds = remainingSeconds;
|
||||
tmp_ExitDaoJiShi.text =
|
||||
$"({remainingSeconds}秒后自动退出)";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>隐藏无操作退出倒计时面板(有操作/关闭按钮/开始离房/面板隐藏时调用)</summary>
|
||||
private void HideIdleExitWarning()
|
||||
{
|
||||
if (autoExitGamePanel != null)
|
||||
{
|
||||
if (autoExitGamePanel.activeSelf)
|
||||
{
|
||||
autoExitGamePanel.SetActive(false);
|
||||
_idleWarnShownSeconds = -1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (tmp_ExitDaoJiShi != null && tmp_ExitDaoJiShi.gameObject.activeSelf)
|
||||
{
|
||||
tmp_ExitDaoJiShi.gameObject.SetActive(false);
|
||||
_idleWarnShownSeconds = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>btn_CloseAutoExitGamePanel:关闭倒计时面板并从头重新计时</summary>
|
||||
private void OnCloseAutoExitPanel()
|
||||
{
|
||||
HideIdleExitWarning();
|
||||
_lastOperationTime = Time.realtimeSinceStartup;
|
||||
}
|
||||
|
||||
/// <summary>本帧是否有玩家输入:触摸/鼠标按下、按住、松开 或 任意按键</summary>
|
||||
private bool IsAnyOperationThisFrame()
|
||||
{
|
||||
var input = GetInput();
|
||||
if (input.isPressed || input.wasPressedThisFrame || input.wasReleasedThisFrame)
|
||||
return true;
|
||||
var keyboard = Keyboard.current;
|
||||
return keyboard != null && keyboard.anyKey.isPressed;
|
||||
}
|
||||
|
||||
/// <summary>开始无操作计时(LoadResources 完成后调用,计时从当前时刻重新起算)</summary>
|
||||
private void ArmIdleAutoExit()
|
||||
{
|
||||
_lastOperationTime = Time.realtimeSinceStartup;
|
||||
_idleExitArmed = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public virtual void OnDestroy()
|
||||
{
|
||||
EventCenter.Instance.RemoveEventListener<SyncGameData>(ConstEventString.SyncGameData_Rotate, SyncGameData_Rotate);
|
||||
@@ -873,8 +1024,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
|
||||
|
||||
UpdateIdleAutoExit();
|
||||
|
||||
HandleAutoRotate();
|
||||
HandleInput();
|
||||
@@ -991,6 +1141,9 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
ClearChangeRoomBar();
|
||||
else if (JinDuTiao != null)
|
||||
JinDuTiao.Hide();
|
||||
|
||||
// ⭐ 无操作自动退出:加载完成,从现在开始计无操作时间
|
||||
ArmIdleAutoExit();
|
||||
}
|
||||
|
||||
public async void UnLoadResources()
|
||||
@@ -2074,6 +2227,14 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
}
|
||||
|
||||
|
||||
/// <summary>进入游戏圆圈 mySitAnim:延时自动消失(6 秒没点屏幕也消失;点击屏幕立即消失在 HandleInput)</summary>
|
||||
private IEnumerator HideMySitAnimAfter(float delay)
|
||||
{
|
||||
yield return new WaitForSeconds(delay);
|
||||
if (mySitAnim != null && mySitAnim.gameObject.activeSelf)
|
||||
mySitAnim.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void HandleInput()
|
||||
{
|
||||
// ⭐ 退出倒计时期间屏蔽本地玩家手动输入(旋转/开火/锁定),其他玩家和机器人不受影响
|
||||
@@ -3123,7 +3284,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
MonsterContral m = kv.Value;
|
||||
if (m == null || !m.gameObject.activeInHierarchy) continue;
|
||||
if (selected.Contains(m.guid)) continue; // 跳过已选中的
|
||||
// ⭐ 路径进度过滤:刚出生(< 0.2)或即将离场(> 0.8)的鱼不作为目标
|
||||
// ⭐ 路径进度过滤:刚出生(< 0.1)或即将离场(> 0.9)的鱼不作为目标
|
||||
if (!IsFishPathLockable(m)) continue;
|
||||
// ⭐ 可锁定过滤:出场效果期间/needLockEvent 鱼未就绪 → 跳过换下一条
|
||||
if (!IsMonsterLockable(m)) continue;
|
||||
@@ -3444,20 +3605,23 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
// ⭐ 用世界坐标(Z=0平面),和 bot AI 一致,否则距离计算会异常(出现 3.4E+38 之类)
|
||||
Vector3 origin = currSitData.turretWorldPos;
|
||||
|
||||
// ⭐ 规则:特殊Boss / Boss鱼 / 特殊鱼 为高价值组(按优先级 + 距离最近),
|
||||
// 黄金鱼 / 普通鱼 为普通组(随机选一只)
|
||||
// 高价值组有目标时只从高价值组选;高价值组无目标时从普通组随机
|
||||
// ⭐ 规则:严格按优先级分层,高价值到低价值:
|
||||
// 特殊Boss(5) > Boss鱼(4) > 特殊鱼(3) > 黄金鱼(2) > 普通鱼(1)
|
||||
// - 特殊Boss/Boss鱼/特殊鱼:同层取距离最近
|
||||
// - 黄金鱼/普通鱼:各自独立分层,同层随机选一只
|
||||
// 高层有目标时绝不选低层
|
||||
MonsterContral best = null;
|
||||
int bestPri = -1;
|
||||
float bestSqr = float.MaxValue;
|
||||
|
||||
var normalList = new List<MonsterContral>();
|
||||
var goldList = new List<MonsterContral>(); // 黄金鱼(2) 单独一层
|
||||
var smallList = new List<MonsterContral>(); // 普通鱼(1) 单独一层
|
||||
|
||||
foreach (var kv in CreateFishManager.Instance.fishDatas_hashCodekey)
|
||||
{
|
||||
MonsterContral m = kv.Value;
|
||||
if (m == null || !m.gameObject.activeInHierarchy) continue;
|
||||
// ⭐ 路径进度过滤:刚出生(< 0.2)或即将离场(> 0.8)的鱼不作为自动锁定目标
|
||||
// ⭐ 路径进度过滤:刚出生(< 0.1)或即将离场(> 0.9)的鱼不作为自动锁定目标
|
||||
if (!IsFishPathLockable(m)) continue;
|
||||
// ⭐ 可锁定过滤:出场效果期间/needLockEvent 鱼未就绪 → 跳过换下一条
|
||||
if (!IsMonsterLockable(m)) continue;
|
||||
@@ -3475,19 +3639,27 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
best = m;
|
||||
}
|
||||
}
|
||||
// 普通组:黄金鱼(2) / 普通鱼(1) → 先收集,后面随机
|
||||
// ⭐ 黄金鱼(2) / 普通鱼(1) 分开收集,不再混在一个列表里
|
||||
else if (pri == 2)
|
||||
{
|
||||
goldList.Add(m);
|
||||
}
|
||||
else
|
||||
{
|
||||
normalList.Add(m);
|
||||
smallList.Add(m);
|
||||
}
|
||||
}
|
||||
|
||||
// 高价值组有目标 → 直接返回最高价值(同价值最近)的目标
|
||||
// ① 特殊Boss/Boss鱼/特殊鱼 有目标 → 返回最高价值(同价值最近)的目标
|
||||
if (best != null) return best;
|
||||
|
||||
// 高价值组无目标 → 从普通组随机选一只
|
||||
if (normalList.Count > 0)
|
||||
return normalList[Random.Range(0, normalList.Count)];
|
||||
// ② 没有特殊鱼及以上 → 黄金鱼层随机选一只
|
||||
if (goldList.Count > 0)
|
||||
return goldList[Random.Range(0, goldList.Count)];
|
||||
|
||||
// ③ 没有黄金鱼 → 普通鱼层随机选一只
|
||||
if (smallList.Count > 0)
|
||||
return smallList[Random.Range(0, smallList.Count)];
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -4489,7 +4661,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
{
|
||||
var m = kv.Value;
|
||||
if (m == null || !m.gameObject.activeInHierarchy) continue;
|
||||
// ⭐ 路径进度过滤:刚出生(< 0.2)或即将离场(> 0.8)的 Boss 不作为目标
|
||||
// ⭐ 路径进度过滤:刚出生(< 0.1)或即将离场(> 0.9)的 Boss 不作为目标
|
||||
if (!IsFishPathLockable(m)) continue;
|
||||
|
||||
int ft = (int)m.fishenum;
|
||||
@@ -4820,7 +4992,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
{
|
||||
var m = kv.Value;
|
||||
if (m == null || !m.gameObject.activeInHierarchy) continue;
|
||||
// ⭐ 路径进度过滤:刚出生(< 0.2)或即将离场(> 0.8)的鱼不作为目标
|
||||
// ⭐ 路径进度过滤:刚出生(< 0.1)或即将离场(> 0.9)的鱼不作为目标
|
||||
if (!IsFishPathLockable(m)) continue;
|
||||
|
||||
Vector3 fishPos = m.transform.position;
|
||||
@@ -4867,7 +5039,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
|
||||
var m = kv.Value;
|
||||
if (m == null || !m.gameObject.activeInHierarchy) continue;
|
||||
if (m == exclude) continue;
|
||||
// ⭐ 路径进度过滤:刚出生(< 0.2)或即将离场(> 0.8)的鱼不作为目标
|
||||
// ⭐ 路径进度过滤:刚出生(< 0.1)或即将离场(> 0.9)的鱼不作为目标
|
||||
if (!IsFishPathLockable(m)) continue;
|
||||
|
||||
Vector3 fishPos = m.transform.position;
|
||||
|
||||
Reference in New Issue
Block a user