This commit is contained in:
2026-10-11 21:57:58 +08:00
parent d35c47deea
commit 0d53dc073d
14 changed files with 287300 additions and 275445 deletions
+172 -2
View File
@@ -1,4 +1,6 @@
using System;
using System.Collections;
using Spine.Unity;
using UnityEngine;
/// <summary>
@@ -277,10 +279,178 @@ public class FlyTurretData : TurretData
base.ChangeState(newState);
}
// ==================== XJMH 进房入场动画(原版 Battery:EnterAction,进入房间时本地座位播一次) ====================
// 原版链路(PW_Data.InitPlayer 收到自己"大飞机信息"):
// GC.BatteryMgr.PlayEnterAction(chairId, PW_Data.EnterAction) —— 首次 false 播完整动画+声音,
// 之后 true 只立即摆好。完整流程:
// Enter 音效 → 容器(底座)Spine 播 "out" → 炮台本体隐藏、飞机出现 →
// 飞机从下方两段升起(EInQuart 0.5s → 停 0.3s → EOutQuart 0.5s)→
// PlaneBack 音效 + 飞机播 "back" → 约 1 秒后飞机隐藏、炮台本体播 "out" 出现。
[Header("XJMH入场动画(原版 EnterAction:飞机从下方升起 → 变回炮台)")]
[Tooltip("入场音效 id(原版 PW_Enter,tbaudio 60003);0=不播")]
public int enterAudioID = 60003;
[Tooltip("飞机回位音效 id(原版 PW_PlaneBack,tbaudio 61106);0=不播")]
public int enterBackAudioID = 61106;
[Tooltip("飞机入场起点相对停靠位的 Y 偏移(负值=从更下方升起,原版从画面下方飞入)")]
public float enterStartOffsetY = -260f;
[Tooltip("第一段升起时长(秒),原版 0.5")]
public float enterRiseTime = 0.5f;
[Tooltip("两段之间的停顿(秒),原版 0.3")]
public float enterHoldTime = 0.3f;
[Tooltip("第二段升到停靠位时长(秒),原版 0.5")]
public float enterBackRiseTime = 0.5f;
[Tooltip("飞机 \"back\" 动画开始后,飞机隐藏、炮台出现的延迟(秒),原版 1.0")]
public float enterBackHoldTime = 1f;
private Coroutine _enterRoutine;
private RectTransform _enterPlane; // 入场中的飞机节点(离场清理复位用)
private Vector2 _enterPlaneHome; // 飞机停靠位(预制体配好的 anchoredPosition)
/// <summary>
/// XJMH 进房入场动画(移植原版 Battery:EnterAction 的非立即分支,本地玩家入座时调用一次):
/// Enter 音效 → 容器 "out" → 飞机从停靠位下方两段升起(PlaneBack 音效 + "back")→
/// 延迟后飞机隐藏、炮台本体播 "out" 出现。
/// ⭐ 未激活(面板加载中/座位隐藏)时直接返回——SitPosData 会挂起并在激活后补播。
/// 找不到飞机节点时只播炮台出现动画(兜底不报错)。
/// </summary>
public void PlayEnterAction()
{
if (!isActiveAndEnabled) return;
Debug.Log($"[XJMH入场] {name} 开始播放(Enter音效={enterAudioID} 回位音效={enterBackAudioID})");
if (_enterRoutine != null) StopCoroutine(_enterRoutine);
_enterRoutine = StartCoroutine(EnterActionRoutine());
}
private IEnumerator EnterActionRoutine()
{
if (enterAudioID > 0 && AudioManager.Instance != null)
AudioManager.Instance.PlayerFX(enterAudioID);
_enterPlane = FindPlaneSkeletonRect();
if (_enterPlane == null)
{
Debug.LogWarning($"[XJMH入场] {name} 未找到飞机节点(名字含 feiji),只播炮台出现动画");
PlayTurretOutAnim();
_enterRoutine = null;
yield break;
}
SkeletonGraphic planeSg = _enterPlane.GetComponent<SkeletonGraphic>();
_enterPlaneHome = _enterPlane.anchoredPosition;
Vector2 home = _enterPlaneHome;
Vector2 start = home + new Vector2(0f, enterStartOffsetY);
Vector2 mid = home + new Vector2(0f, enterStartOffsetY * 0.5f);
// ⭐ 原版 EnterAction 第 6 步:底部(容器/底座外壳)Spine 播 "out" 出场
// (PW_Battery.txt:EnterAction → bottomSkeleton.AnimationState:SetAnimation(0,"out",false))
PlayContainerOutAnim();
// 原版:入场时炮台本体隐藏、飞机出现(飞机从下方升上来,"变"回炮台)
if (skeletonAnimation != null)
skeletonAnimation.gameObject.SetActive(false);
_enterPlane.gameObject.SetActive(true);
_enterPlane.anchoredPosition = start;
if (planeSg != null && planeSg.AnimationState != null)
planeSg.AnimationState.ClearTrack(0); // 清残留(对象池/重复入场)
// 第一段:升到中途位(原版 EInQuart)
yield return RiseTo(_enterPlane, start, mid, enterRiseTime, true);
// 停顿(原版 delay 0.3)
if (enterHoldTime > 0f) yield return new WaitForSeconds(enterHoldTime);
// 第二段:升到停靠位(原版 EOutQuart)
yield return RiseTo(_enterPlane, mid, home, enterBackRiseTime, false);
// PlaneBack 音效 + 飞机回位动画(原版 PlaneBack → PlaneBackAnim)
if (enterBackAudioID > 0 && AudioManager.Instance != null)
AudioManager.Instance.PlayerFX(enterBackAudioID);
if (planeSg != null && planeSg.AnimationState != null)
planeSg.AnimationState.SetAnimation(0, "back", false);
// 原版:back 动画约 1 秒后飞机隐藏、炮台出现(ShowGun)
if (enterBackHoldTime > 0f) yield return new WaitForSeconds(enterBackHoldTime);
_enterPlane.gameObject.SetActive(false);
_enterPlane.anchoredPosition = home;
PlayTurretOutAnim();
_enterPlane = null;
_enterRoutine = null;
}
/// <summary>协程位移:EInQuart / EOutQuart 缓动(与原版 GC.Action 一致)</summary>
private static IEnumerator RiseTo(RectTransform rt, Vector2 from, Vector2 to, float time, bool easeIn)
{
if (rt == null) yield break;
if (time <= 0f) { rt.anchoredPosition = to; yield break; }
float t = 0f;
while (t < time)
{
t += Time.deltaTime;
float p = Mathf.Clamp01(t / time);
float e = easeIn ? p * p * p * p : 1f - Mathf.Pow(1f - p, 4f);
rt.anchoredPosition = Vector2.LerpUnclamped(from, to, e);
yield return null;
}
rt.anchoredPosition = to;
}
/// <summary>炮台本体出现:恢复显示并播 startingAnimation(预制体配的 "out";为空时兜底 "out")</summary>
private void PlayTurretOutAnim()
{
if (skeletonAnimation == null) return;
skeletonAnimation.gameObject.SetActive(true);
if (skeletonAnimation.AnimationState == null) return;
string anim = string.IsNullOrEmpty(skeletonAnimation.startingAnimation) ? "out" : skeletonAnimation.startingAnimation;
skeletonAnimation.AnimationState.SetAnimation(0, anim, skeletonAnimation.startingLoop);
skeletonAnimation.Update(0.1f);
}
/// <summary>
/// 容器(底座/炮台外壳)出场动画:XJMH 炮台容器自身的 Spine 播 "out"(容器动画为 close/open/out)。
/// 对应原版 EnterAction 第 6 步 bottomSkeleton "out";容器上没有该动画时跳过、不报错。
/// </summary>
private void PlayContainerOutAnim()
{
var shell = GetComponent<SkeletonGraphic>();
if (shell == null || shell.AnimationState == null) return;
if (shell.Skeleton != null && shell.Skeleton.Data != null
&& shell.Skeleton.Data.FindAnimation("out") == null) return; // 没有 out 动画:跳过
shell.AnimationState.SetAnimation(0, "out", false);
shell.Update(0.1f);
}
/// <summary>找飞机节点:名字含 "feiji" 的 SkeletonGraphic(四种炮台各配一架:lvptfeiji/lanptfeiji/huangjinfeiji/vipfeiji01)</summary>
private RectTransform FindPlaneSkeletonRect()
{
foreach (var sg in GetComponentsInChildren<SkeletonGraphic>(true))
{
if (sg == null) continue;
if (sg.gameObject.name.ToLowerInvariant().Contains("feiji"))
return sg.rectTransform;
}
return null;
}
/// <summary>入场动画清理:停协程 + 复位(飞机隐藏回停靠位、炮台本体恢复显示),禁用/回收不残留</summary>
private void CleanupEnterAction()
{
if (_enterRoutine != null) { StopCoroutine(_enterRoutine); _enterRoutine = null; }
if (_enterPlane != null)
{
_enterPlane.anchoredPosition = _enterPlaneHome;
_enterPlane.gameObject.SetActive(false);
_enterPlane = null;
}
if (skeletonAnimation != null && !skeletonAnimation.gameObject.activeSelf)
skeletonAnimation.gameObject.SetActive(true);
}
private void OnDisable()
{
// ⭐ 基类入场动画清理:子类 OnDisable 会覆盖基类的私有 OnDisable(Unity 只调最派生),
// 必须显式调用,保证"飞机升起/炮台出场"的清理不被漏掉(在提前 return 之前调用)
// ⭐ 入场动画清理(停协程、飞机复位隐藏、炮台本体恢复显示):必须在提前 return 之前调用
CleanupEnterAction();
// 切炮台/座位被禁用时兜底复位
+56 -6
View File
@@ -456,6 +456,7 @@ public class SitPosData : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
// 入场动画标记复位:座位再次激活(重进房/重新入座)时重新播
_xjmhEnterPlayed = false;
_xjmhEnterPending = false;
if (localPlayerSit == this)
localPlayerSit = null;
@@ -476,6 +477,8 @@ public class SitPosData : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
private void Update()
{
UpdateXJMHEnterPending(); // ⭐ XJMH 入场动画挂起补播(面板/炮台就绪即播,避免加载期播了看不到)
UpdateZhanShenLaser();
// ⭐ 长按 img_OpenPT 计时:到达阈值触发一次(提前松手不触发)
@@ -1148,21 +1151,65 @@ public class SitPosData : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
/// <summary>XJMH 本地座位进房入场动画是否已播(本次激活周期只播一次;OnDisable 复位)</summary>
private bool _xjmhEnterPlayed;
/// <summary>已到入座时机但炮台/面板尚未激活(播了看不到)→ 挂起,等激活后 Update 补播</summary>
private bool _xjmhEnterPending;
/// <summary>
/// XJMH 本地座位进房入场动画(原版 Battery:EnterAction 非立即分支):
/// 本次激活周期只播一次,防 ShowPlayer 被重复调用时重播。
/// XJMH 本地座位进房入场动画(原版 PW_Data.InitPlayer → BatteryMgr.PlayEnterAction,首次播完整动画+声音):
/// 入座时调用一次;面板/炮台未激活时挂起(此时播玩家看不到、协程也起不来)→ Update 补播。
/// </summary>
private void PlayXJMHEnterActionOnce()
{
if (_xjmhEnterPlayed) return;
_xjmhEnterPlayed = true;
if (_xjmhEnterPlayed || _xjmhEnterPending) return;
var turret = currentTurret;
if (turret == null && xjmhTurretList != null && xjmhTurretList.Count > 0)
turret = xjmhTurretList[0];
if (turret != null)
turret.PlayEnterAction();
// 炮台当前不可播(面板加载中/炮台未激活):挂起等就绪
if (turret == null || !turret.isActiveAndEnabled)
{
_xjmhEnterPending = true;
Debug.Log($"[XJMH入场] 座位 {name} 挂起(炮台未就绪:turret={(turret == null ? "null" : turret.name)})");
return;
}
_xjmhEnterPlayed = true;
Debug.Log($"[XJMH入场] 座位 {name} 触发播放(炮台={turret.name})");
if (turret is FlyTurretData flyTurret)
flyTurret.PlayEnterAction();
}
/// <summary>
/// XJMH 入场动画触发/补播检查(Update 每帧):
/// ① 兜底触发:本地座位已入座(user=本地)且从未触发过 → 走一次触发(覆盖 ShowPlayer 路径未走到的情况);
/// ② 挂起补播:已触发但炮台/面板当时未激活 → 就绪(玩家可见)立即播。
/// </summary>
private void UpdateXJMHEnterPending()
{
// ⭐ 加载到 100% 前不触发/不补播(玩家看不到、位置未就绪):等加载完成后 Update 自然触发
var panel = CreateFishManager.Instance?.currGameType as GameMainPanel_FishBase;
if (panel == null || !panel.IsGameLoadCompleted) return;
// ① 兜底触发(Update 只在座位/面板激活时执行 → 触发时玩家已可见)
if (turretControlMode == TurretControlMode.XJMH
&& !_xjmhEnterPlayed && !_xjmhEnterPending
&& !string.IsNullOrEmpty(user) && user == GameManager.Instance?.user?.id)
{
PlayXJMHEnterActionOnce();
}
// ② 挂起补播
if (!_xjmhEnterPending || _xjmhEnterPlayed) return;
var turret = currentTurret;
if (turret == null || !turret.isActiveAndEnabled) return;
_xjmhEnterPending = false;
_xjmhEnterPlayed = true;
Debug.Log($"[XJMH入场] 座位 {name} 补播(炮台={turret.name})");
if (turret is FlyTurretData flyTurret)
flyTurret.PlayEnterAction();
}
/// <summary>
@@ -1175,6 +1222,9 @@ public class SitPosData : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
if (toggle_AutoAttack != null)
toggle_AutoAttack.gameObject.SetActive(false);
// ⭐ 离座取消入场挂起(重进房时重新播放)
_xjmhEnterPending = false;
if (turretControlMode == TurretControlMode.XJMH) return;
if (transform.childCount > 0)
-152
View File
@@ -1,5 +1,4 @@
using Spine.Unity;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
@@ -324,157 +323,6 @@ public class TurretData : MonoBehaviour
_appearAnimHoldUntil = Time.time + hold;
}
// ==================== XJMH 入场动画(原版 PW_Battery:EnterAction,进入房间时本地座位播一次) ====================
[Header("XJMH入场动画(原版 EnterAction:飞机从下方升起 → 变回炮台)")]
[Tooltip("入场音效 id(原版 PW_Enter,tbaudio 60003);0=不播")]
public int enterAudioID = 60003;
[Tooltip("飞机回位音效 id(原版 PW_PlaneBack,tbaudio 61106);0=不播")]
public int enterBackAudioID = 61106;
[Tooltip("飞机入场起点相对停靠位的 Y 偏移(负值=从更下方升起,原版从画面下方飞入)")]
public float enterStartOffsetY = -260f;
[Tooltip("第一段升起时长(秒),原版 0.5")]
public float enterRiseTime = 0.5f;
[Tooltip("两段之间的停顿(秒),原版 0.3")]
public float enterHoldTime = 0.3f;
[Tooltip("第二段升到停靠位时长(秒),原版 0.5")]
public float enterBackRiseTime = 0.5f;
[Tooltip("飞机 \"back\" 动画开始后,飞机隐藏、炮台出现的延迟(秒),原版 1.0")]
public float enterBackHoldTime = 1f;
private Coroutine _enterRoutine;
private RectTransform _enterPlane; // 入场中的飞机节点(离场清理复位用)
private Vector2 _enterPlaneHome; // 飞机停靠位(预制体配好的 anchoredPosition)
/// <summary>
/// XJMH 入场动画(移植原版 Battery:EnterAction 的非立即分支,进入房间时由本地座位调用一次):
/// Enter 音效 → 飞机从停靠位下方升起 → 停顿 → 升到停靠位(PlaneBack 音效 + 飞机播 "back")
/// → 延迟后飞机隐藏、炮台本体出现并播 "out"(原版 ShowGun)。
/// 找不到飞机节点时只播炮台出现动画(兜底不报错)。
/// </summary>
public void PlayEnterAction()
{
if (!isActiveAndEnabled) return;
if (_enterRoutine != null) StopCoroutine(_enterRoutine);
_enterRoutine = StartCoroutine(EnterActionRoutine());
}
private IEnumerator EnterActionRoutine()
{
if (enterAudioID > 0 && AudioManager.Instance != null)
AudioManager.Instance.PlayerFX(enterAudioID);
_enterPlane = FindPlaneSkeletonRect();
if (_enterPlane == null)
{
PlayTurretOutAnim();
_enterRoutine = null;
yield break;
}
SkeletonGraphic planeSg = _enterPlane.GetComponent<SkeletonGraphic>();
_enterPlaneHome = _enterPlane.anchoredPosition;
Vector2 home = _enterPlaneHome;
Vector2 start = home + new Vector2(0f, enterStartOffsetY);
Vector2 mid = home + new Vector2(0f, enterStartOffsetY * 0.5f);
// 原版:入场时炮台本体隐藏、飞机出现(飞机从下方升上来,"变"回炮台)
if (skeletonAnimation != null)
skeletonAnimation.gameObject.SetActive(false);
_enterPlane.gameObject.SetActive(true);
_enterPlane.anchoredPosition = start;
if (planeSg != null && planeSg.AnimationState != null)
planeSg.AnimationState.ClearTrack(0); // 清残留(对象池/重复入场)
// 第一段:升到中途位(原版 EInQuart)
yield return RiseTo(_enterPlane, start, mid, enterRiseTime, true);
// 停顿(原版 delay 0.3)
if (enterHoldTime > 0f) yield return new WaitForSeconds(enterHoldTime);
// 第二段:升到停靠位(原版 EOutQuart)
yield return RiseTo(_enterPlane, mid, home, enterBackRiseTime, false);
// PlaneBack 音效 + 飞机回位动画(原版 PlaneBack → PlaneBackAnim)
if (enterBackAudioID > 0 && AudioManager.Instance != null)
AudioManager.Instance.PlayerFX(enterBackAudioID);
if (planeSg != null && planeSg.AnimationState != null)
planeSg.AnimationState.SetAnimation(0, "back", false);
// 原版:back 动画 1 秒后飞机隐藏、炮台出现(ShowGun)
if (enterBackHoldTime > 0f) yield return new WaitForSeconds(enterBackHoldTime);
_enterPlane.gameObject.SetActive(false);
_enterPlane.anchoredPosition = home;
PlayTurretOutAnim();
_enterPlane = null;
_enterRoutine = null;
}
/// <summary>协程位移:EInQuart / EOutQuart 缓动(与原版 GC.Action 一致)</summary>
private static IEnumerator RiseTo(RectTransform rt, Vector2 from, Vector2 to, float time, bool easeIn)
{
if (rt == null) yield break;
if (time <= 0f) { rt.anchoredPosition = to; yield break; }
float t = 0f;
while (t < time)
{
t += Time.deltaTime;
float p = Mathf.Clamp01(t / time);
float e = easeIn ? p * p * p * p : 1f - Mathf.Pow(1f - p, 4f);
rt.anchoredPosition = Vector2.LerpUnclamped(from, to, e);
yield return null;
}
rt.anchoredPosition = to;
}
/// <summary>炮台本体出现:恢复显示并播 startingAnimation(预制体配的 "out";为空时兜底 "out")</summary>
private void PlayTurretOutAnim()
{
if (skeletonAnimation == null) return;
skeletonAnimation.gameObject.SetActive(true);
if (skeletonAnimation.AnimationState == null) return;
string anim = string.IsNullOrEmpty(skeletonAnimation.startingAnimation) ? "out" : skeletonAnimation.startingAnimation;
skeletonAnimation.AnimationState.SetAnimation(0, anim, skeletonAnimation.startingLoop);
skeletonAnimation.Update(0.1f);
}
/// <summary>找飞机节点:名字含 "feiji" 的 SkeletonGraphic(四种炮台各配一架:lvptfeiji/lanptfeiji/huangjinfeiji/vipfeiji01)</summary>
private RectTransform FindPlaneSkeletonRect()
{
foreach (var sg in GetComponentsInChildren<SkeletonGraphic>(true))
{
if (sg == null) continue;
if (sg.gameObject.name.ToLowerInvariant().Contains("feiji"))
return sg.rectTransform;
}
return null;
}
/// <summary>
/// 入场动画清理:停协程 + 复位(飞机隐藏回停靠位、炮台本体恢复显示),禁用/回收不残留。
/// ⭐ protected(不直接放 private OnDisable 里):子类(如 FlyTurretData)自带 OnDisable 时,
/// Unity 只会调用最派生的 OnDisable、基类同名私有方法被跳过,
/// 子类需在自己的 OnDisable 里显式调用本方法,保证入场动画清理不被漏掉。
/// </summary>
protected void CleanupEnterAction()
{
if (_enterRoutine != null) { StopCoroutine(_enterRoutine); _enterRoutine = null; }
if (_enterPlane != null)
{
_enterPlane.anchoredPosition = _enterPlaneHome;
_enterPlane.gameObject.SetActive(false);
_enterPlane = null;
}
if (skeletonAnimation != null && !skeletonAnimation.gameObject.activeSelf)
skeletonAnimation.gameObject.SetActive(true);
}
private void OnDisable()
{
CleanupEnterAction();
}
private void PlayState(TurretState state)
{
switch (currTurretType)
@@ -319,6 +319,13 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
public ProgressBar JinDuTiao;
#region UI
bool isfirstShow = true;
/// <summary>加载到 100% 是否已完成(进房初始化/入场动画等需在加载完成后执行:玩家可见、位置就绪)</summary>
public bool IsGameLoadCompleted { get; private set; }
/// <summary>进房"首个初始化"(座位/房间信息)已挂起、等加载到 100% 后执行</summary>
private bool _pendingFirstShowInit;
/// <summary>挂起时保存的进房通知(ApplyFirstShowInit 里写入 gameData.roomJoinNotice)</summary>
private RoomJoinNotice _pendingRoomJoinNotice;
protected override void OnShow(object args = null)
{
base.OnShow(args);
@@ -358,61 +365,10 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
}
if (isfirstShow)
{
string roomId = GameManager.Instance.gameData.currRoomData.roomId;
//string roomId = GameManager.Instance.gameData.currRoomData;
foreach (var item in GameManager.Instance.gameData.currRoomData.players)
{
JoinGame(item.position, item.gold, item.diamond, item.cannonMultiple, 0, item.userId, item.cannonId, item.wingsId, item.warGodId);
// ⭐ 优先用后端 isBot 字段,兼容 userId.StartsWith("bot_")
var sit = list_Sits.Find(s => s.posID == item.position);
if (sit != null)
{
bool isBot = item.isBot ||
(!string.IsNullOrEmpty(item.userId) && item.userId.StartsWith("bot_"));
if (isBot) sit.isBot = true;
}
}
GameManager.Instance.gameData.roomJoinNotice = v.arguments[0];
isfirstShow = false;
foreach (var item in list_Sits)
{
// ⭐ 座位镜像:本地玩家的显示座位 = 服务端座位转显示空间(上方座位翻转后落回下方)
if (item.posID == SeatMirror.ToDisplay(roomJoinNotice.Position))
{
currSitData = item;
// ⭐ 长按本地座位的 img_OpenPT 开关炮台面板(与 btn_ChangeTurret 同逻辑)
HookOpenPTLongPress(currSitData);
// ⭐ 本地座位自动攻击关闭时广播 Idle 状态给其他客户端
HookAutoAttackOff(currSitData);
}
else
{
// ⭐ 非本地座位隐藏加减炮倍:Normal 藏座位按钮(未拖引用为 null 跳过),
// XJMH 藏炮台上的加减倍(TurretData.obj_Add / obj_Down)
item.HideTurretAddMinusUI();
// ⭐ 自动攻击开关与加减倍按钮一样:非本地座位隐藏
if (item.toggle_AutoAttack != null)
item.toggle_AutoAttack.gameObject.SetActive(false);
}
}
//// currSitData.ChangeTurret(1); — 已由 SitPosData.OnEnable 自动加载
if (currSitData.currentTurret != null)
SyncChangeTurret(currSitData.currentTurret.turretNumber);
currSitData.lockFX.transform.GetChild(0).gameObject.SetActive(false);
currSitData.lockFX.transform.GetChild(1).gameObject.SetActive(true);
if (mySitAnim != null && currSitData.turretParent != null)
{
// ⭐ 座位位置 + 左右座位各自的偏移:把"我在这"圆圈摆到座位上面
// (左右座位镜像后需要的偏移不一样,分左/右两套配置,面板上调)
Vector3 sitAnimOffset = IsLeftSeat(currSitData) ? mySitAnimOffsetLeft : mySitAnimOffsetRight;
mySitAnim.transform.position = currSitData.turretParent.position + sitAnimOffset;
// ⭐ 进入游戏圆圈:6 秒后自动消失(点击屏幕立即消失的逻辑在 HandleInput)
StartCoroutine(HideMySitAnimAfter(6f));
}
// ⭐ 座位/房间信息初始化推迟到"加载到 100%"后执行(见 ApplyFirstShowInit 与加载完成点):
// 原来在这里立即 JoinGame/摆座位,进房入场动画会在加载期播放——玩家看不到、位置未就绪。
_pendingFirstShowInit = true;
_pendingRoomJoinNotice = roomJoinNotice;
}
else
{
@@ -454,6 +410,72 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
}
}
/// <summary>
/// 进房首个初始化(座位/房间信息):原 OnShow 里 isfirstShow 的整段逻辑(原样搬入),
/// 现由"加载到 100%"的加载完成点调用——保证入场动画等在玩家可见、位置就绪后才初始化/播放。
/// </summary>
private void ApplyFirstShowInit()
{
RoomJoinNotice roomJoinNotice = _pendingRoomJoinNotice;
if (roomJoinNotice == null) return;
string roomId = GameManager.Instance.gameData.currRoomData.roomId;
foreach (var item in GameManager.Instance.gameData.currRoomData.players)
{
JoinGame(item.position, item.gold, item.diamond, item.cannonMultiple, 0, item.userId, item.cannonId, item.wingsId, item.warGodId);
// ⭐ 优先用后端 isBot 字段,兼容 userId.StartsWith("bot_")
var sit = list_Sits.Find(s => s.posID == item.position);
if (sit != null)
{
bool isBot = item.isBot ||
(!string.IsNullOrEmpty(item.userId) && item.userId.StartsWith("bot_"));
if (isBot) sit.isBot = true;
}
}
GameManager.Instance.gameData.roomJoinNotice = roomJoinNotice;
isfirstShow = false;
foreach (var item in list_Sits)
{
// ⭐ 座位镜像:本地玩家的显示座位 = 服务端座位转显示空间(上方座位翻转后落回下方)
if (item.posID == SeatMirror.ToDisplay(roomJoinNotice.Position))
{
currSitData = item;
// ⭐ 长按本地座位的 img_OpenPT 开关炮台面板(与 btn_ChangeTurret 同逻辑)
HookOpenPTLongPress(currSitData);
// ⭐ 本地座位自动攻击关闭时广播 Idle 状态给其他客户端
HookAutoAttackOff(currSitData);
}
else
{
// ⭐ 非本地座位隐藏加减炮倍:Normal 藏座位按钮(未拖引用为 null 跳过),
// XJMH 藏炮台上的加减倍(TurretData.obj_Add / obj_Down)
item.HideTurretAddMinusUI();
// ⭐ 自动攻击开关与加减倍按钮一样:非本地座位隐藏
if (item.toggle_AutoAttack != null)
item.toggle_AutoAttack.gameObject.SetActive(false);
}
}
//// currSitData.ChangeTurret(1); — 已由 SitPosData.OnEnable 自动加载
if (currSitData.currentTurret != null)
SyncChangeTurret(currSitData.currentTurret.turretNumber);
currSitData.lockFX.transform.GetChild(0).gameObject.SetActive(false);
currSitData.lockFX.transform.GetChild(1).gameObject.SetActive(true);
if (mySitAnim != null && currSitData.turretParent != null)
{
// ⭐ 座位位置 + 左右座位各自的偏移:把"我在这"圆圈摆到座位上面
// (左右座位镜像后需要的偏移不一样,分左/右两套配置,面板上调)
Vector3 sitAnimOffset = IsLeftSeat(currSitData) ? mySitAnimOffsetLeft : mySitAnimOffsetRight;
mySitAnim.transform.position = currSitData.turretParent.position + sitAnimOffset;
// ⭐ 进入游戏圆圈:6 秒后自动消失(点击屏幕立即消失的逻辑在 HandleInput)
StartCoroutine(HideMySitAnimAfter(6f));
}
}
private void RefreshGGKCount()
{
var user = GameManager.Instance.user;
@@ -1401,6 +1423,15 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
else if (JinDuTiao != null)
JinDuTiao.Hide();
// ⭐ 加载到 100%(进度条已隐藏):标记完成 + 执行挂起的进房初始化(座位/房间信息)。
// 入场动画等表现由初始化链路在此之后触发——玩家可见、位置已就绪
IsGameLoadCompleted = true;
if (_pendingFirstShowInit)
{
_pendingFirstShowInit = false;
ApplyFirstShowInit();
}
// ⭐ 无操作自动退出:加载完成,从现在开始计无操作时间
ArmIdleAutoExit();
}
@@ -3065,7 +3096,8 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
{
// ⭐ 起飞炮台(FlyTurretData):
// 飞行/悬停中 → 点击空白处=改目标(点到哪飞到哪),立即响应;
// 未起飞 → 仅记录轻点判定,继续走下方正常开火流程(松开时判定轻点才起飞),
// 未起飞 → 仅记录轻点判定,继续走下方正常开火流程(松开时判定轻点是否起飞,
// 是否允许由 FlyTurretData.allowTapTakeoff 控制,默认关=没点起飞按钮不起飞),
// 保证平时按住扫射/自动开火与普通炮台完全一致
if (currSitData.currentTurret is FlyTurretData flyTurret)
{
@@ -3129,7 +3161,7 @@ public class GameMainPanel_FishBase : GameMainPanel_Base
return;
}
// ⭐ 起飞炮台:轻点空白处=起飞(按下时已正常开火,松开时判定轻点→触发起飞)
// ⭐ 起飞炮台:轻点空白处=起飞(受 allowTapTakeoff 控制,默认关;按下时已正常开火)
// 长按/拖动扫射时此判定不通过,正常走下方 Idle,普通开火完全不受影响
if (_flyTapPending)
{
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -6148,8 +6148,11 @@ MonoBehaviour:
goldNum: 0
consumeGoldNum: 0
tMP_GoldNum: {fileID: 5250310886200927483}
goldNum_UGUI: {fileID: 0}
tMP_DiamondNum: {fileID: 9208482097770273619}
diamondNum_UGUI: {fileID: 0}
tMP_TurretConsumeNum: {fileID: 6996998917093333593}
turretConsumeNum_UGUI: {fileID: 0}
wingParent: {fileID: 5982321734877658021}
zhanShenParent: {fileID: 3297566421429981692}
currentZhanShenNumber: 0
@@ -6165,7 +6168,7 @@ MonoBehaviour:
xjmhTurretList: []
tra_CurrTurret: {fileID: 0}
currentAngle: 0
maxRotationAngle: 70
maxRotationAngle: 92
monCotl_Lock: {fileID: 0}
monCotl_Lock_Position: {fileID: 0}
isLock: 0